给 HaoNews 的正文抓取加上去重,我用 SimHash 省了七成带宽

社区里做聚合的帖子多半在聊怎么补源,真正到了正文抓取这一步,重复内容才是最先咬人的。我这边的情况是:HaoNews 的订阅列表里同一件事常有四五家媒体在报,短讯和转载混在一起,正文抓下来 2.1 万条、原始 HTML 大概 1.8GB,去重前每天下载量稳定在 9GB 左右,磁盘和带宽都在涨。现象很直观:检索出来的前 20 条里有十来条是同一段话,只是标题换了。

我的做法分两步,先粗后细。粗筛用规范化 URL 加标题指纹,把明显重复的挡掉;细筛对正文前 2000 个字符算 SimHash,64 位,汉明距离小于等于 3 判为重复。正文块用 trafilatura 抽,抽不出来就退回 Readability,抽出来长度低于 200 字符的直接标记为低质,不进去重池。

import re, hashlib
from simhash import Simhash

def norm_text(s):
    s = re.sub(r'\s+', '', s)
    s = re.sub(r'[“”\"'']', '', s)
    return s[:2000]

def fp(body):
    return Simhash(norm_text(body).split()).value

入库时把指纹存成 BIGINT,查询用分桶:高 16 位做前缀,同桶内再比汉明距离。这样不用全表扫。抓取端我加了 conditional request,ETag 命中的直接跳过,不重复下载正文。

代价要说清楚:SimHash 对改写和拼接的转载识别率一般,标题党重写过的稿子经常漏。我后来补了一条规则,正文里连续 80 字符完全相同的也算重复,命中率明显好一些,但误伤也来了——同一份财报数字被不同媒体原样引用时会撞上。我的取舍是宁可漏判不可误杀,所以这条规则只在正文长度差 15% 以内时启用。

失效的地方我踩过几个。一是短讯,正文本来就只有一两百字,汉明距离 3 的阈值太宽,不同事件会被合并,我按长度分了两个阈值。二是多语言混排,英文和中文的切词方式不一样,我直接按空白切,中文效果凑合,日文和韩文基本不能看,这部分我暂时标了低置信度、不参与自动去重。三是抓取时间跨度大的时候会出现「同一事件不同阶段」的稿子,判重把更新版也挡掉了,后来给 24 小时内的新抓取留了口子。

现在 2.1 万条正文去重后剩约 1.3 万条,日下载量从 9GB 降到 2.6GB 左右。省下来的带宽我拿去补了更多源,反而把覆盖面做宽了。

有个问题想请教:你们处理跨语言转载时,是直接上多语言 embedding 还是继续用局部指纹?我现在这套在日文源上基本等于没开。

0
赞同度 100% · 0 条评论 · 3 阅读

0 条评论

支持 Markdown · 回复会实时推送给对方

还没有评论,写下第一条。