Scrapy 框架(五)之Downloader Middleware

Downloader Middleware

Downloader Middleware就是下载中间件,下载中间件是处于引擎(crawler.engine)和下载器(crawler.engine.download())之间的一层组件,可以有多个下载中间件被加载运行。

  1. 当引擎传递请求给下载器的过程中,下载中间件可以对请求进行处理 (例如增加http header信息,增加proxy信息等);
  2. 在下载器完成http请求,传递响应给引擎的过程中, 下载中间件可以对响应进行处理(例如进行gzip的解压等)

Downloader Middleware的功能十分强大,修改User-Agent、处理重定向、设置代理、失败重试、设置Cookies等功能都需要借助它来实现。

使用说明

其实Scrapy已经提供许多Downloader Middleware,比如负责失败重试、自动重定向等功能的Middleware,它们在default_settings.py文件中被DOWNLOADER_MIDDLEWARES_BASE变量所定义。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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
}

需要注意的是这个字典当中的键值是代表了调用的优先级,越小代表越靠近Scrapy引擎,数字越大越靠近Downloader,越小越先调用

如果我们自己定义下载中间件要添加到项目中,要激活下载器中间件组件,将其加入到 DOWNLOADER_MIDDLEWARES 设置中。

1
2
3
DOWNLOADER_MIDDLEWARES = {
'mySpider.middlewares.MyspiderDownloaderMiddleware': 543,
}

核心方法

每个中间件组件是一个定义了以下一个或多个方法的Python类 ,核的方法有下面三个:

1
2
3
process_request(request, spider)
process_response(request, response, spider)
process_exception(request, exception, spider)
process_request(self, request, spider)
  • 当每个request通过下载中间件时,该方法被调用。
  • process_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)方法会被调用。如果没有代码处理抛出的异常, 则该异常被忽略且不记录(不同于其他异常那样)。
  • 参数:
    • request (Request 对象) – 处理的request
    • spider (Spider 对象) – 该request对应的spider
process_response(self, request, response, spider)
  • 当下载器完成http请求,传递响应给引擎的时候调用

  • process_request() 必须返回以下其中之一: 返回一个 Response 对象、 返回一个 Request 对象或raise一个 IgnoreRequest 异常。

    • 如果其返回一个 Response (可以与传入的response相同,也可以是全新的对象), 该response会被在链中的其他中间件的 process_response() 方法处理。
    • 如果其返回一个 Request 对象,则中间件链停止, 返回的request会被重新调度下载。处理类似于 process_request() 返回request所做的那样。
    • 如果其抛出一个 IgnoreRequest 异常,则调用request的errback(Request.errback)。 如果没有代码处理抛出的异常,则该异常被忽略且不记录(不同于其他异常那样)。
  • 参数:
    • request (Request 对象) – response所对应的request
    • response (Response 对象) – 被处理的response
    • spider (Spider 对象) – response所对应的spider
process_exception(self, request, exception, spider)
  • 当Downloader或process_request()方法抛出异常时被调用
  • process_exception() 必须返回以下其中之一:None、Response对象、Request对象。
    • 如果返回None时,更低优先级的Downloader Middleware的process_exception()方法会被继续调用,直到所有的方法都被调用完毕。
    • 如果返回为Response对象时,更低优先级的Downloader Middleware的process_exception()方法不再被继续调用,每个Downloader Middleware的 process_response()方法转而被依次调用。
    • 如果返回为Request对象时,更低优先级的Downloader Middleware的process_exception()方法不再被继续调用,该 Request 对象会重新放到调度队列里面等待被调度,它相当于一个全新的 Request 。

使用案例

Scrapy代理IP、Uesr-Agent的切换都是通过DOWNLOADER_MIDDLEWARES进行控制,我们在settings.py同级目录下创建middlewares.py文件,包装所有请求 。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
import logging

import requests
from fake_useragent import UserAgent # 导入浏览器用户代理模块


class ProxyMiddleware(object):
def __init__(self, proxy_url):
self.logger = logging.getLogger(__name__)
self.proxy_url = proxy_url

def get_random_proxy(self):
try:
response = requests.get(self.proxy_url)
if response.status_code == 200:
proxy = response.text
return proxy
except requests.ConnectionError:
return False

def process_request(self, request, spider):
# proxy = self.get_random_proxy()
proxy = '223.19.41.6:8197'
if proxy:
uri = 'https://{proxy}'.format(proxy=proxy)
self.logger.debug('使用代理 ' + proxy)
request.meta['proxy'] = uri

@classmethod
def from_crawler(cls, crawler):
settings = crawler.settings
return cls(
proxy_url=settings.get('PROXY_URL') # PROXY_URL在settings中配置的代理池地址
)


class RandomUserAgentMiddleware(object): # 自定义浏览器代理中间件
# 随机更换Requests请求头信息的User-Agent浏览器用户代理
def __init__(self, crawler):
# 获取上一级父类基类的,__init__方法里的对象封装值
super(RandomUserAgentMiddleware, self).__init__()
# 实例化浏览器用户代理模块类
self.ua = UserAgent()
# 获取settings.py配置文件里的RANDOM_UA_TYPE配置的浏览器类型,
# 如果没有,默认random,随机获取各种浏览器类型
self.ua_type = crawler.settings.get('RANDOM_UA_TYPE', 'random')

@classmethod # 函数上面用上装饰符@classmethod,函数里有一个必写形式参数cls用来接收当前类名称
def from_crawler(cls, crawler): # 重载from_crawler方法
return cls(crawler) # 将crawler爬虫返回给类

def process_request(self, request, spider): # 重载process_request方法
def get_ua(): # 自定义函数,返回浏览器代理对象里指定类型的浏览器信息
return getattr(self.ua, self.ua_type)
request.headers.setdefault('User-Agent', get_ua()) # 将浏览器代理信息添加到Requests请求
-------------本文结束感谢您的阅读-------------

本文标题:Scrapy 框架(五)之Downloader Middleware

文章作者:GavinLiu

发布时间:2018年05月06日 - 23:05

最后更新:2018年05月07日 - 15:05

原始链接:http://gavinliu4011.github.io/post/f1fb118c.html

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

请博主吃个鸡腿吧
0%