Bootstrap

Scrapy:DownloaderMiddlewareManager 设计详解

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 提前返回设计的原因

这个设计是中间件系统的一个重要特性,允许中间件:

  1. 短路处理

    • 中间件可以决定是否继续执行后续中间件
    • 可以直接返回响应,跳过实际的下载过程
  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.3 执行顺序示例

DOWNLOADER_MIDDLEWARES = {
    'myproject.middlewares.CustomMiddleware1': 100,
    'myproject.middlewares.CustomMiddleware2': 200,
    'myproject.middlewares.CustomMiddleware3': 300,
}

执行流程:

  1. CustomMiddleware1.process_request
    • 返回 None → 继续执行
  2. CustomMiddleware2.process_request
    • 返回 Response → 直接返回,不执行后续中间件
  3. CustomMiddleware3.process_request
    • 不会执行到
  4. 实际下载操作
    • 不会执行到

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 链式处理

  1. 请求阶段

    • 从左到右执行
    • 支持提前返回
    • 可以跳过实际下载
  2. 响应阶段

    • 从右到左执行
    • 可以修改响应
    • 可以重新发起请求
  3. 异常阶段

    • 从右到左执行
    • 可以恢复错误
    • 可以重试请求

6.2 灵活性

  1. 中间件控制

    • 完全控制请求处理流程
    • 可以修改或替换响应
    • 可以处理异常情况
  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 的设计体现了:

  1. 灵活性:中间件可以完全控制请求处理流程
  2. 可扩展性:易于添加新的中间件
  3. 健壮性:完善的异常处理机制
  4. 优化性:支持缓存和提前返回机制
  5. 可维护性:清晰的处理流程和接口设计
;