什么是新闻模糊去重
新闻场景里大量转载稿件:标题加【快讯】【最新】、修改标点、删减导语、微调部分语句,但整篇新闻本质是同一篇。完全字符串相等、MD5是精准去重,处理不了这种改写转载,需要模糊去重。
模糊去重目标:内容高度相似、局部修改的新闻识别为重复,不完全依赖字符串完全一致。
痛点:
- 标题改几个字,MD5直接失效
- 正文删改几句话,直接比对文本不相等
- 不能把主题相似但完全不同的新闻误判重复
主流工业方案:SimHash(局部敏感哈希)是新闻/舆情领域模糊去重首选;备选:文本相似度算法、向量语义去重。
前置:文本清洗(模糊去重必须做)
网页新闻自带HTML标签、换行、空格、特殊符号【】《》、广告尾注,这些噪声会直接毁掉指纹计算,所有模糊去重前先清洗。
importreimportjiebadefclean_news(text:str)->str:ifnottext:return""# 去除html标签text=re.sub(r"<.*?>","",text)# 去除特殊符号、换行、制表符text=re.sub(r"[【】《》『』「」#@\n\r\t]","",text)# 压缩全部空白字符text=re.sub(r"\s+","",text)returntext.strip()方案一:SimHash 局部敏感哈希(新闻最常用)
核心原理:相似文本生成的指纹,汉明距离很小;文本差异越大,汉明距离越大。
相同含义,少量文字改动,指纹依然接近;完全不同文本指纹差异巨大。
完整可运行代码:
importhashlibimportjiebadefget_md5_hash(s:str):returnint(hashlib.md5(s.encode("utf-8")).hexdigest(),16)defsimhash(text:str,bit=64)->int:""" jieba分词版simhash,适合中文新闻 :param text:清洗之后的文本(标题/正文摘要) :param bit:指纹位数,默认64位 :return:simhash指纹整数 """words=jieba.lcut(text)v=[0]*bitforwordinwords:h=get_md5_hash(word)foriinrange(bit):ifh>>i&1:v[i]+=1else:v[i]-=1fingerprint=0foriinrange(bit):ifv[i]>0:fingerprint|=1<<ireturnfingerprintdefhamming_distance(fp1:int,fp2:int)->int:"""计算两个指纹汉明距离,数值越小越相似"""returnbin(fp1^fp2).count("1")业务阈值(新闻项目实测经验)
汉明距离 = 两个指纹二进制位不一样的数量
- ≤2:几乎完全相同,判定重复
- 3‑4:转载改写新闻,业务直接判定重复
- ≥5:判定为不同新闻
# 测试案例:转载新闻news_a=clean_news("央行宣布下调存款准备金率0.5个百分点")news_b=clean_news("【快讯】央行宣布下调存款准备金率0.5个百分点!")fp_a=simhash(news_a)fp_b=simhash(news_b)dist=hamming_distance(fp_a,fp_b)print(f"汉明距离:{dist}")ifdist<=4:print("✅判定为重复新闻")内存中批量模糊去重示例
⚠️注意:直接双重循环 O(n²),只适合小数据集(几千条以内)
news_data=[{"title":"央行宣布下调存款准备金率0.5个百分点","content":"xxx"},{"title":"【快讯】央行宣布下调存款准备金率0.5个百分点!","content":"xxx"},{"title":"油价迎来年内第七次上调","content":"yyy"}]distinct=[]forcurrinnews_data:t=clean_news(curr["title"])curr_fp=simhash(t)is_dup=Falseforexistindistinct:d=hamming_distance(curr_fp,exist["fp"])ifd<=4:is_dup=Truebreakifnotis_dup:distinct.append({"data":curr,"fp":curr_fp})print(f"原始{len(news_data)},模糊去重后{len(distinct)}")海量数据问题说明
上面直接双重循环,1万条新闻就要1亿次比对,性能爆炸。
百万新闻生产环境不能直接两两比对,解决方案:SimHash分桶。
把64位指纹切分成4段,每段16bit;同一段指纹相同才放入同一个桶。只比对同一个桶内的指纹,大幅减少比对次数。
方案二:difflib 序列相似度(小数据备选)
不需要第三方库,直接计算文本相似度比值,适合几千条以内小规模数据。
fromdifflibimportSequenceMatcherdeftext_similarity(a:str,b:str)->float:returnSequenceMatcher(None,a,b).ratio()t1=clean_news("央行宣布下调存款准备金率0.5个百分点")t2=clean_news("【快讯】央行宣布下调存款准备金率0.5个百分点!")score=text_similarity(t1,t2)print(f"相似度:{score:.2f}")# 业务阈值:大于0.85视为重复ifscore>=0.85:print("判定重复")缺点:数据量大O(n²)很慢;长文本改动局部内容,分数会暴跌,不如SimHash稳定。
方案三:向量语义模糊去重(大模型路线)
SimHash是字面层面,改写幅度很大,换表达方式但是语义相同的新闻,SimHash会失效。
这时使用Embedding向量:
- 调用中文向量模型(bge‑small等),每条新闻生成向量
- 计算向量余弦相似度,大于阈值判定重复
- 海量数据存入向量数据库(FAISS / Milvus / ES向量)
优点:真正语义层面识别;缺点:算力开销大,需要模型,速度慢,适合舆情高级分析。
生产落地实践要点
- 用标题还是正文?
- 短标题:只拿标题做simhash,阈值汉明距离≤3
- 长新闻:建议取标题+前200字正文摘要生成指纹,不要用全文,全文太长会稀释特征。
不要丢掉精准去重
流程:先MD5精准命中完全一样新闻;命中不到,再走SimHash模糊比对。兼顾速度和模糊识别。踩坑点
- 不要不清洗直接丢给simhash,特殊符号会直接改变指纹;
- 短文本不要把汉明距离阈值调太大,容易误判;
- 禁止直接双重循环处理上万条新闻,性能爆炸,必须分桶。
数据库存储设计
MySQL存储simhash指纹,方便爬虫入库去重:
CREATETABLEnews(idBIGINTAUTO_INCREMENTPRIMARYKEY,titleVARCHAR(512),contentTEXT,title_md5CHAR(32),simhash_fpBIGINTUNSIGNEDCOMMENT'64位simhash指纹',INDEXidx_md5(title_md5));业务逻辑:
- 新新闻清洗文本,生成title_md5、simhash_fp
- 查询md5,命中直接判定重复;
- md5无命中,再做simhash分桶比对,判断是否模糊重复。
方案对比总结
| 方案 | 能力 | 适用场景 | 缺点 |
|---|---|---|---|
| SimHash | 字面模糊,抗转载改写 | 爬虫、舆情新闻,工业首选 | 大幅度语义改写识别弱,大数据需要分桶优化 |
| difflib相似度 | 字面相似度 | 几千条以内小数据集 | 大数据性能差 |
| Embedding向量 | 语义层面模糊去重 | 高级舆情分析 | 需要模型,算力消耗大 |
绝大多数新闻爬虫业务,清洗 + jieba版SimHash就是性价比最高的模糊去重方案。