Scrapy 中如何在多个解析函数间传递变量值

在 scrapy 爬虫中,局部变量无法跨回调函数访问;若需将 parse() 中生成的日期(如 scrapedate)传递至深层解析函数(如 parse_race()),应通过 spider 实例属性(self.scrapedate)实现状态共享。

Scrapy 的请求-响应流程是异步且基于回调的:parse() 生成的 Request 对象在后续被调度执行时,会调用指定的回调函数(如 parse_date),但此时原始 parse() 的局部作用域已销毁,其中定义的变量(如 scrapedate)不可访问。因此,直接在 parse_race() 中引用 scrapedate 会导致 NameError: name 'scrapedate' is not defined。

正确做法是将该值提升为 Spider 实例的属性(即 self.scrapedate),使其在整个爬虫生命周期内可被任意回调方法读取。注意:由于 Scrapy 可能并发处理多个请求,不能简单地将 self.scrapedate 作为全局共享变量使用——必须确保每个请求链携带其对应的上下文。更健壮、推荐的方式是使用 cb_kwargs(Scrapy 1.7+ 支持),将参数显式传递给回调函数:

import scrapy
from datetime import datetime, timedelta
from dogscraper.items import DogItem

racedate = '2025-01-25'
days = 2
realdate = datetime.strptime(racedate, '%Y-%m-%d').date()
scrape_list = [(realdate - timedelta(days=x)).strftime('%Y-%m-%d') for x in range(days)]

class DogspiderSpider(scrapy.Spider):
    name = "dogspider"
    allowed_domains = ["www.thedogs.com.au"]
    start_urls = ["https://www.thedogs.com.au/racing/" + racedate]

    def parse(self, response):
        for scrapedate in scrape_list:
            next_dateurl = 'https://www.thedogs.com.au/racing/' + scrapedate
            # ✅ 推荐:使用 cb_kwargs 安全传递上下文
            yield scrapy.Request(
                url=next_dateurl,
                callback=self.parse_date,
                cb_kwargs={'scrapedate': scrapedate}
            )

    def parse_date(self, response, scrapedate):  # ← 参数自动注入
        nswmeetings = response.css('table.meeting-grid')[0].css('td.meetings-venues__name')
        for meeting in nswmeetings:
            meeting_url = meeting.css('a::attr(href)').get()
            if meeting_url:
                nextmeeting = 'https://www.thedogs.com.au' + meeting_url
                yield scrapy.Request(
                    url=nextmeeting,
                    callback=self.parse_meeting,
        

cb_kwargs={'scrapedate': scrapedate} # 向下透传 ) def parse_meeting(self, response, scrapedate): races = response.css('a.race-box.race-box--result') for race in races: race_url = race.css('::attr(href)').get() if race_url: nextrace = 'https://www.thedogs.com.au' + race_url yield scrapy.Request( url=nextrace, callback=self.parse_race, cb_kwargs={'scrapedate': scrapedate} # 持续透传 ) def parse_race(self, response, scrapedate): # ← 最终接收 dogs = response.css('tr.accordion__anchor.race-runner') for dog in dogs: dog_item = DogItem() dog_item['date'] = scrapedate # ✅ 安全赋值 # ... 其他字段提取逻辑 yield dog_item

优势说明

  • cb_kwargs 是 Scrapy 原生支持的线程安全机制,避免多请求间属性覆盖风险;
  • 语义清晰,显式声明依赖,便于调试与维护;
  • 符合函数式回调设计原则,不依赖隐式状态。

⚠️ 注意事项

  • 若仍选择 self.scrapedate = ... 方式,请确保无并发请求冲突(例如禁用并发:custom_settings = {'CONCURRENT_REQUESTS': 1}),否则将导致数据错乱;
  • 所有 cb_kwargs 传入的参数必须在对应回调函数签名中声明,否则抛出 TypeError;
  • 始终校验 CSS 选择器结果(如 .get() 返回 None 时需跳过),避免 IndexError 或 AttributeError。

综上,优先使用 cb_kwargs 传递上下文变量,这是 Scrapy 官方推荐、高可靠、易扩展的最佳实践。