想想大家也清楚,一个爬虫最基础的防反扒机制是动态设置User-Agent(以下简称UA),在scrapy中有几个与随机User-Agent相关的设置,我今天来跟大家交流交流这几个方面。
-
在setting.py内的简单设置有
ROBOTSTXT_USER_AGENT
、USER_AGENT
、DEFAULT_REQUEST_HEADERS
,以下是它们的默认值-
ROBOTSTXT_USER_AGENT=None
官方文档原话:
The user agent string to use for matching in the robots.txt file. If
None
, the User-Agent header you are sending with the request or the USER_AGENT setting (in that order) will be used for determining the user agent to use in the robots.txt file.ROBOTSTXT_USER_AGENT
官方文档的意思是用于匹配目标网站的robots文档协议,来判定你这个爬虫能不能爬取目标网站。如果ROBOTSTXT_USER_AGENT
为None
,那么scrapy会使用USER_AGENT
的值去匹配,但以上工作都只会在ROBOTSTXT_OBEY = True
的情况下才会使用,但是在遵循robots协议的情况下,你几乎爬不到东西,所以ROBOTSTXT_USER_AGENT
这个参数可以说绝大部分情况下是没有用的,大家只要了解就可以。 -
USER_AGENT="Scrapy/VERSION (+https://scrapy.org)"
官方文档原话:
The default User-Agent to use when crawling, unless overridden. This user agent is also used by
RobotsTxtMiddleware
ifROBOTSTXT_USER_AGENT
setting isNone
and there is no overridding User-Agent header specified for the request.如果你没有自己设置UA进行覆盖,那么这会是默认的UA设置,你的所有的请求头都会使用同一个UA。后边讲的和上面一样,在
ROBOTSTXT_OBEY = True
的情况下,如果ROBOTSTXT_USER_AGENT
为None
,那么scrapy会使用USER_AGENT
的值去匹配。 -
DEFAULT_REQUEST_HEADERS = { 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 'Accept-Language': 'en', }
官方文档原话:
The default headers used for Scrapy HTTP Requests. They’re populated in the
DefaultHeadersMiddleware
.如果你不手动设置,这将是所有请求的默认请求头,在这里也可以设置默认UA设置,这个请求头将会在
DefaultHeadersMiddleware
中间件中赋予每个请求。
-
-
在中间件中设置,需要搭配setting.py中的:
-
DOWNLOADER_MIDDLEWARES = { 'baidu.middlewares.bdUserAgentMiddleware': 520, }
这方法大家应该都知道,我就不赘述了,我来告诉大家这个的更深层次的原理。
-
探索
仔细研究就会发现,scrapy 中有很多默认中间件,每个请求都会经过这些中间件,但它们的启动会遵循一定的顺序,而不是同时启动,以下是这些默认中间件:
DOWNLOADER_MIDDLEWARES_BASE={
'scrapy.downloadermiddlewares.robotstxt.RobotsTxtMiddleware': 100,
'scrapy.downloadermiddlewares.httpauth.HttpAuthMiddleware': 300,
'scrapy.downloadermiddlewares.downloadtimeout.DownloadTimeoutMiddleware': 350,
'scrapy.downloadermiddlewares.defaultheaders.DefaultHeadersMiddleware': 400,
'scrapy.downloadermiddlewares.useragent.UserAgentMiddleware': 500,
'scrapy.downloadermiddlewares.retry.RetryMiddleware': 550,
'scrapy.downloadermiddlewares.ajaxcrawl.AjaxCrawlMiddleware': 560,
'scrapy.downloadermiddlewares.redirect.MetaRefreshMiddleware': 580,
'scrapy.downloadermiddlewares.httpcompression.HttpCompressionMiddleware': 590,
'scrapy.downloadermiddlewares.redirect.RedirectMiddleware': 600,
'scrapy.downloadermiddlewares.cookies.CookiesMiddleware': 700,
'scrapy.downloadermiddlewares.httpproxy.HttpProxyMiddleware': 750,
'scrapy.downloadermiddlewares.stats.DownloaderStats': 850,
'scrapy.downloadermiddlewares.httpcache.HttpCacheMiddleware': 900,
}
这些默认中间件会在项目运行中和你在settings.py中设置的DOWNLOADER_MIDDLEWARES
进行合并,以下是该参数:
DOWNLOADER_MIDDLEWARES = {
'baidu.middlewares.bdUserAgentMiddleware': 520,
}
每个中间件的后面都有个整数值,整数值的大小决定了他们的调用顺序,每个中间件的 process_request()
将会根据数值从小到大依次调用,而process_response()
将会反向调用,由此可知scrapy调用中间件的方式是使用栈数据结构,也就是LIFO的模式。
你可以自己写入自己的UserAgentMiddleware
中间件来随机指定UA参数,然后写进DOWNLOADER_MIDDLEWARES
参数中,并指定一个整数值代表其在中间件中的顺序,一般使用scarpy自动给你设置的值就可以,如上面的520,你可以看到520的顺序在DOWNLOADER_MIDDLEWARES_BASE
参数中排在默认的UA中间件scrapy.downloadermiddlewares.useragent.UserAgentMiddleware
后,在scrapy.downloadermiddlewares.retry.RetryMiddleware
中间件之前,scrapy的用意在于在scrapy.downloadermiddlewares.retry.RetryMiddleware
之前用户得解决完毕每个请求的请求头等参数问题,后面的默认中间件都不会再去覆盖这些参数,由此得出,你自己的UserAgentMiddleware
的值设置最好在500-550之间,但经我实际测试,其实只要大于0就可以,但是为了保持scapy内部中间件的默认顺序,自己瞎赋值可能会造成不正常运行,最好还是在500-550之间。当然,此时也可以设置'scrapy.downloadermiddlewares.useragent.UserAgentMiddleware': None,让scrapy不要调用自己的UA中间件了,可以提高一点效率。
下面我们来看几个跟本话题有关的几个中间件:
-
'scrapy.downloadermiddlewares.robotstxt.RobotsTxtMiddleware': 100,
这是最先执行的中间件,用于请求robots文件,只有在
ROBOTSTXT_OBEY = True
的情况下才会使用,此时会用到ROBOTSTXT_USER_AGENT
或USER_AGENT
的值。 -
'scrapy.downloadermiddlewares.defaultheaders.DefaultHeadersMiddleware': 400,
加上默认请求头,也就是
DEFAULT_REQUEST_HEADERS
的值在这个中间件的process_request()
中会添加到每个请求中,如果你在DEFAULT_REQUEST_HEADERS
设置了UA,那么默认UA就设置进去了。 -
'scrapy.downloadermiddlewares.useragent.UserAgentMiddleware': 500,
如果
DEFAULT_REQUEST_HEADERS
没有设置UA,那么在这个中间件的process_request()
中会代入USER_AGENT
的值作为默认UA,如果DEFAULT_REQUEST_HEADERS
设置了UA,那么就什么事都不做。 -
'baidu.middlewares.bdUserAgentMiddleware': 520,
也就是你自己写的UA中间件,设置了动态UA:from fake_useragent import UserAgent class bdUserAgentMiddleware(UserAgentMiddleware): # 重写process_request方法,中途截获request并对其进行useragent随机取值操作 def process_request(self, request, spider): # 随机从列表中取出一个作为useragent的值 ua=UserAgent(path='./fake_useragent.json') request.headers['User-Agent']=ua.random
但是不管你在项目中使用自己的UA中间件设置随机UA还是在spider类中直接添加headers设置随机UA,里面的UA设置将会覆盖以上所有中间件的UA设置,这会导致
USER_AGENT
参数及DEFAULT_REQUEST_HEADERS
里的UA参数无效。
看了这么多,那么我来考一下大家:
Q:如果同时指定了USER_AGENT的值和DEFAULT_REQUEST_HEADERS参数里的UA的值,在不调用自己的UserAgentMiddleware中间件及spider类中直接添加headers设置随机UA的情况下,scrapy内部取谁的值呢?
A:希望大家能畅所欲言。答案可以自行去测试。
以上是根据scrapy官方文档和相关博文,再结合自己测试和理解给大家整理的,如有错误,欢迎指教交流。