在Downloader Middleware的功能十分强大:
- 可以修改User-Agent
- 处理重定向
- 设置代理
- 失败重试
- 设置Cookies等
Downloader Middleware在整个架构中起作用的位置是以下两个:
- 在Scheduler调度出队列的Request发送给Doanloader下载之前,也就是我们可以在Request执行下载前对其进行修改。
- 在下载后生成的Response发送给Spider之前,也就是我们可以生成Resposne被Spider解析之前对其进行修改。
- 在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
}
- 字典格式,其中数字为优先级,越小的优先调用。
我们可以通过项目的DOWNLOADER_MIDDLEWARES变量设置来添加自己定义的Downloader Middleware。其中Downloader Middleware有三个核心方法:
1 ) 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)方法会被调用。如果没有代码处理抛出的异常,则该异常被忽略且不记录。
2 ) 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
3 ) 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()
方法。
1 ) 新建一个项目douban,命令如下所示:
$ scrapy startproject douban
2 ) 新建一个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
3 ) 执行爬虫命令,排除错误
$ scrapy crawl dbbook
#结果返回403错误(服务器端拒绝访问)。
原因分析:默认scrapy框架请求信息中的User-Agent的值为:Scrapy/1.5.0(http://scrapy.org).
解决方案:我们可以在settings.py配置文件中:设置 USER_AGENT 或者DEFAULT_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'
# 或
# ...
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',
}
# ...
还有一种解决方案:
在middlewares.py
文件中找到DoubanDownloaderMiddleware
类, 在里面的process_request
方法中处理如下:
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
4 ) 开启Downloader Middleware中间件
在项目的settings.py配置文件中:开启设置DOWNLOADER_MIDDLEWARES信息:
DOWNLOADER_MIDDLEWARES = {
'douban.middlewares.DoubanDownloaderMiddleware': 543,
}