Scrapy中间件顺序

7 投票
2 回答
3913 浏览
提问于 2025-04-16 21:05

Scrapy的文档中提到:

第一个中间件是离引擎最近的,而最后一个中间件是离下载器最近的。

要决定你的中间件的顺序,可以查看DOWNLOADER_MIDDLEWARES_BASE这个设置,并根据你想要插入中间件的位置选择一个值。顺序很重要,因为每个中间件执行的操作不同,你的中间件可能依赖于之前(或之后)某个中间件的执行。

我不太明白的是,值越高的中间件是先执行还是后执行。

例如:

'myproject.middlewares.MW1': 543,
'myproject.middlewares.MW2': 542,

问题:

  1. 这些中间件中,哪个会先执行?我的尝试显示MW2会先执行。
  2. 有效的顺序范围是什么?是0到999吗?

2 个回答

11

我知道这个问题已经有人回答过了,但其实事情比看起来要复杂一些——请求和响应的处理顺序是相反的。

你可以这样理解:

  • 0 - 引擎发出请求
  • 1..无穷大 - 处理请求的中间件被调用
  • 无穷大 - 实际下载发生(如果没有中间件处理请求的话)
  • 无穷大..1 - 处理响应的中间件被调用
  • 0 - 引擎收到响应

所以……如果我把我的中间件标记为1,它将是第一个被执行的请求中间件,同时也是最后一个被执行的响应中间件……如果我的中间件标记为901,它将是最后一个被执行的请求中间件,同时也是第一个被执行的响应中间件(如果只定义了默认中间件的话)。

其实答案就是,这确实让人困惑。请求的开始离引擎最近(在零的位置),而请求的结束离下载器最近(在高数字的位置)。响应的开始离下载器最近(在高数字的位置),而响应的结束离引擎最近(在零的位置)。这就像是从引擎出发再返回的旅程……下面是来自scrapy的相关代码,让这一切变得有趣(init是从MiddlewareManager复制过来的,仅包含相关的方法):

class DownloaderMiddlewareManager(MiddlewareManager):
    def __init__(self, *middlewares):
        self.middlewares = middlewares
        self.methods = defaultdict(list)
        for mw in middlewares:
            self._add_middleware(mw)

    def _add_middleware(self, mw):
        if hasattr(mw, 'process_request'):
            self.methods['process_request'].append(mw.process_request)
        if hasattr(mw, 'process_response'):
            self.methods['process_response'].insert(0, mw.process_response)
        if hasattr(mw, 'process_exception'):
            self.methods['process_exception'].insert(0, mw.process_exception)

如你所见,请求方法是按顺序添加的(高数字的放在后面),而响应和异常方法则是插入到最前面(高数字的在前面)。

3
  1. 这些中哪个会先执行?我试了一下,发现MW2会先执行。

正如你引用的文档所说:

第一个中间件是离引擎最近的,最后一个是离下载器最近的。

所以,值为542的下载器中间件会在值为543的中间件之前执行。这意味着首先会调用myproject.middlewares.MW1.process_request(request, spider),然后如果需要修改请求,它会被传递给下一个下载器中间件。

  1. 这些顺序的有效范围是什么?是0到999吗?

这个值是一个整数。

更新:

看看架构

还有完整的引用

DOWNLOADER_MIDDLEWARES设置会与Scrapy中定义的DOWNLOADER_MIDDLEWARES_BASE设置合并(这个设置不应该被覆盖),然后按顺序排序,以得到最终的启用中间件的排序列表:第一个中间件是离引擎最近的,最后一个是离下载器最近的。

所以,由于这些值是整数,它们的范围是Python整数的范围。

撰写回答