赞
踩
反爬虫的技术越来越丰富,种类也越来越多,以下归纳爬虫与反爬虫的应对措施和绕过方法。
甲.对网站感兴趣,分析网络请求并写爬虫进行数据爬取
乙.监控发现某时间段访问增大,IP相同,user-agent都是python,判断是爬虫,直接限制访问(不是封IP)
甲.随机切换user-agent,比如火狐 chrome 360浏览器 ie等,并且用IP代理手段继续爬取。
乙.发现IP变化,直接要求登录才可以访问(知乎就是)
甲.注册账号,每次请求带上cookie或者token。
乙.健全账号体系,限制只能访问好友信息等(facebook就是这样)
甲.注册多个账号,养着。多个账号联合爬取。
乙.单个账号请求过于固定或者频繁,限定IP访问频率
甲.模仿人类的请求,放慢请求速率
乙.偶尔弹出验证码,识别验证码后才能操作
甲.各种手段识别验证码
乙.增加动态网站,数据通过js动态加载,增加网络分析复杂度;发现大量请求只请求html,而不请求image/css/js。
甲.通过seleniumh和phantomjs完全模拟浏览器操作
乙.考虑成本太高的问题,可能放弃反爬虫
考虑到需要绕过反爬虫对user-agent的识别和限制,实际爬取过程中需要自动切换user-agent以继续爬取网站数据。
简单的,先设置一个user-agent池,里面预先放置各种浏览器的agent,通过随机数取到随机的agent。在每一次发起请求都需要带上这个随机获得的agent。例如:
- user_agent_list = [
- "Mozilia firefox xxx…",
- "Mozilia chrome xxx…",
- Mozilia ie xxx…
- ]
-
- random_index = randmo.randint(0,len(user_agent_list))
- random_agent = user_agent_list[random_index]
- self.headers["User-Agent"] = random_agent
-
这是最简单的形态,但是不符合代码开发需求。一个是因为这样太麻烦,每一次请求都必须这样带上;二是代码分离性不好,一旦要更改就很麻烦,代码重用性也不高;三是耦合性太高,难以分离。
根据scrapy的数据流图
可以发现第4、5步经过的MIddleware是全局的,可以在这里面写
在scrapy里面负责下载的中间件DownloadMiddleware里面进行编写,这样才能够达到高重用性/低耦合性和自动切换的需求。
在settings.py中找到DOWNLOADER_MIDDLEWARES,并且取消掉它的注释(scrapy默认不开启,所以是注释着的)。
接着到[项目目录jobbole-test\Lib\site-packages\scrapy\downloadermiddlewares\useragent.py]中可以看到,scrapy默认设置了user_agent,里面也包含了很多的方法:
-
- from scrapy import signals
-
-
- class UserAgentMiddleware(object):
- """This middleware allows spiders to override the user_agent"""
-
- def __init__(self, user_agent='Scrapy'):
- self.user_agent = user_agent
-
- @classmethod
- def from_crawler(cls, crawler):
- o = cls(crawler.settings['USER_AGENT'])
- crawler.signals.connect(o.spider_opened, signal=signals.spider_opened)
- return o
-
- def spider_opened(self, spider):
- self.user_agent = getattr(spider, 'user_agent', self.user_agent)
-
- def process_request(self, request, spider):
- if self.user_agent:
- request.headers.setdefault(b'User-Agent', self.user_agent)

init中写明scrapy的UserAgent是Scrapy
from_crawler方法会读取settings.py中的USER_AGENT配置,如果有的话就用配置,如果没有的话就默认用Scrapy,这里我可以在setting里面新增配置:
- """ 自定义 UserAgent 配置 """
- USER_AGENT = "Mozilla/5.0 (X11; Linux x86_64; rv:57.0) Gecko/20100101 Firefox/57.0"
根据官方文档中对下载中间件的介绍,如果要开启自己的Middleware,则需要将后面的值设置变大,将默认的Middleware值设置为None(因为数值越大,执行越靠后,这样才会在执行的时候选择我们自己编写的Middleware配置)
在settings.py中:
- DOWNLOADER_MIDDLEWARES = {
- 'jobbolecvs.middlewares.MyCustomDownloaderMiddleware': 543,
- 'scrapy.downloadermiddleware.useragent.UserAgentMiddleware':None,
- }
下方还有一个函数:
- def process_request(self, request, spider):
- if self.user_agent:
- request.headers.setdefault(b'User-Agent', self.user_agent)
它就会在每次请求的时候都带上配置的UserAgent,如果没有配置就带上"Scrapy"
同时,在官方文档中下面部分有介绍:
下载器中间件(Downloader Middleware)
下载器中间件是介于Scrapy的request/response处理的钩子框架。 是用于全局修改Scrapy request和response的一个轻量、底层的系统。
并且根据文档中描述[激活下载器中间件]:
- DOWNLOADER_MIDDLEWARES = {
- 'myproject.middlewares.CustomDownloaderMiddleware': 543,
- 'scrapy.contrib.downloadermiddleware.useragent.UserAgentMiddleware': None,
- }
除了把自己编写的Middleware数值定义较大,而且还需要关闭默认的,那就是需要到settings中进行配置。
由于它是request/response双向钩子,所以重载的时候如果是处理request就重载request相关的方法,如果是response则只重载response相关的方法。
编写自己的Middleware需要重载几个方法process_request、process_response、process_exception
在建立scrapy项目的时候,目录中自动建立了一个middlewares.py文件(与setting、item、pipelin同目录),我们在里面编写自己的middleware就行了(这里random还没写):
-
- class RandomUserAgentMiddleware(object):
- """
- 自定义随机切换UserAgent
- """
- def __inti__(self, crawler):
- """ 主要获取settings配置中的user_agent_list """
- super(RandomUserAgentMiddleware, self).__init__()
- self.user_agent_list = crawler.settings.getr("user_agent_list",[])
-
- @classmethod
- def from_crawler(cls, crawler):
- return cls(crawler)
-
- def process_request(self, request, spider):
- """
- 这里未完成的是需要编写一个随机取值,从user_agent_list里面随机取1个
- 设置请求的时候每次都带上一个User-Agent """
- request.headers.setdefault('User-Agent', random())
-

然后在settings中配置:
- user_agent_list = ["Mozilla/5.0 (X11; Linux x86_64; rv:57.0) Gecko/20100101 Firefox/57.0",
- "Mozilla/5.0 (X10; Windows x86_64; rv:55.0) Gecko/20100101 Firefox/57.0"]
这样的一个列表即可。(写完后记得在setting开启RandomUserAgentMiddleware配置)。
自定义UserAgent有一个缺点,就是浏览器的版本很多、平台也很多,有些会淘汰、新的一直在升级。所以用这个的话还得自己长期维护版本号。所以可以用网上的fake-useragent包,它有github网友维护,我拿来安装后根据代码提示,集成到scrapy即可。
首先 安装fake-useragent包,然后到middleware.py中新增代码:
- from fake_useragent import UserAgent
-
- class RandomUserAgentMiddleware(object):
- """
- 自定义下载中间件 以达到每次请求都会随机切换user-agent的目的
- 通过安装fake-useragent包,来获取浏览器版本号(自己写个list也可以,但是没有网上的这个那么丰富齐全)
- 为了增强可选择性,比如选择随机ie或者随机firefox浏览器的版本号,所以增加了ua_type和get_ua的一些代码
- """
-
- def __init__(self, crawler):
- super(RandomUserAgentMiddleware, self).__init__()
- self.ua = UserAgent()
- # 从settings.py中读取RANDOM_UA_TYPE配置 如果没有则默认值为random 达到可配置的目的
- # 默认是random随机选择,但是可以在配置指定ie或者firefox、chrome等浏览器的不同版本
- self.ua_type = crawler.settings.get("RANDOM_UA_TYPE", "random")
-
- @classmethod
- def from_crawler(cls, crawler):
- return cls(crawler)
-
- def process_request(self, request, spider):
- def get_ua():
- """
- 函数中的函数 闭包
- 读取上面的ua_type设置 让process_request直接调用本get_ua
- """
- return getattr(self.ua, self.ua_type)
-
- request.headers.setdefault('User-Agent', get_ua())
-

然后在settings.py中新增代码:
RANDOM_UA_TYPE = "random"
然后在配置中开启:
- DOWNLOADER_MIDDLEWARES = {
- 'jobbolecvs.middlewares.RandomUserAgentMiddleware': 543,
- 'scrapy.downloadermiddleware.useragent.UserAgentMiddleware':None,
- }
即可实现随机切换User-Agent的功能。
在爬虫爬取的过程中,不仅对User-Agent有识别和限制,很多时候也对IP地址进行判断和限制,比如拉勾网在爬虫爬取的过程中返回的警告信息:
- 2017-12-29 17:01:35 [scrapy.core.engine] DEBUG: Crawled (200) <GET https://passport.lagou.com/login/login.html?msg=validation&uStatus=2&clientIp=113.106.97.12> (referer: https://www.lagou.com/)
- 2017-12-29 17:01:35 [scrapy.core.scraper] DEBUG: Scraped from <200 https://passport.lagou.com/login/login.html?msg=validation&uStatus=2&clientIp=113.106.97.12>
就限制了爬虫的继续爬取。
所以IP代理就派上了用场。IP代理实际上是利用了代理服务器在中间作梗:
默认访问形式:我->浏览器->目标服务器
代理访问形式:我->浏览器->代理服务器<->目标服务器
实际上后面变成代理服务器与目标之间的沟通,我自己的ip则隐藏起来了,不会被禁止。而ip代理服务器由于提供了很多的ip,也不怕被封。所以可以用这个办法绕过对IP限制的反爬虫手段。
做IP代理的服务很多,也有人专门从事爬虫IP代理服务,百度搜一下就出来很多。这里用西刺代理做演示.打开西刺选择一个ip地址,记得复制端口号。
在刚才的middlewares.py中写了一个RandomUserAgentMiddleware类,里面最后一个方法是process_request,在方法里面,最底部加上一句代码:
- def process_request(self, request, spider):
- def get_ua():
- return getattr(self.ua,self.ua_type)
- request.headers.setdefault('User-Agent',get_ua())
- request.meta["proxy"] = "http://101.68.73.54:53281"
代码里的ip和端口就是西刺上面复制下来的。
保存运行就可以实现ip代理访问了。再次爬取拉勾网,则发现比用自己的IP地址顺畅很多,虽然也有限制,但是不像之前爬取2个就要被关闭。
用上面的方法实现了IP代理,但是还没有达到自动切换IP地址的方法。实现自动切换的方法可以爬取免费IP代理网站的IP/端口/http类型(有些是https)还有有效时间等,可以爬取多网站。
最好的IP代理插件包是scrapy官方提供的scrapy-crawlera包,在github上有,但是收费的。
然而穷就要用免费的,在github上搜索scrapy-proxies就能找到对应的包,用pycharm进行安装即可。
根据上面文档的介绍,安装好后只需要在settings.py中进行配置即可:
- # Retry many times since proxies often fail
- RETRY_TIMES = 10
- # Retry on most error codes since proxies fail for different reasons
- RETRY_HTTP_CODES = [500, 503, 504, 400, 403, 404, 408]
-
- DOWNLOADER_MIDDLEWARES = {
- 'scrapy.downloadermiddlewares.retry.RetryMiddleware': 90,
- 'scrapy_proxies.RandomProxy': 100,
- 'scrapy.downloadermiddlewares.httpproxy.HttpProxyMiddleware': 110,
- }
-
- # Proxy list containing entries like
- # http://host1:port
- # http://username:password@host2:port
- # http://host3:port
- # ...
- PROXY_LIST = '/path/to/proxy/list.txt'
-
- # Proxy mode
- # 0 = Every requests have different proxy
- # 1 = Take only one proxy from the list and assign it to every requests
- # 2 = Put a custom proxy to use in the settings
- PROXY_MODE = 0
-
- # If proxy mode is 2 uncomment this sentence :
- #CUSTOM_PROXY = "http://host1:port"

这是原文,里面的'/path/to/proxy/list.txt'就是存放ip的文件。所以我在settings.py中空白地方实际添加的代码是:
-
- """ 通过scrapy-proxies实现ip切换 """
- # Retry many times since proxies often fail
- RETRY_TIMES = 10
- # Retry on most error codes since proxies fail for different reasons
- RETRY_HTTP_CODES = [500, 503, 504, 400, 403, 404, 408]
- # Proxy list containing entries like
- # http://host1:port
- # http://username:password@host2:port
- # http://host3:port
- # ...
- project_dir = os.path.abspath(os.path.dirname(__file__))
- proxys_path = os.path.join(project_dir, 'utils/proxys.txt')
- PROXY_LIST = proxys_path
- # Proxy mode
- # 0 = Every requests have different proxy
- # 1 = Take only one proxy from the list and assign it to every requests
- # 2 = Put a custom proxy to use in the settings
- PROXY_MODE = 0
- # If proxy mode is 2 uncomment this sentence :
- #CUSTOM_PROXY = "http://host1:port"

然后在DOWNLOADER_MIDDLEWARES中新增:
- # 以下三个是实现IP动态切换
- 'scrapy.downloadermiddlewares.retry.RetryMiddleware': 90,
- 'scrapy_proxies.RandomProxy': 100,
- 'scrapy.downloadermiddlewares.httpproxy.HttpProxyMiddleware': 110,
这样就完成了配置。但是还没有设置存放ip的文件。我在utils目录下新建proxys.py。里面存放了网上复制下来的IP:
- http://27.46.74.52:9999
- http://187.87.76.25:3128
- http://202.179.189.18:3128
- http://95.68.244.254:3128
- http://178.32.213.128:80
- http://103.76.175.88:8080
然后就可以实现每次请求都切换IP地址(开关可设置,在使用说明里写有)
一般来讲,市面上免费的IP代理服务都是比较差的,要么是服务器不稳定,要么是请求太慢。所以其实用IP代理能解决被封的问题,但是速度会变慢。
收费的IP代理能够有效的解决这个问题,但是穷。
还有一个办法,就是在自己的电脑上使用VPN来进行IP切换,但是要放慢速度爬取,,免得VPN的地址也被封。
这个就需要参考我另一篇文章了《目标反爬虫怎么办?实践出真知-scrapy集成动态ip代理(以阿布云为例)》
验证码识别方法有:
1.编码实现(tesseract-ocr)这是谷歌开源的一个工具,但是已经不适用了,而且很麻烦,需要很多时间。
2.在线打码,识别率较高90%,云平台识别
3.人工打码,认为识别打码,最高识别率
这里使用云打码平台。注册两个用户,一个是普通用户:
- yundamas
- 123456789
另一个是开发者:
- wanliruhu
- 123456789
用云打码平台提供的开发文档中的PythonHTTP示例下载,下载后有python3的示例代码。
根据示例代码和普通用户中心/开发者中心需要填写普通用户的用户名密码以及开发者的ID和密钥:
- # 用户名
- username = 'yundamas'
-
- # 密码
- password = '123456789'
-
- # 软件ID,开发者分成必要参数。登录开发者后台【我的软件】获得!
- appid = 4342
-
- # 软件密钥,开发者分成必要参数。登录开发者后台【我的软件】获得!
- appkey = '2e02326f1d782de768b784eaabc287'
-
- codetype = 5000
-
- filename = 'getimage.jpg'
codetype = 5000是指不知道验证码的格式是纯数字 英文或者中文等类型。
filename = 'getimage.jpg'是具体识别的图片。实际爬取中遇到验证码,需要下载下来,然后调用代码对验证码图片进行识别,返回值。
有些网站会根据cookie识别是否爬虫,不需要登录也能访问的网站生效,如果是需要登录的网站如果禁用则会无法登陆。
在settings.py中将
COOKIES_ENABLED = False
设置为False即可实现禁用cookie
默认是一直下载的,下载延迟为0.但是有可能被目标发现,所以需要进行限速下载。在官方文档中有个关于自动限速AutoThrottle扩展的介绍,里面包括扩展的实现,算法等。这个扩展会自动调节限制的速度。
文档里的参数都是在settings.py中存在的,去取消注释或者改变状态即可开启。比如启用扩展就在settings.py中:
AUTOTHROTTLE_ENABLED = True
即可开启扩展。
然后初始下载延迟(单位:秒)的设置就是:
AUTOTHROTTLE_START_DELAY = 3
还有DOWNLOAD_DELAY下载器在下载同一个网站下一个页面前需要等待的时间。该选项可以用来限制爬取速度, 减轻服务器压力。同时也支持小数:
DOWNLOAD_DELAY = 0.5
就是0.5秒后开启下一个页面的爬取
在同一个工程项目中,会存在多个spider,但settings.py却只有一个。比如需要爬取jobbole和知乎的时候,知乎需要开启cookie用于登录,Jobbole则可能会通过我们提交的cookie判断是否是爬虫,所以需要在settings.py中实现动态设置,给每个spider分配不同的配置
打开scrapy源码(路径是项目/Lib/site-packages/scrapy/spiders/init.py)中的Spider类。里面有
custom_settings = None
如果设置有值的话,就可以自动覆盖默认的custom_settings。所以要实现动态设置就是 在settings.py文件中保持默认配置,然后 在具体的spider文件的类中 添加如下代码:
- custom_settings = {
- "COOKIES_ENABLED":True,
- }
就可以实现单个spider开启个性化配置。就是知乎可以在spiders目录下的zhihu.py中的class ZhihuSpider类里面添加这么一句代码即可。
其他spider类似。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。