本笔记彻底讲清三个问题:
- process_request 返回 Response 会发生什么?
- process_request 返回 Request 会发生什么?
- 在多个中间件情况下执行顺序如何变化?
一、Downloader Middleware 正常执行流程
假设中间件顺序:
MiddlewareA
MiddlewareB
MiddlewareC
正常流程(没有中断)
Engine
↓
A.process_request
↓
B.process_request
↓
C.process_request
↓
Downloader
↓
C.process_response
↓
B.process_response
↓
A.process_response
↓
Engine
↓
Spider
特点:
● process_request 正向执行
● process_response 反向执行
二、process_request 返回 Response
示例:
def process_request(self, request, spider):
return HtmlResponse(url=request.url, body=b"mock", encoding="utf-8")会发生什么?
Engine
↓
A.process_request
↓
B.process_request ← 返回 Response
↓
(停止执行 C.process_request)
(跳过 Downloader)
↓
C.process_response
↓
B.process_response
↓
A.process_response
↓
Engine
↓
Spider
关键结论
● 不会进入下载器
● 不会执行后续 process_request
● 会进入 process_response 链
● 会按反向顺序执行
● 最终交给 Spider
本质理解
返回 Response = 中断 request 链,直接开始 response 链
三、process_request 返回 Request
示例:
def process_request(self, request, spider):
request.meta["flag"] = True
return request会发生什么?
A.process_request ← 返回 Request
↓
停止当前 middleware 链
↓
重新进入 Scheduler
↓
重新走 Engine
↓
重新进入 process_request 链
关键结论
● 不会进入下载器
● 不会继续执行后续 process_request
● 不会进入 process_response
● 会被重新调度
重要
return request ≠ 发包
return request = 重新调度
如果无条件 return request
def process_request(self, request, spider):
return request会导致:
无限循环
必须加条件控制:
def process_request(self, request, spider):
if not request.meta.get("retry_flag"):
request.meta["retry_flag"] = True
return request
四、process_request 返回 None
示例:
def process_request(self, request, spider):
request.headers["User-Agent"] = "xxx"
return None会发生什么?
继续执行下一个 middleware 的 process_request
最终进入 Downloader
五、三种返回值对比总结
返回值 是否继续 process_request 是否进入 Downloader 是否进入 process_response 是否重新调度
None ✅ 是 ✅ 是 ✅ 是 ❌ 否
Response ❌ 否 ❌ 否 ✅ 是 ❌ 否
Request ❌ 否 ❌ 否 ❌ 否 ✅ 是
六、完整模型理解
可以把 Downloader Middleware 理解为:
正向
A → B → C → Downloader
反向A ← B ← C
情况一:return None
继续正向 → 到 Downloader → 再反向
情况二:return Response
停止正向 → 直接开始反向
情况三:return Request
停止正向 → 重新调度 → 从头开始
七、一句话总总结
● return None → 正常流转
● return Response → 跳过下载器,进入 response 链
● return Request → 重新调度
● return request 不是发包,而是回炉重造
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用。你还可以使用@来通知其他用户。