scrapy 的 `parse` 方法必须显式 `yield` 所有后续请求,若将请求生成逻辑拆分为子函数但未逐层 `yield`,这些请求将被丢弃,导致爬虫停止递归抓取。
在 Scrapy 中,parse 方法不仅是数据解析入口,更是请求调度的唯一出口。其返回值(或 yield 产出)会被 Scrapy 引擎捕获并加入调度队列;任何未被 yield 的 scrapy.Request 对象都将被静默丢弃——这正是重构后代码失效的根本原因。
回顾原始有效代码:
def parse(self, response, **kwargs):
yield ScrapyItem(...) # ✅ 显式产出 Item
for link in self.extract_links(response):
yield scrapy.Request(...) # ✅ 显式产出 Request → 进入队列
所有 Request 均由 parse 直接 yield,Scrapy 可完整感知并调度。
而重构后的错误版本中:
def parse(self, response, **kwargs):
yield ScrapyItem(...)
self.extract_and_follow_links(response) # ❌ 仅调用,未 yield 返回值
def extract_and_follow_links(self, response):
links = self.extract_links(response)
return self.follow_links(response, links) # ✅ 返回 generator,但未被消费
def follow_links(self, response, links):
for link in links:
yield scrapy.Request(...) # ✅ generator 内部 yield,但外部未迭代
follow_links() 是一个生成器函数(generator function),它返回的是一个惰性迭代器对象,而非立即执行的请求列表。若不主动遍历该迭代器(如用 for req in gen: yield req)或直接 yield from gen,其中的 yield scrapy.Request(…) 永远不会触发,请求也就永远不会提交给 Scrapy 调度器。
✅ 正确修复方式有两种(推荐后者,更简洁清晰):