Scrapy CSV追加导出时如何过滤重复条目?
问题分析与修复方案
你的代码之所以不管条目是否存在都会追加,核心问题有两个:
- 直接用
item not in row做比较完全无效:Scrapy的Item对象和CSV里的字符串列表row是完全不同的类型,永远不会匹配,所以每次都会执行export_item。 - 每次
process_item都重复打开文件读取,不仅效率极低,还和spider_opened中打开的文件句柄存在冲突风险。
下面是修复后的完整代码,同时优化了查重效率:
修改后的Pipelines.py
from scrapy import signals from scrapy.contrib.exporter import CsvItemExporter import csv class BlogscrapePipeline(object): def __init__(self): self.files = {} self.existing_links = set() # 用集合存储已存在的链接,实现O(1)快速查重 @classmethod def from_crawler(cls, crawler): pipeline = cls() crawler.signals.connect(pipeline.spider_opened, signals.spider_opened) crawler.signals.connect(pipeline.spider_closed, signals.spider_closed) return pipeline def spider_opened(self, spider): filename = f'{spider.name}_items.csv' # 先读取已有文件,把存在的链接存入集合 try: with open(filename, 'rt', encoding='utf-8') as file: reader = csv.DictReader(file) # 假设Link是唯一标识(标题可能重复,但链接通常唯一),用它判断条目是否存在 for row in reader: self.existing_links.add(row['Link']) except FileNotFoundError: # 如果文件不存在,直接跳过集合初始化 pass # 以追加模式打开文件,初始化exporter file = open(filename, 'a+b') self.files[spider] = file self.exporter = CsvItemExporter(file) self.exporter.fields_to_export = ['Title','Link','Comments','Words'] # 仅当文件是新建时写入表头,避免重复写入 if not self.existing_links: self.exporter.start_exporting() else: # 文件已有内容,跳过表头直接准备追加 self.exporter._write_headers_and_set_fields_to_export() def spider_closed(self, spider): self.exporter.finish_exporting() file = self.files.pop(spider) file.close() def process_item(self, item, spider): # 用唯一标识Link检查是否已存在 item_link = item['Link'] if item_link not in self.existing_links: self.exporter.export_item(item) self.existing_links.add(item_link) # 加入集合,避免后续重复导出 return item
关键修复点说明
- 高效查重逻辑:用集合存储已存在的链接,集合的查找操作是常数时间,比每次读文件遍历高效太多。
- 有效的存在判断:用
item['Link'](链接作为唯一标识更合理)和集合中的元素对比,这才是符合逻辑的查重方式。 - 处理文件初始化场景:区分新建文件和已有内容的文件,避免重复写入表头。
- 减少IO操作:仅在爬虫启动时读取一次已有数据,后续处理直接用内存中的集合判断,避免重复打开文件的开销。
这样修改后,就能精准实现「条目不存在则追加,存在则丢弃」的需求了。
内容的提问来源于stack exchange,提问作者Dhiraz Gazurel
相关产品推荐
相关产品推荐

