scrapy下载中间件实现动态与固定UserAgent
- 前言
- 关卡:实现动态切换User-Agent
- scrapy设置User-Agent方式梳理
- User-Agent生效梳理
- 为何选择在下载中间件中实现
- 自定义User-Agent下载中间件
- 结束
前言
请求头User-Agent
是比较常规的反爬手段,不同站点对其检测机制各异,有的是检测是否是合规的浏览器User-Agent
,有的是在这基础上检测使用次数与频率,更有甚者是跟ip和cookie绑定在一起检测,这就要求我们能够动态去切换User-Agent
(随机or判定切换)。
关卡:实现动态切换User-Agent
scrapy设置User-Agent方式梳理
这里整理一下笔者已知的scrapy
设置User-Agent
的方式:
setting
配置文件中设置DEFAULT_REQUEST_HEADERS
- 生成requests请求时设置请求头
setting
配置文件中设置USER_AGENT
我们在源码看一下USER_AGENT
的默认值
scrapy
是有自带的USER_AGENT
中间件的,而USER_AGENT
就是跟它一起配合生效的,这个中间件默认也是开启的。所以大家注意了,如果我们直接使用scrapy
自带的USER_AGENT
机制,是很容易被是被为爬虫的!!!
- 自定义
User-Agent
下载中间件,这种方式其实跟方式3
是一样的逻辑
User-Agent生效梳理
我们来调试一下方式1
、方式2
和方式3
,看看三种方式同时设置时最终是哪个方式生效
如下图,按顺序分别为三种方式设置好User-Agent
如下图,在发起请求生成request
队列前,方式1
设置成功
如下图,生成request
队列后,经过下载中间件可以看到方式2
覆盖了方式1
我们仔细看下中间件的代码,特别是process_request
这个具体实现方法。
在此之前先了解一下setdefault
这个方法:
setdefault是Python中字典的一个方法, 它用于在字典中查找指定键 如果键存在, 则返回对应的值; 如果键不存在,则在字典中添加该键,并将其值设置为指定的默认值
由于request.headers
的User-Agent
有值且是2,所以经过下载中间件后,它还是2
综上,我们可以得到结论:
方式1
、方式2
和方式3
共存时,结果会取方式2
- 只存在
方式1
、方式2
时,结果会取方式2
- 只存在
方式1
、方式3
时,结果会取方式1
方式3
要生效必须只有它这种方式
为何选择在下载中间件中实现
首先要说明一下,并非一定要在中间件中才能实现User-Agent
动态切换,也可以在脚本开发中对每次生成的request
请求时动态设置User-Agent
(请求头),但这种方式在笔者看来是不符合python之美
的,功能未解耦而不够灵活,每个脚本都要单独实现,既繁琐又提高学习成本。
自定义User-Agent下载中间件
既然选择自定义中间件,那我们就可以随便玩了
首先思考一下,根据需求整理出设计方案:
- 动态:随机生成
User-Agent
- 切换:控制是否动态生成
- 全局:需要适配不同场景(固定、动态、切换)
通过上面的思考,在一个全局改动的位置要适配各式各样的脚本应用场景,就需要引入全局控制参数来实现点对点应用,下面是笔者的想法:
在request.meta中增加 auto_change_ua
,change_ua
,_ua
三个参数来实现,
auto_change_ua是自动切换,每次request都会切换User-Agent
change_ua是一个切换,当前request会切换User-Agent
_ua是固定存储,不切换时一直使用它
同时准备User-Agent
列表,用于随机生成,大致样式如下:
def get_random_ua(self):"""Return a ua if possible"""return random.choice(self.ua_list)def process_request(self, request, spider):meta = request.metaua = Noneif meta.get('auto_change_ua') or meta.get('change_ua', None):ua = self.get_random_ua()elif meta.get('_ua', None):ua = meta['_ua']elif not request.headers.get('User-Agent', None):ua = self.get_random_ua()if ua:request.headers['User-Agent'] = uadef process_response(self, request, response, spider):request.meta['_ua'] = request.headers['User-Agent']if 'change_ua' in request.meta:del request.meta['change_ua']if 'auto_change_ua' in request.meta:request.meta['change_ua'] = Truereturn response
开发好后需要在setting
的DOWNLOADER_MIDDLEWARES
配置才能生效,同时,记得把原生的UserAgentMiddleware
设置为None,不然它还是会开启的。
结束
这个关卡的设计方案是笔者根据自己业务场景实现的,大家如果有同样的需求,需要结合自己的业务场景来思考、设计与实现。
好了,分享就到这了,有啥错误的地方请指正~