如何从Scrapy的RFPDupeFilter或自定义过滤器生成Item?
问题分析与解决方案
你当前的核心需求是:针对同一URL发送的不同meta的重复请求,在去重逻辑中提取重复请求的meta并生成Item,最终通过Pipeline写入JSON。但原代码存在关键问题——request_seen方法的设计目的是返回布尔值标记请求是否重复,不能直接在该方法中yield Item,需要借助Scrapy的信号机制来实现需求。
可行方案实现
方案1:利用request_reached_downloader信号实时处理重复请求
在请求到达下载器时触发信号,检查请求是否重复,若重复则生成Item并送入Pipeline。
步骤1:自定义去重过滤器
保留指纹记录逻辑,额外提供检查重复的方法:
from scrapy.dupefilters import BaseDupeFilter from scrapy.utils.request import request_fingerprint class CustomDupFilter(BaseDupeFilter): def __init__(self, path=None): self.fingerprints = set() super().__init__(path) def request_seen(self, request): fp = request_fingerprint(request) if fp in self.fingerprints: return True self.fingerprints.add(fp) return False def is_duplicate(self, request): # 供外部调用的重复检查方法 fp = request_fingerprint(request) return fp in self.fingerprints
步骤2:在爬虫中连接信号并处理重复请求
from scrapy import signals from scrapy.item import Item, Field # 定义存储meta的Item class MetaItem(Item): some_field = Field() some_other_field = Field() class MySpider(scrapy.Spider): name = "meta_spider" start_urls = ["http://www.example.com"] @classmethod def from_crawler(cls, crawler, *args, **kwargs): spider = super().from_crawler(crawler, *args, **kwargs) # 连接请求到达下载器的信号 crawler.signals.connect(spider.handle_duplicate_request, signal=signals.request_reached_downloader) return spider def handle_duplicate_request(self, request, spider): # 获取自定义去重过滤器实例 dupe_filter = spider.crawler.engine.downloader.middlewares.dupefilter if isinstance(dupe_filter, CustomDupFilter) and dupe_filter.is_duplicate(request): # 从meta生成Item item = MetaItem() item.update(request.meta) # 将Item送入Pipeline spider.crawler.engine.scraper.item_scraped(item, spider) def parse(self, response): # 发送第一个请求 yield scrapy.Request(url="http://www.example.com", meta={"some_field": "some_value"}) # 发送重复请求(必须加dont_filter=True,否则会被默认过滤) yield scrapy.Request(url="http://www.example.com", meta={"some_other_field": "some_other_value"}, dont_filter=True)
步骤3:配置自定义去重过滤器
在settings.py中添加:
DUPEFILTER_CLASS = "your_project_name.dupefilters.CustomDupFilter"
方案2:批量处理重复请求meta
将重复请求的meta暂存到过滤器中,在爬虫结束时批量生成Item。
修改自定义过滤器
class CustomDupFilter(BaseDupeFilter): def __init__(self, path=None): self.fingerprints = set() self.duplicate_metas = [] # 存储所有重复请求的meta super().__init__(path) def request_seen(self, request): fp = request_fingerprint(request) if fp in self.fingerprints: self.duplicate_metas.append(request.meta) return True self.fingerprints.add(fp) return False def get_duplicate_metas(self): return self.duplicate_metas
在爬虫中连接spider_closed信号批量生成Item
class MySpider(scrapy.Spider): # ... 其他代码省略 ... @classmethod def from_crawler(cls, crawler, *args, **kwargs): spider = super().from_crawler(crawler, *args, **kwargs) crawler.signals.connect(spider.batch_process_duplicates, signal=signals.spider_closed) return spider def batch_process_duplicates(self, spider): dupe_filter = spider.crawler.engine.downloader.middlewares.dupefilter if isinstance(dupe_filter, CustomDupFilter): for meta in dupe_filter.get_duplicate_metas(): item = MetaItem() item.update(meta) self.crawler.engine.scraper.item_scraped(item, spider)
关键注意事项
- 所有重复请求必须添加
dont_filter=True,否则会被Scrapy默认过滤,无法进入去重逻辑。 - 自定义Item需与你的meta字段匹配,确保Pipeline能正确解析并写入JSON。
- 信号处理逻辑中需正确获取去重过滤器实例,不同Scrapy版本的实例位置可能略有差异,若无法获取可尝试通过
crawler.settings.get('DUPEFILTER_CLASS')动态加载。
内容的提问来源于stack exchange,提问作者Kiran Kyle
相关产品推荐
相关产品推荐

