豆备 DOUBAK
← 开发日志

同一份档案放了两处,出处就多了九千条

一个下载文件夹里同一份档案躺了两遍,逐字节相同。产出的记录一条不差——错的是每条记录的出处,2933 条标记声称同一次抓取看见过它两次。记录对、出处假,而下游没有一处会读到它。

一个普通的下载文件夹里,同一份档案躺在两个地方:顶层一份, 20260806/ 下面一份,逐字节相同。 解压过两遍,或者整理之前先备份了一下——总之是人会做的事。

解析器把它读成了两份。而产出的记录,一条不多一条不少:

有那份重复没有
标记2964 条(修订 3117)一模一样
广播3423 条(修订 3898)一模一样
作品 / 长文 / 豆列2964 / 5 / 6一模一样
档案份数27 份26 份
观测次数4764641327
起点7 个(3eef52 列了两次)6 个

记录是对的,因为合并本来就是并集——同一条捕获读两遍,第二遍什么都没添上。 错的是出处

出处是这份档案唯一的凭据

canonical 里每一条记录都带着一串 capture_ids,指回 WARC 里 具体某一条捕获。整个项目的说法——「这不是我编的,你自己去看那一页」—— 全押在这串东西上。

而多出来的那一份,让它开始说假话:

标记   2933 条修订里,同一个 bundle 记了两次「我看见了它」
作品   2933 条
广播   3188 条

它只看见过一次。多出来的那一次,是一个文件同时躺在两个文件夹里造出来的。

没有任何一处会报错,因为记录数是对的、修订数是对的、 页面是对的、导出是对的。只有去数出处才看得见。

为什么不干脆让人自己收拾干净

因为这条路走过。解析器早就不再要求把档案摊平: 它会钻进子目录,一个目录里混着几份索引也逐份读出来。理由每次都一样—— 要求人先手工整理,换来的只会是漏掉一份,而漏掉一份不会报错, 产出照样是一份看着完整的档案。

而「钻进子目录」这件事本身,让复制一个文件夹更容易变成两份档案, 不是更少。所以修法不是拒绝,是认出来。

判据:索引是不是前缀

抓取过程中索引只往后追加,导出只是把文件拷走。所以「早一次导出」的索引 一定是「晚一次导出」的前缀——逐字节相同只是它的特例。 这条性质是免费的,也足够严:

  • 是前缀:同一份档案的一份拷贝。留捕获多的那个, 并把略过了哪个目录打印出来。
  • 不是前缀:两个不同的东西顶着同一个编号。 两份都读,并报警告。

第二条要紧。丢掉其中一个才是不安全的方向——那会静默丢数据; 而两份都读的全部代价,只是重复的出处又回来了,而且这回它被说出来了。 方向不对称,处置就不该一样。

顺带说一句它为什么不影响你已经有的东西

样张站、导出到 NeoDB 的包、Markdown 树——一个字节都没变, 因为下游没有一处读 capture_ids。这不是「所以无所谓」, 恰恰相反:一个只有去数才看得见、而且下游全都不看的错误, 正是这个项目最怕的那一种。

修完之后拿真实档案跑了一遍,产出与「手工把那份重复删掉再跑」逐字节 相同——五个文件,五个哈希,一个不差。

← 更新:全绿,只是没在查 更早:重标一次,2018 年那条就没了 →