13.5 下载器中间件
从Scrapy框架图12-1中可以看到,下载器中间件是介于Scrapy的request/response处理的钩子框架,是用于全局修改Scrapy的request和response,可以帮助我们定制自己的爬虫系统。
13.5.1 激活下载器中间件
要激活下载器中间件组件,需要将其加入到DOWNLOADER_MIDDLEWARES设置中。该设置位于Settings.py文件,是一个字典(dict),键为中间件类的路径,值为其中间件的顺序。示例如下:
DOWNLOADER_MIDDLEWARES = { 'myproject.middlewares.CustomDownloaderMiddleware': 543, }
在Settings.py中对DOWNLOADER_MIDDLEWARES的设置,会与Scrapy内置的下载器中间件设置DOWNLOADER_MIDDLEWARES_BASE合并,但不会覆盖,而是根据顺序值进行排序,最后得到启用中间件的有序列表:第一个中间件是最靠近引擎的,最后一个中间件是最靠近下载器的。Scrapy内置的中间件设置DOWNLOADER_MIDDLEWARES_BASE为:
·'scrapy.downloadermiddlewares.robotstxt.RobotsTxtMiddleware':100
·'scrapy.downloadermiddlewares.httpauth.HttpAuthMiddleware':300
·'scrapy.downloadermiddlewares.downloadtimeout.DownloadTimeoutMiddleware':350
·'scrapy.downloadermiddlewares.useragent.UserAgentMiddleware':400
·'scrapy.downloadermiddlewares.retry.RetryMiddleware':500
·'scrapy.downloadermiddlewares.defaultheaders.DefaultHeadersMiddleware':550
·'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.chunked.ChunkedTransferMiddleware':830
·'scrapy.downloadermiddlewares.stats.DownloaderStats':850
·'scrapy.downloadermiddlewares.httpcache.HttpCacheMiddleware':900
如何分配中间件的位置,首先看一下内置的中间件的位置,然后根据将你想放置的中间件的位置设置一个值。有时候你想放置的中间件可能会依赖前后的中间件的作用,因此设置顺序相当重要。如果想禁用内置的中间件,必须在DOWNLOADER_MIDDLEWARES中定义该中间件,并将值设置为None。例如想关闭User-Agent中间件:
DOWNLOADER_MIDDLEWARES = { 'myproject.middlewares.CustomDownloaderMiddleware': 543, 'scrapy.downloadermiddlewares.useragent.UserAgentMiddleware': None, }
注意 DOWNLOADER_MIDDLEWARES_BASE中内置中间件并不是都开启的,有些中间件需要通过特定的设置来启用。
13.5.2 编写下载器中间件
如何定制我们自己的下载器中间件才是我们比较关心的问题,编写下载器中间件非常简单。每个中间件组件是定义了以下一个或多个方法的Python类:
·process_request(request,spider)
·process_response(request,response,spider)
·process_exception(request,exception,spider)
下面分别介绍这三种中间件。
1.process_request(request,spider)
方法说明: 当每个Request通过下载中间件时,该方法被调用,返回值必须为None、Response对象、Request对象中的一个或Raise IgnoreRequest异常。
参数:
Request(Request对象):处理的Request。
Spider(Spider对象):该Request对应的Spider。
返回值:
如果返回None,Scrapy将继续处理该Request,执行其他的中间件的相应方法,直到合适的下载器处理函数被调用,该Request被执行(其Response被下载)。
如果返回Response对象,Scrapy不会调用其他的process_request(),process_exception()方法,或相应的下载方法,将返回该response。已安装的中间件的process_response()方法则会在每个response返回时被调用。
如果返回Request对象,Scrapy则停止调用process_request方法并重新调度返回的Request。当新返回的Request被执行后,相应地中间件链将会根据下载的Response被调用。
如果是Raise IgnoreRequest异常,则安装的下载中间件的process_exception()方法会被调用。如果没有任何一个方法处理该异常,则Request的errback方法会被调用。如果没有代码处理抛出的异常,则该异常被忽略且不记录。
2.process_response(request,response,spider)
方法说明: 该方法主要用来处理产生的Response,返回值必须是Response对象、Request对象中的一个或Raise IgnoreRequest异常。
参数:
request(Request对象):Response对应的Request。
response(Response对象):处理的Response。
spider(Spider对象):Response对应的Spider。
返回值:
如果返回Response对象,可以与传入的Response相同,也可以是新的对象,该Response会被链中的其他中间件的process_response()方法处理。
如果返回Request对象,则中间件链停止,返回的Request会被重新调度下载。处理类似于process_request()返回Request时所做的那样。
如果抛出IgnoreRequest异常,则调用Request的errback方法。如果没有代码处理抛出的异常,则该异常被忽略且不记录。
3.process_exception(request,exception,spider)
方法说明: 当下载处理器或process_request()抛出异常,比如IgnoreRequest异常时,Scrapy调用process_exception()。process_exception()应该返回None、Response对象或者Request对象其中之一。
参数:
request(Request对象):产生异常的Request。
exception(Exception对象):抛出的异常。
spider(Spider对象):Request对应的Spider。
返回值:
如果返回None,Scrapy将会继续处理该异常,接着调用已安装的其他中间件的process_exception()方法,直到所有中间件都被调用完毕,则调用默认的异常处理。
如果返回Response对象,则已安装的中间件链的process_response()方法被调用。Scrapy将不会调用任何其他中间件的process_exception()方法。
如果返回Request对象,则返回的request将会被重新调用下载,这将停止中间件的process_exception()方法执行,类似于返回Response对象的处理。
下面通过两个例子帮助大家理解下载器中间件的编写,这两个例子也是在实际项目中经常用到。
一个是动态设置Request的User-Agent字段,主要是为了突破反爬虫对User-Agent字段的检测,实例代码如下:
''' 这个类主要用于产生随机User-Agent ''' class RandomUserAgent(object): def __init__(self,agents): self.agents = agents @classmethod def from_crawler(cls,crawler): # 从Settings中加载USER_AGENTS的值 return cls(crawler.settings.getlist('USER_AGENTS')) def process_request(self,request,spider): # 在process_request中设置User-Agent的值 request.headers.setdefault('User-Agent', random.choice(self.agents))
其中USER_AGENTS是写在Settings.py中的User-Agent列表,内容如下:
USER_AGENTS=[ "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; AcooBrowser; .NET CLR 1.1.4322; .NET CLR 2.0.50727)", "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; Acoo Browser; SLCC1; .NET CLR 2.0.50727; Media Center PC 5.0; .NET CLR 3.0.04506)", "Mozilla/4.0 (compatible; MSIE 7.0; AOL 9.5; AOLBuild 4337.35; Windows NT 5.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)", "Mozilla/5.0 (Windows; U; MSIE 9.0; Windows NT 9.0; en-US)", "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Tri dent/5.0; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET CLR 2.0.50727; Media Center PC 6.0)", "Mozilla/5.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET CLR 1.0.3705; .NET CLR 1.1.4322)", "Mozilla/4.0 (compatible; MSIE 7.0b; Windows NT 5.2; .NET CLR 1.1.4322; .NET CLR 2.0.50727; InfoPath.2; .NET CLR 3.0.04506.30)", "Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN) AppleWebKit/523.15 (KHTML, like Gecko, Safari/419.3) Arora/0.3 (Change: 287 c9dfb30)", "Mozilla/5.0 (X11; U; Linux; en-US) AppleWebKit/527+ (KHTML, like Gecko, Safari/419.3) Arora/0.6", "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.2pre) Gecko/20070215 K-Ninja/2.1.1", "Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9) Gecko/20080705 Firefox/3.0 Kapiko/3.0", "Mozilla/5.0 (X11; Linux i686; U;) Gecko/20070322 Kazehakase/0.4.5", "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.8) Gecko Fedora/1.9.0.8-1. fc10 Kazehakase/0.5.6", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11", ]
一个是动态设置Request的代理IP,主要是为了突破反爬虫对IP的检测,实例代码如下:
''' 这个类主要用于产生随机代理 ''' class RandomProxy(object): def __init__(self,iplist):# 初始化一下数据库连接 self.iplist=iplist @classmethod def from_crawler(cls,crawler): # 从Settings中加载IPLIST的值 return cls(crawler.settings.getlist('IPLIST')) def process_request(self, request, spider): ''' 在请求上添加代理 :param request: :param spider: :return: ''' proxy = random.choice(self.iplist) request.meta['proxy'] =proxy
其中IPLIST是写在Settings.py中的代理IP列表,内容如下:
IPLIST=["http://220.160.22.115:80", "http://183.129.151.130:80","http://112.228. 35.24:8888"]
写完以上两个中间件,如果想使用的话,直接按照上一节讲解的那样,进行激活即可。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论