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