Scrapy DownloaderMiddlewareManager 设计详解
1. 整体架构
DownloaderMiddlewareManager
继承自 MiddlewareManager
,是 Scrapy 下载系统中的中间件管理器,负责协调和管理所有下载中间件的执行。
源码地址
class DownloaderMiddlewareManager(MiddlewareManager):
component_name = "downloader middleware"
2. 核心组件
2.1 中间件方法注册
def _add_middleware(self, mw: Any) -> None:
if hasattr(mw, "process_request"):
self.methods["process_request"].append(mw.process_request)
if hasattr(mw, "process_response"):
self.methods["process_response"].appendleft(mw.process_response)
if hasattr(mw, "process_exception"):
self.methods["process_exception"].appendleft(mw.process_exception)
特点:
- 支持三种中间件方法:请求处理、响应处理、异常处理
- 请求处理方法从左到右执行(append)
- 响应和异常处理方法从右到左执行(appendleft)
3. 请求处理流程
3.1 process_request 的设计
@inlineCallbacks
def process_request(request: Request) -> Generator[Deferred[Any], Any, Response | Request]:
for method in self.methods["process_request"]:
response = yield deferred_from_coro(method(request=request, spider=spider))
if response is not None and not isinstance(response, (Response, Request)):
raise _InvalidOutput(
f"Middleware {method.__qualname__} must return None, Response or "
f"Request, got {response.__class__.__name__}"
)
if response:
return response
return (yield download_func(request, spider))
3.2 提前返回设计的原因
这个设计是中间件系统的一个重要特性,允许中间件:
-
短路处理:
- 中间件可以决定是否继续执行后续中间件
- 可以直接返回响应,跳过实际的下载过程
-
常见应用场景:
class CacheMiddleware: def process_request(self, request, spider): if self.has_cache(request): return self.get_cached_response(request) # 返回 None 继续处理 return None
-
优势:
- 提高效率:避免不必要的下载
- 灵活控制:中间件可以完全接管请求处理
- 支持缓存:可以直接返回缓存的响应
3.3 执行顺序示例
DOWNLOADER_MIDDLEWARES = {
'myproject.middlewares.CustomMiddleware1': 100,
'myproject.middlewares.CustomMiddleware2': 200,
'myproject.middlewares.CustomMiddleware3': 300,
}
执行流程:
- CustomMiddleware1.process_request
- 返回 None → 继续执行
- CustomMiddleware2.process_request
- 返回 Response → 直接返回,不执行后续中间件
- CustomMiddleware3.process_request
- 不会执行到
- 实际下载操作
- 不会执行到
4. 响应处理流程
4.1 process_response 的设计
@inlineCallbacks
def process_response(response: Response | Request) -> Generator[Deferred[Any], Any, Response | Request]:
for method in self.methods["process_response"]:
response = yield deferred_from_coro(method(request=request, response=response, spider=spider))
if not isinstance(response, (Response, Request)):
raise _InvalidOutput(...)
if isinstance(response, Request):
return response
return response
特点:
- 可以修改响应
- 可以返回新的请求(重试机制)
- 从右到左执行中间件
5. 异常处理流程
5.1 process_exception 的设计
@inlineCallbacks
def process_exception(failure: Failure) -> Generator[Deferred[Any], Any, Failure | Response | Request]:
for method in self.methods["process_exception"]:
response = yield deferred_from_coro(method(request=request, exception=exception, spider=spider))
if response:
return response
return failure
特点:
- 可以处理下载异常
- 可以返回新的响应或请求
- 从右到左执行中间件
6. 设计特点
6.1 链式处理
-
请求阶段:
- 从左到右执行
- 支持提前返回
- 可以跳过实际下载
-
响应阶段:
- 从右到左执行
- 可以修改响应
- 可以重新发起请求
-
异常阶段:
- 从右到左执行
- 可以恢复错误
- 可以重试请求
6.2 灵活性
-
中间件控制:
- 完全控制请求处理流程
- 可以修改或替换响应
- 可以处理异常情况
-
优化机制:
- 缓存支持
- 重试机制
- 错误恢复
7. 最佳实践
7.1 中间件开发
class CustomMiddleware:
def process_request(self, request, spider):
# 返回 None 继续处理
# 返回 Response 直接返回响应
# 返回 Request 重新发起请求
pass
def process_response(self, request, response, spider):
# 必须返回 Response 或 Request
return response
def process_exception(self, request, exception, spider):
# 返回 None 继续处理异常
# 返回 Response 或 Request 恢复错误
pass
8. 总结
DownloaderMiddlewareManager 的设计体现了:
- 灵活性:中间件可以完全控制请求处理流程
- 可扩展性:易于添加新的中间件
- 健壮性:完善的异常处理机制
- 优化性:支持缓存和提前返回机制
- 可维护性:清晰的处理流程和接口设计