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

反爬虫与绕过方法

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


小蜘蛛

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

乙.监控发现某时间段访问增大,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。例如:

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的数据流图

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配置)。


使用fake外部包

自定义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的功能。


自动切换ip地址

在爬虫爬取的过程中,不仅对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代理服务,百度搜一下就出来很多。这里用西刺代理做演示.打开西刺选择一个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代理网站的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代理能解决被封的问题,但是速度会变慢。

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

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

商用ip代理的实现

这个就需要参考我另一篇文章了《目标反爬虫怎么办?实践出真知-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

有些网站会根据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文件的类中 添加如下代码:

custom_settings = {
    "COOKIES_ENABLED":True,
}

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

其他spider类似。

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值