Scrapy串行爬取商品时自定义yield数据及start_requests调用失效问题
核心问题原因
1. 手动调用start_requests无效果的原因
start_requests是带yield的生成器函数,直接调用时只会返回一个未执行的生成器对象,不会运行内部逻辑,也不会将封装的Request对象提交给Scrapy调度引擎,自然没有任何响应。- Scrapy的调度逻辑仅识别在回调流程中直接yield传递的Request/Item对象,不会主动扫描方法调用返回的生成器内容。
2. 取消yield_data的yield语句后行为异常的原因
- Python的生成器是惰性执行机制:只要函数体内包含
yield关键字,函数就会被识别为生成器,只有对返回的生成器对象做迭代操作(遍历、next()取值、强制转列表)时,函数内部的代码才会真正运行。 - 注释掉yield语句时,
yield_data退化为普通函数,调用时会直接执行内部的日志、打印逻辑,因此能看到控制台输出;一旦加回yield,函数变成生成器,仅做空函数调用不会触发内部任何代码执行,自然会感觉程序根本没有进入这个函数。
适配需求的正确实现方案
要实现逐商品串行爬取、拿到不存在商品的响应才关闭蜘蛛、不提前发起多余请求,需要修正两个核心逻辑:
- 所有新发起的请求必须通过yield传递给Scrapy引擎,不能仅调用生成器方法
- 强制全局单并发,避免Scrapy提前批量发送队列中的请求
- 独立数据处理函数需按普通函数返回数据,避免无迭代的生成器造成执行逻辑中断
修正后可运行的代码
import scrapy import json from scrapy.exceptions import CloseSpider class ItemsSpider(scrapy.Spider): name = 'items' # 强制单并发+请求间隔,实现完全串行爬取,杜绝提前发多余请求 custom_settings = { 'CONCURRENT_REQUESTS': 1, 'DOWNLOAD_DELAY': 1 } # 起始商品ID current_crawl_id = 555555 def start_requests(self): # 每次仅生成当前单个商品的请求 crawl_id = self.current_crawl_id self.current_crawl_id += 1 url = f'https://market.com/item/{crawl_id}' yield scrapy.Request(url, meta={'item_id': crawl_id}) def parse(self, response): item_id = response.meta['item_id'] # 检测到商品不存在直接关闭蜘蛛 if b'Incorrect' in response.body: raise CloseSpider('Incorrect item') # 请求对应API获取商品销售数据 api_url = f'https://market.com/api/item?ajax=1&itemid={item_id}' yield scrapy.Request( api_url, callback=self.parse_item, headers={ 'accept': 'application/json, text/plain, */*', 'user-agent': 'Mozilla/5.0' }, meta={'item_id': item_id} ) def parse_item(self, response): raw_data = json.loads(response.body) # 调用独立函数处理并返回item数据 item = self.yield_data(raw_data) # 将item传递到Scrapy管道 yield item # 发起下一个商品的爬取请求:必须迭代start_requests返回的生成器,将请求yield给引擎 for next_req in self.start_requests(): yield next_req def yield_data(self, raw_data): self.logger.info('Starting process item data') processed_item = { 'Name': raw_data['name'], 'Price': raw_data['price'], } self.logger.info(f'Processed item: {processed_item}') # 作为普通函数返回处理后的item,而非生成器 return processed_item
关键逻辑说明
- 之前
self.start_requests()调用无效的问题,已通过迭代生成器返回值、逐个yield Request的方式修复,Scrapy引擎能正常识别到新的爬取请求。 - 之前
yield_data函数无执行痕迹的问题,已将其从生成器改为普通函数,接收原始数据处理后直接返回item,在parse_item中拿到返回值后再yield到管道,函数内部的日志、打印逻辑会正常执行。 - 全局单并发配置确保每次仅处理一个请求,当检测到“Incorrect item”响应时,队列中没有提前发出的多余请求,可直接关闭蜘蛛,完全符合爬取逻辑要求。
内容的提问来源于stack exchange,提问作者user9958765
相关产品推荐
相关产品推荐

