当前位置: 首页 > news >正文

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

做数据采集的同学一定经历过这种绝望:辛辛苦苦爬了一天,入库前跑个去重脚本,结果内存直接爆掉;或者用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天无需人工干预——这才是去重真正的价值所在

http://www.jsqmd.com/news/1098952/

相关文章:

  • 【VMware OVF导出终极指南】:20年资深架构师亲授5大避坑要点与3种加速导出实战技巧
  • 【数字孪生国标落地第一个月,我给新能源行业测了测段位】
  • 主流开源LLM(Qwen、ChatGLM等)的本地化部署
  • 验厂时,食品工作服需要注意什么?
  • GoalFlow:四、轨迹评分筛选模块(Trajectory Scorer, M3)
  • ps怎么调整图片大小?ps调整图片大小快捷键
  • 虚拟摇杆vJoy:Windows游戏控制器模拟的技术深度解析
  • 查新报告分为哪几种?科技查新、查收查引与专利查新区别
  • 基于 VC++ 与机器人 SDK 的工业多轴示教器软件设计与实现
  • 驾驶行为识别 打电话识别数据集 驾驶注意力监控 驾驶分心识别数据集 危险驾驶行为检测 抽烟打电话 睡觉 吃东西识别图像数据集第10149期
  • Metasploit渗透测试实战:从漏洞利用到后渗透操作详解
  • 车的使用年限,从来不是出厂定的!
  • OpenClaw排坑实录:启动失败、技能失效、模型报错,30个高频问题一次讲透
  • 解决方案|腾讯安全天御金融反电诈产品解决方案
  • 【LeetCode Hot100】189.轮转数组-三种解法以及效果评估
  • 搞定99%安装问题!OpenClaw 完整部署与故障修复
  • G-Helper终极指南:重新定义华硕笔记本硬件控制的轻量级革命
  • 模块 包 循环导入 系统
  • 3PEAK思瑞浦 TPA133A2-VS1R-S MSOP8 电流信号检测放大器
  • AI4C编译调优的终极技巧:提升程序性能30%的秘密
  • NCM音乐格式转换终极指南:5分钟解锁你的音乐收藏
  • Mineradio开源音乐播放器下载安装介绍(附下载链接)
  • 为什么好人没好报?你可能误解了“诚信”
  • 【会议征稿通知 | 西藏大学主办 | JPCS出版 | EI 、Scopus稳定检索】2026年水电系统与能源工程学术会议 (HSEE 2026)
  • 智慧矿场施工状态监测 推土机识别 装载机数据集 挖掘机等工程机械数据集第10096期
  • 实测有效|OpenClaw 闪退卡顿、网关异常根治教程
  • lac_agent自愈链路上篇——crontab守护的那些坑与健康检查实战
  • 计算机毕业设计之基于实训室管理平台的设计与实现
  • 逻辑回归(Logistic Regression)培训课件
  • .NET 8加持:C#上位机调用国产PLC运动控制指令实战