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

11次阅读

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 = '2024-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 官方推荐、高可靠、易扩展的最佳实践。

text=ZqhQzanResources