Scrapy技术问询:写入前检查CSV文件中的条目标题是否存在
我明白你要解决的核心问题——想让Scrapy爬虫只往CSV里写入标题不存在的条目,而且现成的DuplicatesPipeline对你不适用。咱们来把你的自定义Pipeline补全并优化,确保它能稳定工作。
问题分析
你之前的代码只完成了读取CSV的开头部分,缺少了存储已有标题、检查重复和写入新条目的核心逻辑。另外直接用open操作文件时,还要注意编码、文件不存在的异常处理,以及爬虫结束时的文件关闭操作。
完整的自定义Pipeline代码
下面是补全后的CheckCsvPipeline,我会逐段解释关键细节:
import csv from scrapy.exceptions import DropItem class CheckCsvPipeline(object): def __init__(self): # 你的CSV文件路径 self.csv_path = r"C:\Users\HP\PycharmProjects\ToScrape\book\items.csv" # 用集合存储已有标题(集合的成员检查速度远快于列表) self.existing_titles = set() # 读取已有CSV,收集所有标题 try: with open(self.csv_path, 'r', newline='', encoding='utf-8') as csvfile: # 用DictReader按字段名读取,不用关心列顺序 reader = csv.DictReader(csvfile) # 这里假设你的CSV中标题列的字段名是'title',请根据实际情况修改 for row in reader: title = row.get('title', '').strip() if title: self.existing_titles.add(title) except FileNotFoundError: # 如果文件不存在,初始化空集合即可 pass # 打开文件准备写入(用追加模式'a',保留原有内容) self.csvfile = open(self.csv_path, 'a', newline='', encoding='utf-8') # 定义CSV的字段名,要和你的Item类字段完全对应 self.fieldnames = ['title', 'price', 'rating', 'stock'] # 替换成你实际的字段 self.writer = csv.DictWriter(self.csvfile, fieldnames=self.fieldnames) # 如果是新创建的文件(集合为空),写入表头 if not self.existing_titles: self.writer.writeheader() def process_item(self, item, spider): # 获取当前条目的标题,做去空格处理避免因空格导致的误判 item_title = item.get('title', '').strip() if not item_title: # 如果标题为空,直接丢弃条目 raise DropItem("Item has empty title") if item_title not in self.existing_titles: # 标题不存在,写入CSV并更新集合 self.writer.writerow(item) self.existing_titles.add(item_title) spider.logger.info(f"✅ Added new item: {item_title}") else: # 标题已存在,丢弃重复条目 raise DropItem(f"❌ Skipping duplicate item: {item_title}") return item def close_spider(self, spider): # 爬虫结束时关闭文件,确保所有写入操作生效 self.csvfile.close() spider.logger.info("Pipeline closed, CSV file saved.")
关键细节说明
- 用集合存储标题:集合的
in操作是O(1)时间复杂度,比列表的O(n)快得多,爬虫数据量大时优势明显。 - 异常处理:处理文件不存在的情况,避免爬虫启动时报错。
- 编码与换行符:指定
encoding='utf-8'避免中文乱码,newline=''让CSV模块自动处理换行符,兼容不同系统。 - 字段名匹配:
fieldnames必须和你的Scrapy Item类中的字段完全一致,否则写入会丢失数据。 - 日志输出:通过
spider.logger打印操作日志,方便调试和查看去重情况。
启用Pipeline
最后别忘了在settings.py中启用这个Pipeline:
ITEM_PIPELINES = { 'book.pipelines.CheckCsvPipeline': 300, # 优先级数字越小越先执行 }
内容的提问来源于stack exchange,提问作者Land Owner
相关产品推荐
相关产品推荐

