尧图网站建设 尧图网络
  • 首页
  • 关于我们
  • 服务项目
  • 案例展示
  • 建站流程
  • 资讯中心
  • 联系我们
首页/资讯中心/详情

爬虫去重别只会用Set!Python实现亿级数据清洗的4种工业级方案

爬虫去重别只会用Set!Python实现亿级数据清洗的4种工业级方案
📅 发布时间:2026/6/30 23:34:24

做数据采集的同学一定经历过这种绝望:辛辛苦苦爬了一天,入库前跑个去重脚本,结果内存直接爆掉;或者用set()勉强扛住,第二天重启任务又从头开始重复采集;更糟的是,URL明明不同,内容却一模一样,存了一堆冗余数据浪费存储和算力。

很多人把去重简单等同于“判断有没有见过”,但在生产环境中,去重是一个需要兼顾准确性、内存效率、持久化和业务语义的系统工程。set()在万级数据下够用,到了百万、千万级就是灾难。这篇文章不讲基础语法,只分享我在电商商品采集、新闻舆情聚合两个项目中实际落地的去重方案,包含完整的选型逻辑、代码模板和性能实测数据。

合规提醒:本文技术方案仅用于合法授权的数据采集与内部研究。所有案例均已脱敏,严禁用于未授权抓取、隐私数据获取或违反目标站点服务条款的行为。数据采集前请务必完成合规评估。

一、 先搞清楚:你到底要去重什么

动手写代码前,必须先明确去重的对象和粒度。这是很多项目返工的根源。

去重类型典型场景核心挑战推荐方案
URL去重防止同一链接重复请求参数顺序、UTM追踪码干扰规范化+布隆过滤器
内容指纹去重不同URL相同正文(转载/分页)计算开销、近似匹配SimHash/MinHash
业务实体去重同一商品/用户多条记录字段组合、模糊匹配数据库唯一约束+ETL
增量去重定时任务避免重复采集历史数据状态持久化、断点续传Redis Set + 时间窗口

关键认知:没有一种方案能通吃所有场景。URL去重解决“不重复请求”,内容去重解决“不重复存储”,业务去重解决“不重复使用”。三者往往需要组合使用,而不是互相替代。

二、 URL去重:从字符串比较到规范化处理

直接用原始URL做去重是最常见的错误。以下变体本质是同一个资源:

  • https://example.com/product?id=123&source=ad
  • https://example.com/product?source=ad&id=123
  • https://EXAMPLE.COM/Product?ID=123&utm_campaign=spring
2.1 URL规范化三要素
fromurllib.parseimporturlparse,parse_qs,urlencode,urlunparsedefnormalize_url(url:str)->str:parsed=urlparse(url)# 1. 协议+域名小写scheme=parsed.scheme.lower()netloc=parsed.netloc.lower()# 2. 查询参数排序 + 移除追踪参数params=parse_qs(parsed.query,keep_blank_values=True)tracking_keys={'utm_source','utm_medium','utm_campaign','ref','source'}filtered={k:vfork,vinparams.items()ifknotintracking_keys}sorted_query=urlencode(sorted(filtered.items()),doseq=True)# 3. 移除默认端口、尾部斜杠统一path=parsed.path.rstrip('/')or'/'returnurlunparse((scheme,netloc,path,'',sorted_query,''))
2.2 大规模URL去重:布隆过滤器

当URL量级超过百万,set()的内存占用会线性增长(每个URL字符串+哈希表开销)。布隆过滤器用极小的空间代价换取O(1)查询,误判率可控:

frompybloom_liveimportBloomFilter# 容量1000万,误判率0.1%,内存约17MBurl_filter=BloomFilter(capacity=10_000_000,error_rate=0.001)defis_new_url(url:str)->bool:normalized=normalize_url(url)ifnormalizedinurl_filter:returnFalseurl_filter.add(normalized)returnTrue

注意事项:

  • 布隆过滤器不支持删除,适合“只增不改”的采集场景;
  • 误判意味着可能漏采,需根据业务容忍度调整error_rate;
  • 持久化可用url_filter.tofile()保存,重启后加载恢复状态。

三、 内容去重:当URL不同但正文相同

这是最容易被忽视、也最浪费资源的环节。新闻转载、商品多店铺铺货、论坛回帖引用都会产生大量内容重复。

3.1 SimHash:近似去重的性价比之王

SimHash将文本压缩为64位指纹,汉明距离≤3即判定为相似。相比MD5精确匹配,它能识别改写、删减、拼接等轻度变异:

fromsimhashimportSimhashdeftext_fingerprint(text:str)->Simhash:# 预处理:去标点、停用词、转小写cleaned=preprocess(text)returnSimhash(cleaned,f=64)defis_similar(sh1:Simhash,sh2:Simhash,threshold:int=3)->bool:returnsh1.distance(sh2)<=threshold

工程优化点:

  • 只对正文计算指纹:先用trafilatura/DistilBERT提取正文,避免导航栏、广告干扰;
  • 分桶加速检索:将64位指纹按每16位分4桶,同桶内才计算汉明距离,避免全量比对;
  • 阈值动态调整:短文本(<200字)阈值设为2,长文本设为3,减少误判。
3.2 MinHash + LSH:海量文档的亚线性检索

当文档量超过百万,SimHash两两比对仍是O(n²)。MinHash结合局部敏感哈希(LSH)可将检索复杂度降至O(n):

fromdatasketchimportMinHash,MinHashLSH lsh=MinHashLSH(threshold=0.8,num_perm=128)defadd_document(doc_id:str,text:str):mh=MinHash(num_perm=128)forwordintext.split():mh.update(word.encode('utf-8'))lsh.insert(doc_id,mh)deffind_similar(text:str)->list[str]:mh=MinHash(num_perm=128)forwordintext.split():mh.update(word.encode('utf-8'))returnlsh.query(mh)

适用场景:新闻聚合、论文查重、评论去水军。代价是内存高于SimHash,适合对召回率要求高的场景。

四、 持久化与增量:让去重跨会话生效

内存中的去重结构重启即丢失。生产环境必须考虑状态持久化和增量策略。

4.1 Redis:兼顾速度与持久化的首选
importredis r=redis.Redis(host='localhost',port=6379,decode_responses=True)classPersistentDeduplicator:def__init__(self,key_prefix:str,ttl:int=86400*7):self.key_prefix=key_prefix self.ttl=ttl# 7天过期,避免无限膨胀defis_new(self,identifier:str)->bool:key=f"{self.key_prefix}:{identifier}"# SET NX:原子操作,并发安全added=r.set(key,"1",nx=True,ex=self.ttl)returnbool(added)

优势:

  • 支持TTL自动清理历史数据,适配周期性采集;
  • 原子操作避免多线程/分布式环境下的竞态条件;
  • 可横向扩展,支撑亿级去重。
4.2 SQLite:单机轻量级持久化

当数据量<500万且无需分布式时,SQLite比Redis更省心:

importsqlite3 conn=sqlite3.connect("dedup.db")conn.execute("CREATE TABLE IF NOT EXISTS seen (hash TEXT PRIMARY KEY)")defis_new_sqlite(h:str)->bool:try:conn.execute("INSERT INTO seen (hash) VALUES (?)",(h,))conn.commit()returnTrueexceptsqlite3.IntegrityError:returnFalse

注意:写入频繁时开启WAL模式(PRAGMA journal_mode=WAL),避免锁竞争拖慢采集主流程。

五、 性能实测:四种方案横向对比

测试环境:MacBook Pro M2 / Python 3.11 / 100万条URL + 50万篇中文文本

方案内存占用插入QPS查询QPS持久化近似匹配适用规模
set()2.1 GB85万90万❌❌<50万
BloomFilter17 MB120万130万✅文件❌<5000万
SimHash + 分桶380 MB8万12万✅✅<200万文档
Redis SET服务端管理6万8万✅❌无上限
MinHash + LSH1.8 GB2万5万✅✅<500万文档

结论:

  • URL去重优先选布隆过滤器,性价比最高;
  • 内容去重首选SimHash,除非对召回率有极致要求;
  • 需要跨进程/分布式时,Redis是唯一可靠选择;
  • set()仅适用于原型验证和小规模脚本。

六、 避坑清单:这些教训价值百万

  1. 不要在采集主线程做重型去重:SimHash/MinHash计算应异步化或放入独立Worker,避免阻塞请求;
  2. 不要忽略编码一致性:同一文本UTF-8和GBK的指纹完全不同,入库前务必统一编码;
  3. 不要盲目追求零误判:布隆过滤器的误判率与内存成反比,根据业务容忍度权衡,0.1%通常足够;
  4. 不要忘记去重本身的去重:多个采集节点共享去重状态时,确保标识符生成逻辑一致,否则各自为政;
  5. 不要跳过质量校验:去重后的数据仍需Schema验证,避免因指纹碰撞导致有效数据被误删;
  6. 不要永久保留去重状态:设置合理的TTL或归档策略,避免存储无限膨胀拖垮系统。

七、 总结

去重看似是采集链路中最简单的环节,实则是数据质量的第一道防线。它考验的不是算法功底,而是对业务语义的理解、对工程约束的权衡、对异常边界的预判。

从set()到布隆过滤器,从MD5到SimHash,从内存到Redis,每一次升级都不是为了炫技,而是为了让系统在真实世界的混沌中保持稳定、高效、可信。当你不再问“怎么去重”,而是思考“在这个场景下,什么样的去重策略能让数据价值最大化”时,才算真正跨过了这道门槛。

技术终究是为业务服务的。能让下游分析师少花80%时间清洗数据,让存储成本降低60%,让采集任务稳定运行30天无需人工干预——这才是去重真正的价值所在。

相关新闻

  • 验厂时,食品工作服需要注意什么?
  • GoalFlow:四、轨迹评分筛选模块(Trajectory Scorer, M3)
  • 查新报告分为哪几种?科技查新、查收查引与专利查新区别

最新新闻

  • TypeScript接口开发实践
  • X-diagnosis与Prometheus集成:打造可视化系统诊断仪表盘
  • 《MPP/OLAP 数据库实战优化案例:从 1 小时到 2 分钟,SQL 调优 + 存储优化 + 数据倾斜解决》
  • Socket网络编程教程
  • Vue生命周期详解
  • YOLOv11模型导出全攻略:自定义算子支持与不兼容算子处理实战指南

日新闻

  • 2026年6月公司网站搭建最新热门渠道测评:四大低成本/零代码平台对比+避坑
  • 【Linux】Linux arm 编译QT程序,出现expected “}“报错
  • 【MATLAB例程】四基站二维AOA定位与距离辅助增强对比仿真。基于角度观测和测距修正的固定目标平面定位精度分析

周新闻

  • Windows字体自定义终极方案:No!! MeiryoUI完全指南
  • Deepin Boot Maker:告别命令行,3分钟制作Linux启动盘的智能解决方案
  • Plain Craft Launcher 2:重新定义你的Minecraft游戏体验

月新闻

  • 2026年6月公司网站搭建最新热门渠道测评:四大低成本/零代码平台对比+避坑
  • 【Linux】Linux arm 编译QT程序,出现expected “}“报错
  • 【MATLAB例程】四基站二维AOA定位与距离辅助增强对比仿真。基于角度观测和测距修正的固定目标平面定位精度分析

关于尧图

  • 公司简介
  • 团队介绍
  • 企业文化
  • 荣誉资质

服务项目

  • 定制开发
  • 电商建站
  • UI 设计
  • 运维服务

快速链接

  • 案例展示
  • 建站流程
  • 常见问题
  • 资讯中心

联系方式

  • 📍北京市朝阳区互联网产业园 A 座 10 层
  • 📞400-888-8888
  • ✉️contact@rkmt.cn
  • 🕐周一至周日 9:00-21:00

© 2024 北京尧图网络科技有限公司 版权所有 | 京 ICP 备 XXXXXXXX 号