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头部(如Accept 、Accept-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_list 或HTTPERROR_ALLOWED_CODES/HTTPERROR_ALLOW_ALL 设置让蜘蛛处理特定或所有非2xx 响应 |
RefererMiddleware(700) | 自动根据上一级页面设置请求的Referer 头;遵循REFERER_ENABLED 和REFERRER_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,
}
当然,鉴于大部分内置中间件都有详细的配置项以调整其行为,你可能并不真的需要“移除”一个中间件。如果你确实需要这样做,最好还是先查看下其中的源码逻辑,避免移除一个重要的基础功能导致整个框架的运行流程出现问题。