Scrapy去重Pipeline未过滤重复SKU,是否需改用数据库存储?
问题分析与解决方案
为啥类内集合会失效
你单产品调试时没问题,但全量运行就出重复,核心原因是Scrapy多进程/多线程环境下的内存隔离:
当爬虫启用多进程(比如高并发请求、分布式部署)时,每个进程会单独实例化DuplicateSkuPipeline,每个进程里的self.skus集合是独立的,进程之间没法共享数据。比如进程A抓了SKU123,进程B根本看不到这个记录,再抓一次就会绕过去重逻辑,最终出现重复数据。
解决方案:用共享存储替代类内集合
是的,把SKU存到共享存储系统(比如Redis、MySQL)是解决跨进程/分布式去重的正确思路。其中Redis是最优选择——它的集合(Set)结构天生适合高效去重,性能比数据库查询快得多。
Redis版去重Pipeline示例
import redis from scrapy.exceptions import DropItem from itemadapter import ItemAdapter class DuplicateSkuPipeline: def __init__(self): # 按你的Redis配置修改参数 self.redis_conn = redis.Redis(host='localhost', port=6379, db=0, decode_responses=True) # 定义存储已抓取SKU的集合key self.sku_set_key = "scraped_product_skus" def process_item(self, item, spider): adapter = ItemAdapter(item) sku = adapter.get("sku") # 先标准化SKU,避免大小写、空格导致的误判 if sku: sku = sku.strip().upper() else: spider.logger.warning("商品无SKU,跳过重复检查") return item # 检查SKU是否已存在 if self.redis_conn.sismember(self.sku_set_key, sku): raise DropItem(f"发现重复SKU: {sku}") else: self.redis_conn.sadd(self.sku_set_key, sku) return item def close_spider(self, spider): self.redis_conn.close() total_unique = self.redis_conn.scard(self.sku_set_key) spider.logger.info(f"共抓取到 {total_unique} 个唯一SKU")
额外注意事项
- SKU标准化是必须的:一定要对SKU做清洗(去空格、统一大小写),不然
"SKU123"和"sku123 "会被当成不同的SKU,导致漏判重复。 - 持久化需求适配:如果需要下次爬取时直接跳过已抓过的SKU,开启Redis的RDB/AOF持久化即可;如果要和业务数据库联动,也可以用MySQL的唯一索引实现(但性能不如Redis)。
- 分布式场景兼容:如果用Scrapy-Redis做分布式爬虫,这个Redis版Pipeline可以直接用,所有爬虫节点共享同一个SKU集合。
后续排查点
如果换了共享存储还是有重复,建议检查:
- 抓取过程中SKU字段是否被篡改,比如不同页面的SKU格式不一致;
- Pipeline优先级是否正确(你的300优先级没问题,要确保去重在入库/下载图片前执行);
- 是否存在爬虫中途重启的情况(共享存储不会因为进程重启丢失记录,这个可以排除)。
内容的提问来源于stack exchange,提问作者Matthew G
相关产品推荐
相关产品推荐

