当前位置:   article > 正文

scrapy框架-反爬虫与绕过方法+setting动态配置

scrapy反爬绕过

反爬虫与绕过方法

反爬虫的技术越来越丰富,种类也越来越多,以下归纳爬虫与反爬虫的应对措施和绕过方法。


小蜘蛛

甲.对网站感兴趣,分析网络请求并写爬虫进行数据爬取

乙.监控发现某时间段访问增大,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以继续爬取网站数据。

简单的,先设置一个user-agent池,里面预先放置各种浏览器的agent,通过随机数取到随机的agent。在每一次发起请求都需要带上这个随机获得的agent。例如:

  1. user_agent_list = [
  2. "Mozilia firefox xxx…",
  3. "Mozilia chrome xxx…",
  4. Mozilia ie xxx…
  5. ]
  6. random_index = randmo.randint(0,len(user_agent_list))
  7. random_agent = user_agent_list[random_index]
  8. self.headers["User-Agent"] = random_agent

这是最简单的形态,但是不符合代码开发需求。一个是因为这样太麻烦,每一次请求都必须这样带上;二是代码分离性不好,一旦要更改就很麻烦,代码重用性也不高;三是耦合性太高,难以分离。

根据scrapy的数据流图

Scrapy架构图

可以发现第4、5步经过的MIddleware是全局的,可以在这里面写

在scrapy里面负责下载的中间件DownloadMiddleware里面进行编写,这样才能够达到高重用性/低耦合性和自动切换的需求。

开启配置

在settings.py中找到DOWNLOADER_MIDDLEWARES,并且取消掉它的注释(scrapy默认不开启,所以是注释着的)。

查看源码

接着到[项目目录jobbole-test\Lib\site-packages\scrapy\downloadermiddlewares\useragent.py]中可以看到,scrapy默认设置了user_agent,里面也包含了很多的方法:

  1. from scrapy import signals
  2. class UserAgentMiddleware(object):
  3. """This middleware allows spiders to override the user_agent"""
  4. def __init__(self, user_agent='Scrapy'):
  5. self.user_agent = user_agent
  6. @classmethod
  7. def from_crawler(cls, crawler):
  8. o = cls(crawler.settings['USER_AGENT'])
  9. crawler.signals.connect(o.spider_opened, signal=signals.spider_opened)
  10. return o
  11. def spider_opened(self, spider):
  12. self.user_agent = getattr(spider, 'user_agent', self.user_agent)
  13. def process_request(self, request, spider):
  14. if self.user_agent:
  15. request.headers.setdefault(b'User-Agent', self.user_agent)

init中写明scrapy的UserAgent是Scrapy
from_crawler方法会读取settings.py中的USER_AGENT配置,如果有的话就用配置,如果没有的话就默认用Scrapy,这里我可以在setting里面新增配置:

  1. """ 自定义 UserAgent 配置 """
  2. USER_AGENT = "Mozilla/5.0 (X11; Linux x86_64; rv:57.0) Gecko/20100101 Firefox/57.0"

根据文档编写

根据官方文档中对下载中间件的介绍,如果要开启自己的Middleware,则需要将后面的值设置变大,将默认的Middleware值设置为None(因为数值越大,执行越靠后,这样才会在执行的时候选择我们自己编写的Middleware配置)
在settings.py中:

  1. DOWNLOADER_MIDDLEWARES = {
  2. 'jobbolecvs.middlewares.MyCustomDownloaderMiddleware': 543,
  3. 'scrapy.downloadermiddleware.useragent.UserAgentMiddleware':None,
  4. }

下方还有一个函数:

  1. def process_request(self, request, spider):
  2. if self.user_agent:
  3. request.headers.setdefault(b'User-Agent', self.user_agent)

它就会在每次请求的时候都带上配置的UserAgent,如果没有配置就带上"Scrapy"

同时,在官方文档中下面部分有介绍:

下载器中间件(Downloader Middleware)

下载器中间件是介于Scrapy的request/response处理的钩子框架。 是用于全局修改Scrapy request和response的一个轻量、底层的系统。

并且根据文档中描述[激活下载器中间件]:

  1. DOWNLOADER_MIDDLEWARES = {
  2. 'myproject.middlewares.CustomDownloaderMiddleware': 543,
  3. 'scrapy.contrib.downloadermiddleware.useragent.UserAgentMiddleware': None,
  4. }

除了把自己编写的Middleware数值定义较大,而且还需要关闭默认的,那就是需要到settings中进行配置。

由于它是request/response双向钩子,所以重载的时候如果是处理request就重载request相关的方法,如果是response则只重载response相关的方法。

编写自己的Middleware需要重载几个方法process_request、process_response、process_exception

在建立scrapy项目的时候,目录中自动建立了一个middlewares.py文件(与setting、item、pipelin同目录),我们在里面编写自己的middleware就行了(这里random还没写):

  1. class RandomUserAgentMiddleware(object):
  2. """
  3. 自定义随机切换UserAgent
  4. """
  5. def __inti__(self, crawler):
  6. """ 主要获取settings配置中的user_agent_list """
  7. super(RandomUserAgentMiddleware, self).__init__()
  8. self.user_agent_list = crawler.settings.getr("user_agent_list",[])
  9. @classmethod
  10. def from_crawler(cls, crawler):
  11. return cls(crawler)
  12. def process_request(self, request, spider):
  13. """
  14. 这里未完成的是需要编写一个随机取值,从user_agent_list里面随机取1个
  15. 设置请求的时候每次都带上一个User-Agent """
  16. request.headers.setdefault('User-Agent', random())

然后在settings中配置:

  1. user_agent_list = ["Mozilla/5.0 (X11; Linux x86_64; rv:57.0) Gecko/20100101 Firefox/57.0",
  2. "Mozilla/5.0 (X10; Windows x86_64; rv:55.0) Gecko/20100101 Firefox/57.0"]

这样的一个列表即可。(写完后记得在setting开启RandomUserAgentMiddleware配置)。


使用fake外部包

自定义UserAgent有一个缺点,就是浏览器的版本很多、平台也很多,有些会淘汰、新的一直在升级。所以用这个的话还得自己长期维护版本号。所以可以用网上的fake-useragent包,它有github网友维护,我拿来安装后根据代码提示,集成到scrapy即可。

首先 安装fake-useragent包,然后到middleware.py中新增代码:

  1. from fake_useragent import UserAgent
  2. class RandomUserAgentMiddleware(object):
  3. """
  4. 自定义下载中间件 以达到每次请求都会随机切换user-agent的目的
  5. 通过安装fake-useragent包,来获取浏览器版本号(自己写个list也可以,但是没有网上的这个那么丰富齐全)
  6. 为了增强可选择性,比如选择随机ie或者随机firefox浏览器的版本号,所以增加了ua_type和get_ua的一些代码
  7. """
  8. def __init__(self, crawler):
  9. super(RandomUserAgentMiddleware, self).__init__()
  10. self.ua = UserAgent()
  11. # 从settings.py中读取RANDOM_UA_TYPE配置 如果没有则默认值为random 达到可配置的目的
  12. # 默认是random随机选择,但是可以在配置指定ie或者firefox、chrome等浏览器的不同版本
  13. self.ua_type = crawler.settings.get("RANDOM_UA_TYPE", "random")
  14. @classmethod
  15. def from_crawler(cls, crawler):
  16. return cls(crawler)
  17. def process_request(self, request, spider):
  18. def get_ua():
  19. """
  20. 函数中的函数 闭包
  21. 读取上面的ua_type设置 让process_request直接调用本get_ua
  22. """
  23. return getattr(self.ua, self.ua_type)
  24. request.headers.setdefault('User-Agent', get_ua())

然后在settings.py中新增代码:

RANDOM_UA_TYPE = "random"

然后在配置中开启:

  1. DOWNLOADER_MIDDLEWARES = {
  2. 'jobbolecvs.middlewares.RandomUserAgentMiddleware': 543,
  3. 'scrapy.downloadermiddleware.useragent.UserAgentMiddleware':None,
  4. }

即可实现随机切换User-Agent的功能。


自动切换ip地址

在爬虫爬取的过程中,不仅对User-Agent有识别和限制,很多时候也对IP地址进行判断和限制,比如拉勾网在爬虫爬取的过程中返回的警告信息:

  1. 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/)
  2. 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代理服务,百度搜一下就出来很多。这里用西刺代理做演示.打开西刺选择一个ip地址,记得复制端口号。


实现简单的ip代理

在刚才的middlewares.py中写了一个RandomUserAgentMiddleware类,里面最后一个方法是process_request,在方法里面,最底部加上一句代码:

  1. def process_request(self, request, spider):
  2. def get_ua():
  3. return getattr(self.ua,self.ua_type)
  4. request.headers.setdefault('User-Agent',get_ua())
  5. request.meta["proxy"] = "http://101.68.73.54:53281"

代码里的ip和端口就是西刺上面复制下来的。
保存运行就可以实现ip代理访问了。再次爬取拉勾网,则发现比用自己的IP地址顺畅很多,虽然也有限制,但是不像之前爬取2个就要被关闭。


外部包+实现IP代理池

用上面的方法实现了IP代理,但是还没有达到自动切换IP地址的方法。实现自动切换的方法可以爬取免费IP代理网站的IP/端口/http类型(有些是https)还有有效时间等,可以爬取多网站。

最好的IP代理插件包是scrapy官方提供的scrapy-crawlera包,在github上有,但是收费的。

然而穷就要用免费的,在github上搜索scrapy-proxies就能找到对应的包,用pycharm进行安装即可。

根据上面文档的介绍,安装好后只需要在settings.py中进行配置即可:

  1. # Retry many times since proxies often fail
  2. RETRY_TIMES = 10
  3. # Retry on most error codes since proxies fail for different reasons
  4. RETRY_HTTP_CODES = [500, 503, 504, 400, 403, 404, 408]
  5. DOWNLOADER_MIDDLEWARES = {
  6. 'scrapy.downloadermiddlewares.retry.RetryMiddleware': 90,
  7. 'scrapy_proxies.RandomProxy': 100,
  8. 'scrapy.downloadermiddlewares.httpproxy.HttpProxyMiddleware': 110,
  9. }
  10. # Proxy list containing entries like
  11. # http://host1:port
  12. # http://username:password@host2:port
  13. # http://host3:port
  14. # ...
  15. PROXY_LIST = '/path/to/proxy/list.txt'
  16. # Proxy mode
  17. # 0 = Every requests have different proxy
  18. # 1 = Take only one proxy from the list and assign it to every requests
  19. # 2 = Put a custom proxy to use in the settings
  20. PROXY_MODE = 0
  21. # If proxy mode is 2 uncomment this sentence :
  22. #CUSTOM_PROXY = "http://host1:port"

这是原文,里面的'/path/to/proxy/list.txt'就是存放ip的文件。所以我在settings.py中空白地方实际添加的代码是:

  1. """ 通过scrapy-proxies实现ip切换 """
  2. # Retry many times since proxies often fail
  3. RETRY_TIMES = 10
  4. # Retry on most error codes since proxies fail for different reasons
  5. RETRY_HTTP_CODES = [500, 503, 504, 400, 403, 404, 408]
  6. # Proxy list containing entries like
  7. # http://host1:port
  8. # http://username:password@host2:port
  9. # http://host3:port
  10. # ...
  11. project_dir = os.path.abspath(os.path.dirname(__file__))
  12. proxys_path = os.path.join(project_dir, 'utils/proxys.txt')
  13. PROXY_LIST = proxys_path
  14. # Proxy mode
  15. # 0 = Every requests have different proxy
  16. # 1 = Take only one proxy from the list and assign it to every requests
  17. # 2 = Put a custom proxy to use in the settings
  18. PROXY_MODE = 0
  19. # If proxy mode is 2 uncomment this sentence :
  20. #CUSTOM_PROXY = "http://host1:port"

然后在DOWNLOADER_MIDDLEWARES中新增:

  1. # 以下三个是实现IP动态切换
  2. 'scrapy.downloadermiddlewares.retry.RetryMiddleware': 90,
  3. 'scrapy_proxies.RandomProxy': 100,
  4. 'scrapy.downloadermiddlewares.httpproxy.HttpProxyMiddleware': 110,

这样就完成了配置。但是还没有设置存放ip的文件。我在utils目录下新建proxys.py。里面存放了网上复制下来的IP:

  1. http://27.46.74.52:9999
  2. http://187.87.76.25:3128
  3. http://202.179.189.18:3128
  4. http://95.68.244.254:3128
  5. http://178.32.213.128:80
  6. http://103.76.175.88:8080

然后就可以实现每次请求都切换IP地址(开关可设置,在使用说明里写有)

ip代理的问题

一般来讲,市面上免费的IP代理服务都是比较差的,要么是服务器不稳定,要么是请求太慢。所以其实用IP代理能解决被封的问题,但是速度会变慢。

收费的IP代理能够有效的解决这个问题,但是穷。

还有一个办法,就是在自己的电脑上使用VPN来进行IP切换,但是要放慢速度爬取,,免得VPN的地址也被封。

商用ip代理的实现

这个就需要参考我另一篇文章了《目标反爬虫怎么办?实践出真知-scrapy集成动态ip代理(以阿布云为例)》


识别验证码(商用)

验证码识别方法有:

1.编码实现(tesseract-ocr)这是谷歌开源的一个工具,但是已经不适用了,而且很麻烦,需要很多时间。

2.在线打码,识别率较高90%,云平台识别

3.人工打码,认为识别打码,最高识别率

这里使用云打码平台。注册两个用户,一个是普通用户:

  1. yundamas
  2. 123456789

另一个是开发者:

  1. wanliruhu
  2. 123456789

云打码平台提供的开发文档中的PythonHTTP示例下载,下载后有python3的示例代码。

根据示例代码和普通用户中心/开发者中心需要填写普通用户的用户名密码以及开发者的ID和密钥:

  1. # 用户名
  2. username = 'yundamas'
  3. # 密码
  4. password = '123456789'
  5. # 软件ID,开发者分成必要参数。登录开发者后台【我的软件】获得!
  6. appid = 4342
  7. # 软件密钥,开发者分成必要参数。登录开发者后台【我的软件】获得!
  8. appkey = '2e02326f1d782de768b784eaabc287'
  9. codetype = 5000
  10. filename = 'getimage.jpg'

codetype = 5000是指不知道验证码的格式是纯数字 英文或者中文等类型。

filename = 'getimage.jpg'是具体识别的图片。实际爬取中遇到验证码,需要下载下来,然后调用代码对验证码图片进行识别,返回值。


禁用cookie

有些网站会根据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秒后开启下一个页面的爬取


实现动态settings配置

在同一个工程项目中,会存在多个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文件的类中 添加如下代码:

  1. custom_settings = {
  2. "COOKIES_ENABLED":True,
  3. }

就可以实现单个spider开启个性化配置。就是知乎可以在spiders目录下的zhihu.py中的class ZhihuSpider类里面添加这么一句代码即可。

其他spider类似。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/754592
推荐阅读
相关标签
  

闽ICP备14008679号