Middleware中间件

Scrapy中的Middleware是一种切面扩展机制,能够对请求发出前和响应返回后以及Spider处理逻辑进行前置和后置处理,实现特定的切面逻辑。Scrapy中的Middleware在框架层面其实分为两种,分别是Spider Middleware和Downloader Middleware,分别作用于Spider组件和Downloader组件。通过这种切面机制,Middleware能够实现拦截修改请求和响应、错误处理、监控等功能。这篇笔记我们学习Scrapy中Middleware的用法。

自定义Middleware中间件

下面代码是一个最简单的Downloader Middleware,它会在请求发出前打印日志信息。

import logging


class DemoMiddleware(object):
    def __init__(self):
        self.log = logging.getLogger(__name__)

    def process_request(self, request, spider):
        self.log.info(f'Request URL: {request.url}')
        return None

编写自定义中间件类后它还不会立刻起作用,我们需要将其配置到框架中,在settings.py中我们如下配置即可。中间件的配置也具有优先级,数字越小优先级越高(越先执行)。

DOWNLOADER_MIDDLEWARES = {
   "tutorial.middlewares.DemoMiddleware": 543,
}

和上一章节中的Item Pipeline类似,中间件也可以在Spider中进行局部配置。

class QuotesSpider(scrapy.Spider):
    name = "quotes"
    custom_settings = {
        'DOWNLOADER_MIDDLEWARES': {
            "tutorial.middlewares.DemoMiddleware": 543,
        }
    }
    # ... 其它配置和具体执行逻辑

中间件类定义

Scrapy中的Middleware中间件类是一个普通类,下载器中间件通常包含以下定义。

class MyDownloaderMiddleware(object):
    def __init__(self):
        # 类构造方法

    @classmethod
    def from_crawler(cls, crawler):
        # 可以用来访问Scrapy框架核心组件的类方法
        return cls()

    def process_request(self, request, spider):
        # 当下载器发起请求前回调

    def process_response(self, request, response, spider):
        # 当下载器返回响应后回调

    def process_exception(self, request, exception, spider):
        # 当下载器或process_request()方法抛出异常时回调

    def spider_opened(self, spider):
        # 爬虫启动时回调,可以用于初始化资源

process_request():该方法在下载器发起请求前回调,常用于修改请求,比如实现随机化User-Agent、添加元信息、使用代理等。该方法如果返回None表示继续处理;返回Request对象则表示停止处理当前请求转为发出这个新的请求;返回Response对象表示立即返回该响应给引擎;使用raise关键字抛出IgnoreRequest异常则表示忽略该请求,随后process_exception()方法会被调用。

process_response():该方法当下载器返回响应给引擎时回调(无论是正常响应还是404等非正常响应),可用于对响应数据的解码、清洗等。该方法返回Response对象表示正常继续处理响应;返回Request表示停止处理当前的响应重新调度发起请求;使用raise关键字抛出IgnoreRequest异常表示丢弃该响应。

process_exception():该方法会在下载器或process_request()方法抛出异常时回调,其中包含异常处理逻辑。返回None表示将异常继续传递给其他中间件;返回Response表示生成一个成功响应传递给后续处理机制;返回Request表示重新调度发起请求,常用于异常重试。

Spider中间件通常包含以下定义。

class MySpiderMiddleware(object):
    def __init__(self):
        # 类构造方法

    @classmethod
    def from_crawler(cls, crawler):
        # 可以用来访问Scrapy框架核心组件的类方法
        return cls()

    def process_spider_input(self, response, spider):
        # 在响应被传递给爬虫的parse()方法之前回调

    def process_spider_output(self, response, result, spider):
        # 在爬虫的parse()方法返回结果之后回调

    def process_spider_exception(self, response, exception, spider):
        # 当爬虫parse()方法或process_spider_input()方法抛出异常时回调

    def process_start_requests(self, start_requests, spider):
        # 当爬虫启动,引擎收到初始请求之后回调

    def spider_opened(self, spider):
        # 爬虫启动时回调,可以用于初始化资源

process_spider_input():该方法在响应被传递给爬虫的parse()方法之前回调,可用于检查或修改传入爬虫的响应。返回None表示继续正常处理,也可以抛出异常中断处理,此时process_spider_exception()会被回调。

process_spider_output():该方法在爬虫的parse()方法返回结果之后回调,常用于记录日志、过滤修改爬虫产生的Item实例或请求信息等,比较常用。参数中result是一个Spider返回的可迭代对象,该方法也需要返回一个可迭代对象,其中包含修改后的Spider输出。

process_spider_exception():该方法在爬虫parse()方法或process_spider_input()方法抛出异常时回调。返回None表示将异常继续传递给其他中间件或引擎;也可以返回一个可迭代对象表示将异常修改为正常执行流程的pider输出。

process_start_requests():该方法在爬虫启动,引擎收到初始请求之后回调,可用于修改爬虫的初始请求列表、为所有初始请求添加统一参数、设置优先级、过滤或添加请求等。返回值必须是一个包含Request对象的可迭代对象。

默认启动的内置中间件

即使我们在settings.py中没有配置任何中间件,实际上Scrapy框架还是会为我们启动一组内置中间件,以实现爬虫的基础功能,这是因为Scrapy框架内置包含了一组*_MIDDLEWARES_BASE配置项。

默认启用的Downloader Middleware

默认启用的Downloader Middleware如下表格所示。

中间件 功能
OffsiteMiddleware(50) 根据allowed_domains过滤出站请求,不允许爬出指定域以外的URL
RobotsTxtMiddleware(100) 解析并遵守目标站点的robots.txt规则,禁止爬虫访问不允许的路径
HttpAuthMiddleware(300) 处理HTTP基本认证(Basic Auth),自动添加认证头以访问受保护页面
DownloadTimeoutMiddleware(350) 对下载过程设置超时时间限制,超过则触发错误或重试
DefaultHeadersMiddleware(400) 为每个请求添加默认HTTP头部(如AcceptAccept-Language
UserAgentMiddleware(500) 自动设置或轮换 User-Agent 字符串以模拟浏览器或隐藏爬虫身份
RetryMiddleware(550) 在失败(如 500、连接超时)时自动重试请求一定次数
AjaxCrawlMiddleware(560) 支持旧式AJAX爬取机制(如处理?_escaped_fragment_=类型的URL)
MetaRefreshMiddleware(580) 处理HTML中的<meta http-equiv="refresh">重定向并自动跟进
HttpCompressionMiddleware(590) 自动解压gzip/deflate压缩的响应体
RedirectMiddleware(600) 处理HTTP重定向(301/302/3xx 状态码),并跟进新的URL
CookiesMiddleware(700) 管理Cookie(接收服务器Set-Cookie响应头并在后续请求中返回Cookie)
HttpProxyMiddleware(750) 支持通过HTTP代理发送请求,处理代理连接与设置
DownloaderStats(850) 收集下载过程统计信息,如请求数、失败数、时延等
HttpCacheMiddleware(900) 启用HTTP缓存,将响应缓存在本地复用以减少网络请求

默认启用的Spider Middleware

默认启用的Spider Middleware如下表格所示。

中间件 功能
HttpErrorMiddleware(50) 过滤掉失败的HTTP响应(仅保留状态码2xx的响应)。支持通过handle_httpstatus_listHTTPERROR_ALLOWED_CODES/HTTPERROR_ALLOW_ALL设置让蜘蛛处理特定或所有非2xx响应
RefererMiddleware(700) 自动根据上一级页面设置请求的Referer头;遵循REFERER_ENABLEDREFERRER_POLICY等配置,控制引用来源策略
UrlLengthMiddleware(800) 拦截并丢弃超过URLLENGTH_LIMIT设置长度的URL请求,防止处理过长的URL
DepthMiddleware(900) 为每个请求计算并记录爬取深度(meta['depth']),可结合DEPTH_LIMIT限制深度,或使用 DEPTH_STATS_VERBOSE/DEPTH_PRIORITY 统计与排序请求

关闭内置中间件

如果你想关闭一个内置中间件,通常可以先查看文档看看该中间件使用的配置字段,其中是否有关闭或排除其处理行为的配置项,例如对于RobotsTxtMiddleware我们可以添加如下配置将其关闭。

ROBOTSTXT_OBEY = False

如果你真的在想从Scrapy处理流程中彻底移除一个中间件,也可以采用类似如下的配置(不推荐)。

DOWNLOADER_MIDDLEWARES = {
    'scrapy.downloadermiddlewares.robotstxt.RobotsTxtMiddleware': None,
}

当然,鉴于大部分内置中间件都有详细的配置项以调整其行为,你可能并不真的需要“移除”一个中间件。如果你确实需要这样做,最好还是先查看下其中的源码逻辑,避免移除一个重要的基础功能导致整个框架的运行流程出现问题。

作者:Gacfox
版权声明:本网站为非盈利性质,文章如非特殊说明均为原创,版权遵循知识共享协议CC BY-NC-ND 4.0进行授权,转载必须署名,禁止用于商业目的或演绎修改后转载。