5. Downloader Middleware的使用
- 在Downloader Middleware的功能十分强大:可以修改User-Agent、处理重定向、设置代理、失败重试、设置Cookies等。
- Downloader Middleware在整个架构中起作用的位置是以下两个。
- 在Scheduler调度出队列的Request发送给Doanloader下载之前,也就是我们可以在Request执行下载前对其进行修改。
- 在下载后生成的Response发送给Spider之前,也就是我们可以生成Resposne被Spider解析之前对其进行修改。
5.1 使用说明:
- 在Scrapy中已经提供了许多Downloader Middleware,如:负责失败重试、自动重定向等中间件:
- 它们都被定义到DOWNLOADER_MIDDLEWARES_BASE变量中。
# 在python3.6/site-packages/scrapy/settings/default_settings.py默认配置中
DOWNLOADER_MIDDLEWARES_BASE = {
# Engine side
'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,
# Downloader side
}
- 字典格式,其中数字为优先级,越小的优先调用。
5.2 自定义Downloader Middleware中间件
我们可以通过项目的DOWNLOADER_MIDDLEWARES变量设置来添加自己定义的Downloader Middleware。
其中Downloader Middleware有三个核心方法:
- process_request(request,spider)
- process_response(request,response,spider)
- process_exception(request,exception,spider)
① process_request(request,spider)
当每个request通过下载中间件时,该方法被调用,这里有一个要求,该方法必须返回以下三种中的任意一种:
None
,返回一个Response对象
、返回一个Request对象
或raise IgnoreRequest
。三种返回值的作用是不同的。None
:Scrapy将继续处理该request,执行其他的中间件的相应方法,直到合适的下载器处理函数(download handler)被调用,该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(Request.errback)方法会被调用。如果没有代码处理抛出的异常,则该异常被忽略且不记录。
② process_response(request, response, spider)
process_response的返回值也是有三种:
response对象
,request对象
,或者raise一个IgnoreRequest异常
如果其返回一个Response(可以与传入的response相同,也可以是全新的对象), 该response会被在链中的其他中间件的 process_response() 方法处理。
如果其返回一个 Request 对象,则中间件链停止, 返回的request会被重新调度下载。处理类似于 process_request() 返回request所做的那样。
如果其抛出一个 IgnoreRequest 异常,则调用request的errback(Request.errback)。
如果没有代码处理抛出的异常,则该异常被忽略且不记录(不同于其他异常那样)。
这里我们写一个简单的例子还是上面的项目,我们在中间件中继续添加如下代码:
...
def process_response(self, request, response, spider):
response.status = 201
return response
...
③ process_exception(request, exception, spider)
当下载处理器(download handler)或 process_request() (下载中间件)抛出异常(包括 IgnoreRequest 异常)时,Scrapy调用 process_exception()。
process_exception() 也是返回三者中的一个: 返回 None 、 一个 Response 对象、或者一个 Request 对象。
如果其返回 None ,Scrapy将会继续处理该异常,接着调用已安装的其他中间件的 process_exception() 方法,直到所有中间件都被调用完毕,则调用默认的异常处理。
如果其返回一个 Response 对象,则已安装的中间件链的 process_response() 方法被调用。Scrapy将不会调用任何其他中间件的 process_exception() 方法。
5.3 实战案例:
任务测试:使用scrapy爬取豆瓣图书Top250信息
使用shell命令直接爬取报403错误
# 在命令行下直接运行scrapy shell命令爬取信息,报403错误
$ scrapy shell https://book.douban.com/top250
>>> response.status
>>> 403
- ① 新建一个项目douban,命令如下所示:
scrapy startproject douban
- ② 新建一个Spider类,名字为dbbook,命令如下所示:
cd douban
scrapy genspider dbbook book.douban.com
- 编写爬虫代码。如下:
# -*- coding: utf-8 -*-
import scrapy
class DbbookSpider(scrapy.Spider):
name = 'dbbook'
allowed_domains = ['book.douban.com']
start_urls = ['https://book.douban.com/top250?start=0']
def parse(self, response):
#print("状态:")
pass
- ③ 执行爬虫命令,排除错误。
$ scrapy crawl dbbook
#结果返回403错误(服务器端拒绝访问)。
原因分析:默认scrapy框架请求信息中的User-Agent
的值为:Scrapy/1.5.0(http://scrapy.org)
.
解决方案:我们可以在settings.py配置文件中:设置 USER_AGENT
或者DEFAULT_REQUEST_HEADERS
信息:
USER_AGENT = 'Opera/9.80(WindowsNT6.1;U;en)Presto/2.8.131Version/11.11'
或者
...
DEFAULT_REQUEST_HEADERS = {
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language': 'en',
'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Safari/537.36',
}
...
④ 开启Downloader Middleware中间件
在项目的settings.py配置文件中:开启设置
DOWNLOADER_MIDDLEWARES
信息:
DOWNLOADER_MIDDLEWARES = {
'douban.middlewares.DoubanDownloaderMiddleware': 543,
}
def process_request(self, request, spider):
#输出header头信息
print(request.headers)
#伪装浏览器用户
request.headers['user-agent']='Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Safari/537.36'
return None