- title: scrapy反爬虫策略date: 2018-06-06 00:51:34
tags: spider - scrapy框架图
- Request类 和 Response类
- User Agent用户代理
- IP代理池
- 西刺免费IP代理来获取IP代理。">方法一:通过爬取西刺免费IP代理来获取IP代理。
- scrapy-crawlera来获取IP代理服务">方法二:通过收费项目scrapy-crawlera来获取IP代理服务
- 洋葱网络Tor来包装IP">方法三:通过洋葱网络Tor来包装IP
- 验证码识别
- 隐藏爬虫行为的配置
- Spider设置特定setting
title: scrapy反爬虫策略date: 2018-06-06 00:51:34
tags: spider
scrapy框架图
组件
①引擎(Scrapy)
②调度器(Scheduler)
③下载器(Downloader)
④爬虫(Spiders)
⑤项目管道(Pipeline)
⑥下载器中间件(Downloader Middlewares)
⑦爬虫中间件(Spider Middlewares)
⑧调度中间件(Scheduler Middewares)
运行流程
- 引擎从调度器中取出一个链接(URL)用于接下来的抓取
- 引擎把URL封装成一个请求(Request)传给下载器
- 下载器把资源下载下来,并封装成应答包(Response)
- 爬虫解析Response
- 解析出实体(Item),则交给实体管道进行进一步的处理
- 解析出的是链接(URL),则把URL交给调度器等待抓取
Request类 和 Response类
resquest由spiders产生
response由downloader产生
具体函数用法及其参数可以通过查看源码和文档了解。
User Agent用户代理
User Agent
在setting中设置可能现有的User Agent:
user_agent_list = []
在需要的文件中import。
通过生成随机数,来取user_agent:
import random
random_index = random.randint(0, len(user_agent_list) - 1)
random_agent = user_agent_list[random_index]
可以在需要使用的地方写该段代码,但缺少了代码复用。
可以通过github上一个开源的库fake-useragent来动态获取user-agent。
实例:
from fake_useragent import UserAgent
ua = UserAgent()
# 指定浏览器
us.ie
us.chrome
us.firefox
# 随机浏览器
us.random
Downloader Middlewares(下载器中间件)
由于上述的UserAgent的获取没进行代码复用,使用中间件可以进行设置。
可以在setting中配置Downloader Middlewares,默认的放在DownloaderMiddlewares类的useragent下。
自定义DownloaderMiddleware:
class RandomUserAgentMiddlware(object):
# 随即更换UserAgent
def __init__(self, crawler):
super(RandomUserAgentMiddlware, self).__init__()
self.ua = UserAgent()
self.ua_type = crawler.settings.get("RANDOM_US_THPE", "random")
def from_crawler(cls, crawler):
return cls(crawler)
def process_request(self, request, spider):
def get_ua():
return getattr(self.ua, self.ua_type)
request.headers.setdefault('User_Agent', get_ua())
并且可以在setting中指定获得的ua的类型,并通过写闭包函数,用getattr方法将类型连接。
RANDOM_UA_TYPE = "random"
IP代理池
对于亚马逊的服务器,可以重启动态分配IP(阿里云除外,为静态IP)。公司、家中的路由器提供的IP也是动态分配的。本机IP是爬取效果最好的(IP代理较慢),最好是控制好爬取速度,保证本地IP不被封禁。
方法一:通过爬取西刺免费IP代理来获取IP代理。
在项目根目录下新建文件夹tools,存放脚本文件crawl_xici_ip.py:
连接数据库
conn = MySQLdb.connect(host="xxx.xxx.xxx.xxx", user="username", passwd="password", db="dbname", charset="utf8")
爬取西刺免费ip代理
def crawl_ips():
# 爬取西刺的免费ip代理
headers = {
"User-Agent": "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Mobile Safari/537.36"}
for i in range(3205):
re = requests.get("http://www.xicidaili.com/nn/{0}".format(i), headers=headers)
selector = Selector(text=re.text)
all_trs = selector.css("#ip_list tr")
ip_list = []
for tr in all_trs[1:]:
speed_str = tr.css(".bar::attr(title)").extract_first()
if speed_str:
speed = float(speed_str.split("秒")[0])
all_texts = tr.css("td::text").extract()
ip = all_texts[0]
port = all_texts[1]
# proxy_type = all_texts[5]
ip_list.append((ip, port, speed))
for ip_info in ip_list:
cursor.execute(
"insert proxy_ip(ip, port, speed, proxy_type) VALUES('{0}', '{1}', {2}, 'HTTP')".format(ip_info[0], ip_info[1], ip_info[2])
)
conn.commit()
获取IP
class GetIP(object):
def delete_ip(self, ip):
# 从数据库中删除无效的ip
delete_sql = """
delete from xwd_proxy_ip where ip='{ip}'
"""
cursor.execute(delete_sql)
conn.commit()
return True
def judge_ip(self, ip, port):
# 判断ip是否可用
http_url = "http://www.baidu.com"
proxy_url = "http://{0}:{1}".format(ip, port)
try:
proxy_dict = {
"http": proxy_url
}
response = requests.get(http_url, proxies=proxy_dict)
except Exception as e:
print("invalid ip and port")
self.delete_ip(ip)
return False
else:
code = response.status_code
if 200 <= code < 300:
print("effective ip")
return True
else:
print("invalid ip and port")
self.delete(ip)
return False
def get_random_ip(self):
# 从数据库中随机获取一个可用的IP
random_sql = """
SELECT ip, port FROM xwd_proxy_ip
ORDER BY RAND()
LIMIT 1
"""
result = cursor.execute(random_sql)
for ip_info in cursor.fetchall():
ip = ip_info[0]
port = ip_info[1]
judge_re = self.judge_ip(ip, port)
if judge_re:
return "http://{0}:{1}".format(ip, port)
else:
return self.get_random_ip()
# print(crawl_ips())
if __name__ == "__main__":
get_ip = GetIP()
get_ip.get_random_ip()
注意一定要加上“if name == “main“:”这条代码,否则在别的spider文件中调用时会执行下面两条代码。
可以从GitHub上的项目scrapy-proxies中得到更完善的获取IP代理的脚本。
在DownloadMiddleware中添加获取IP的类:
class RandomProxyMiddleware(object):
def process_request(self, request, spider):
get_ip = GetIP()
request.meta["proxy"] = get_ip.get_random_ip()
方法二:通过收费项目scrapy-crawlera来获取IP代理服务
具体使用方法文档中有写,使用流程非常简单。
方法三:通过洋葱网络Tor来包装IP
使用洋葱网络可以将自己的IP地址进行包装,达到匿名爬取网站的效果,而且效率要比网上免费IP代理甚至收费IP代理更好。主要问题是需要VPN。
验证码识别
前言
无需自己编写验证码识别代码,由于很多识别工具是需要通过训练集训练的,并且自己实现非常复杂,效果也不好。
常见方法
编码实现(tessract-ocr)—— 识别率低
在线打码 —— 识别率90%
人工打码 —— 识别率基本100%
在线打码
可以使用在线付费打码平台,如云打码可以很好的实现常见验证码识别。具体使用文档可自行查看官网。
隐藏爬虫行为的配置
在setting中设置:
具体内容和详情可以参照scrapy官方文档(中文文档),在其中可以查询到setting详细信息。
Cookie禁用
COOKIES_ENABLED = False
自动限速
CONCURRENT_REQUESTS = 32
DOWNLOAD_DELAY = 3
Spider设置特定setting
在爬虫class信息中设置特定的setting: