You need to enable JavaScript to run this app. Scrapy串行爬取商品时自定义yield数据及start_requests调用失效问题
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.30 07:27:03