如何在Scrapy CrawlSpider中把__init__变量传递到parse_item?
Scrapy CrawlSpider 参数传递问题解决方案
问题核心
使用CrawlSpider时,需要将__init__中从数据库获取的种子URL附加信息传递到parse_item方法,但普通Spider的meta参数在Rule中无法直接生效,无法关联每个请求对应的初始doc数据。
可行解决方案:重写start_requests + 传递Request的meta
步骤1:重写start_requests生成带meta的初始请求
放弃直接在__init__中填充start_urls,改为重写start_requests方法,为每个种子URL生成Request时,将对应的doc数据存入meta字段。
步骤2:在process_request中传递meta
Rule中配置的process_request方法负责将初始请求的meta传递给后续Rule生成的所有请求,确保整个爬取链路中都能拿到对应的doc信息。
修改后的完整代码示例
from scrapy.spiders import CrawlSpider, Rule from scrapy.linkextractors import LinkExtractor import scrapy class TestCrawlerSpider(CrawlSpider): name = 'testcrawler' allowed_domains = [] current_batch = None rules = ( Rule(LinkExtractor(), process_request='my_request_processor', callback='parse_item', follow=True, process_links="filter_links"), Rule(LinkExtractor(), process_request='my_request_processor', follow=True, process_links="filter_links"), ) def __init__(self, *args, **kwargs): self.current_batch = run_manager.get_next_batch() # 填充允许的域名列表 for doc in self.current_batch: self.allowed_domains.append(doc.get('domain')) super(TestCrawlerSpider, self).__init__(*args, **kwargs) def start_requests(self): # 生成携带doc信息的初始请求 for doc in self.current_batch: yield scrapy.Request( url=doc.get('website'), meta={'doc': doc}, dont_filter=True # 若存在重复URL可启用该参数 ) def my_request_processor(self, request, response): # 将当前请求的doc信息传递给新生成的请求 request.meta['doc'] = response.meta.get('doc') return request def parse_item(self, response): # 直接从meta中获取对应的doc数据 doc = response.meta.get('doc') if doc: doc_id = doc.get('id') # 后续Item处理逻辑示例 item = {} item['doc_id'] = doc_id # ... 填充其他Item字段 yield item
关键说明
start_requests是Scrapy Spider的核心入口,重写它可以完全控制初始请求的生成逻辑,包括添加自定义meta。process_request必须作为Spider类的方法(原代码中的全局函数改为类方法更合理),确保能访问请求上下文的meta数据。- 所有由Rule提取生成的子请求都会继承初始请求的doc信息,因此在
parse_item中可以稳定获取对应种子的附加数据。
替代方案:URL映射字典(仅适用于特殊场景)
如果爬取范围仅限制在种子URL本身,不跟进其他链接,可以在__init__中建立url_to_doc映射字典:
def __init__(self, *args, **kwargs): self.current_batch = run_manager.get_next_batch() self.url_to_doc = {} for doc in self.current_batch: url = doc.get('website') self.url_to_doc[url] = doc self.allowed_domains.append(doc.get('domain')) super(TestCrawlerSpider, self).__init__(*args, **kwargs) def parse_item(self, response): doc = self.url_to_doc.get(response.url) if doc: doc_id = doc.get('id')
此方案仅适用于不跟进子链接的场景,无法处理Rule生成的后续请求,因此优先推荐第一种方案。
内容的提问来源于stack exchange,提问作者Chris
相关产品推荐
相关产品推荐

