三亩地 三亩地SAN MU DI · CODE DIARY
ARTICLE DETAIL

日记详情

真实记录编程学习的某一天,欢迎挑你感兴趣的翻一翻。

如何对新闻数据进行模糊去重

如何对新闻数据进行模糊去重

什么是新闻模糊去重

新闻场景里大量转载稿件:标题加【快讯】【最新】、修改标点、删减导语、微调部分语句,但整篇新闻本质是同一篇。完全字符串相等、MD5是精准去重,处理不了这种改写转载,需要模糊去重

模糊去重目标:内容高度相似、局部修改的新闻识别为重复,不完全依赖字符串完全一致

痛点:

  1. 标题改几个字,MD5直接失效
  2. 正文删改几句话,直接比对文本不相等
  3. 不能把主题相似但完全不同的新闻误判重复

主流工业方案: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向量:

  1. 调用中文向量模型(bge‑small等),每条新闻生成向量
  2. 计算向量余弦相似度,大于阈值判定重复
  3. 海量数据存入向量数据库(FAISS / Milvus / ES向量)

优点:真正语义层面识别;缺点:算力开销大,需要模型,速度慢,适合舆情高级分析。

生产落地实践要点

  1. 用标题还是正文?
  • 短标题:只拿标题做simhash,阈值汉明距离≤3
  • 长新闻:建议取标题+前200字正文摘要生成指纹,不要用全文,全文太长会稀释特征。
  1. 不要丢掉精准去重
    流程:先MD5精准命中完全一样新闻;命中不到,再走SimHash模糊比对。兼顾速度和模糊识别。

  2. 踩坑点

  • 不要不清洗直接丢给simhash,特殊符号会直接改变指纹;
  • 短文本不要把汉明距离阈值调太大,容易误判;
  • 禁止直接双重循环处理上万条新闻,性能爆炸,必须分桶。

数据库存储设计

MySQL存储simhash指纹,方便爬虫入库去重:

CREATETABLEnews(idBIGINTAUTO_INCREMENTPRIMARYKEY,titleVARCHAR(512),contentTEXT,title_md5CHAR(32),simhash_fpBIGINTUNSIGNEDCOMMENT'64位simhash指纹',INDEXidx_md5(title_md5));

业务逻辑:

  1. 新新闻清洗文本,生成title_md5、simhash_fp
  2. 查询md5,命中直接判定重复;
  3. md5无命中,再做simhash分桶比对,判断是否模糊重复。

方案对比总结

方案能力适用场景缺点
SimHash字面模糊,抗转载改写爬虫、舆情新闻,工业首选大幅度语义改写识别弱,大数据需要分桶优化
difflib相似度字面相似度几千条以内小数据集大数据性能差
Embedding向量语义层面模糊去重高级舆情分析需要模型,算力消耗大

绝大多数新闻爬虫业务,清洗 + jieba版SimHash就是性价比最高的模糊去重方案。

← 返回列表