pin_drop当前位置:知识文库 ❯ 图文
Scrapy中间件详解 - 下载器与爬虫中间件完整指南
一、中间件概述
中间件(Middleware)是Scrapy框架中用于扩展和修改请求/响应处理流程的核心机制。Scrapy的中间件分为两大类:下载器中间件(Downloader Middleware)和爬虫中间件(Spider Middleware)。它们充当了Scrapy引擎与下载器/Spider之间的拦截器,可以在请求发送前和响应返回后插入自定义逻辑。
下载器中间件位于Scrapy引擎和下载器之间,主要用于设置代理IP、修改请求头、处理Cookie、重试请求、处理响应等场景。它是Scrapy中最常用的中间件类型。爬虫中间件位于引擎和Spider之间,主要用于处理Spider的输入和输出,如处理异常、过滤Item、修改请求等。
二、中间件核心方法与语法
下载器中间件通过三个核心方法来拦截和处理请求与响应:
代码示例
class MyDownloaderMiddleware:
def process_request(self, request, spider):
"""处理请求 - 在请求发送到下载器之前调用"""
# 可以修改请求头、设置代理等
return None # 返回None继续处理
def process_response(self, request, response, spider):
"""处理响应 - 在下载器返回响应后调用"""
# 可以检查响应内容、处理重试等
return response # 必须返回response或request
def process_exception(self, request, exception, spider):
"""处理异常 - 当下载器或process_request抛出异常时调用"""
# 可以处理超时、连接错误等
return None # 返回None继续处理其他异常处理器
三、中间件方法与返回值详解
下载器中间件方法
process_request 返回值含义
settings.py 配置方法
代码示例
DOWNLOADER_MIDDLEWARES = {
'myproject.middlewares.RandomUserAgentMiddleware': 400,
'myproject.middlewares.ProxyMiddleware': 500,
'myproject.middlewares.CustomRetryMiddleware': 550,
}
SPIDER_MIDDLEWARES = {
'myproject.middlewares.MySpiderMiddleware': 500,
}
四、完整代码示例
示例1:随机User-Agent中间件
每次请求随机选择一个User-Agent,模拟不同浏览器访问,是最基础的反反爬虫手段:
代码示例
import random
class RandomUserAgentMiddleware:
"""随机User-Agent中间件 - 每次请求使用不同的浏览器标识"""
def __init__(self):
self.user_agents = [
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/120.0.0.0 Safari/537.36',
'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:121.0) Gecko/20100101 Firefox/121.0',
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 Safari/605.1.15',
'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 Chrome/120.0.0.0 Safari/537.36',
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Edge/120.0.0.0',
]
def process_request(self, request, spider):
user_agent = random.choice(self.user_agents)
request.headers['User-Agent'] = user_agent
spider.logger.debug(f'使用UA: {user_agent[:50]}...')
return None
示例2:代理中间件
通过 request.meta['proxy'] 设置代理IP,并使用 from_crawler 从settings读取配置:
代码示例
class ProxyMiddleware:
"""代理中间件 - 为请求设置HTTP代理"""
def __init__(self, proxy_url):
self.proxy_url = proxy_url
@classmethod
def from_crawler(cls, crawler):
return cls(
proxy_url=crawler.settings.get('PROXY_URL', ''),
)
def process_request(self, request, spider):
if self.proxy_url:
request.meta['proxy'] = self.proxy_url
spider.logger.debug(f'使用代理: {self.proxy_url}')
return None
def process_exception(self, request, exception, spider):
spider.logger.warning(f'请求异常: {exception}')
return None
示例3:自定义重试中间件
继承Scrapy内置的RetryMiddleware,自定义重试策略。当遇到5xx错误码或网络异常时自动重试,最多重试3次:
代码示例
from scrapy.downloadermiddlewares.retry import RetryMiddleware
from scrapy.utils.response import response_status_message
class CustomRetryMiddleware(RetryMiddleware):
"""自定义重试中间件 - 针对特定状态码和异常自动重试"""
RETRY_TIMES = 3
RETRY_HTTP_CODES = [500, 502, 503, 504, 408, 429]
def process_response(self, request, response, spider):
# 检查响应状态码是否需要重试
if response.status in self.RETRY_HTTP_CODES:
retry_times = request.meta.get('retry_times', 0)
if retry_times < self.RETRY_TIMES:
spider.logger.info(
f'重试 {retry_times + 1}/{self.RETRY_TIMES}: '
f'{request.url} (状态码: {response.status})'
)
request.meta['retry_times'] = retry_times + 1
request.dont_filter = True
return request
return response
def process_exception(self, request, exception, spider):
retry_times = request.meta.get('retry_times', 0)
if retry_times < self.RETRY_TIMES:
spider.logger.info(
f'异常重试 {retry_times + 1}/{self.RETRY_TIMES}: {request.url}'
)
request.meta['retry_times'] = retry_times + 1
request.dont_filter = True
return request.copy()
return None
示例4:Cookie池中间件
代码示例
import random
class CookiePoolMiddleware:
"""Cookie池中间件 - 随机使用Cookie池中的Cookie"""
def __init__(self, cookie_list):
self.cookie_list = cookie_list
@classmethod
def from_crawler(cls, crawler):
return cls(
cookie_list=crawler.settings.getlist('COOKIE_POOL', []),
)
def process_request(self, request, spider):
if self.cookie_list:
cookie = random.choice(self.cookie_list)
request.cookies = cookie
spider.logger.debug(f'使用Cookie: {cookie.get("session_id", "")[:10]}...')
return None
五、中间件类型对比
六、实际应用场景
-
场景1 - 反反爬虫:组合使用随机User-Agent、代理IP池和Cookie池中间件,使爬虫的请求特征更接近真实用户,绕过网站的反爬检测机制。
-
场景2 - 请求重试:当遇到网络波动、服务器5xx错误或429(请求过快)时,自动重试请求,提高爬取成功率。可以自定义重试次数、间隔和触发条件。
-
场景3 - 请求认证:对特定URL自动添加认证信息,如API Key、Token、Referer等。可以在
process_request中根据URL模式判断并添加请求头。
小贴士
Scrapy默认启用了多个内置中间件,如 scrapy.downloadermiddlewares.retry.RetryMiddleware(优先级550)和 scrapy.downloadermiddlewares.redirect.RedirectMiddleware(优先级600)。在编写自定义中间件时,需要注意与内置中间件的优先级关系,确保执行顺序正确。可以使用 scrapy settings --get DOWNLOADER_MIDDLEWARES 查看当前启用的中间件列表。
七、注意事项与最佳实践
注意1:中间件优先级数字越小越先执行(
process_request顺序执行),但process_response是逆序执行的(数字大的先执行)。
注意2:
process_request返回None才会继续下载,返回Response会跳过下载器直接返回给引擎。
注意3:代理设置通过
request.meta['proxy']传递,格式为http://host:port。如果需要认证,使用http://user:pass@host:port。
注意4:
from_crawler类方法是获取Scrapy配置的标准方式,通过它可以访问crawler.settings、crawler.signals等核心对象。
八、练习题
练习1
编写一个下载器中间件 RefererMiddleware,为每个请求添加 Referer 请求头。Referer的值从settings.py中的 DEFAULT_REFERER 配置项读取,使用 from_crawler 方法获取配置。
练习2
编写一个中间件 AutoProxyMiddleware,当响应状态码为403时,自动从代理池中更换代理IP并重试请求。代理池维护在settings.py的 PROXY_POOL 列表中,使用 process_response 方法实现。
九、常见问题FAQ
常见问题
下载器中间件和爬虫中间件有什么区别?
下载器中间件作用于请求和响应阶段,位于引擎与下载器之间,主要用于处理HTTP层面的逻辑(代理、UA、重试等);爬虫中间件作用于Spider阶段,位于引擎与Spider之间,主要用于处理Spider的输入输出(Item过滤、请求去重等)。日常开发中下载器中间件使用频率更高。
中间件的执行顺序是怎样的?
process_request按优先级从小到大执行(数字小的先执行),process_response则相反(数字大的先执行),process_exception也按从小到大执行。这种设计确保了请求和响应的处理形成"洋葱模型"。
如何在中间件中使用异步代理池API?
可以在 open_spider() 中初始化代理池连接,在 process_request() 中从池中获取代理。对于异步API,可以使用 twisted.internet.defer 的 Deferred 对象来返回异步结果。
中间件中可以访问Spider的属性吗?
可以。所有中间件方法都接收 spider 参数,可以通过 spider.name、spider.custom_attr 等访问Spider的属性和方法。也可以通过 spider.logger 记录日志。
如何禁用某个内置中间件?
在 settings.py 中将内置中间件的优先级设置为 None 即可禁用,例如:DOWNLOADER_MIDDLEWARES = {'scrapy.downloadermiddlewares.cookies.CookiesMiddleware': None}。
本文涉及AI创作
内容由AI创作,请仔细甄别