一堆档案不等于一条链
增量抓取上线,以及一个把四次独立全量误接成一条链、把完整性说成「已验证」的 bug。
第一次抓取是全量,之后每次都全量就太贵了——几千个作品详情页,每次重抓一遍毫无意义, 对豆瓣也不礼貌。所以要有增量。增量看起来只是「记住上次抓到哪儿,这次从那儿开始」, 实际上它把一个新概念带进了整个系统:链。
下界是一个值,不是一个引用
豆瓣的每一个列表都是头插的:新东西加在最前面。所以抓取方向是新 → 旧, 这样插入只会把条目往还没读到的页推,产生重复(免费修掉)而不是静默漏掉 (检测不到,而且永久)。
增量要做的就是沿着这个方向走到「上次已经抓过的地方」为止。 这个下界必须是一个时间值,不能是一个条目 ID——任何一条广播都可能被删掉, 用 ID 当锚点,锚点会凭空消失。同理,跨会话保存的页码一律不可信, 每次都要靠内容重新定位。
规范里为此加了 crawl_state.floor_from_bundle_id:
这一份档案的下界是从哪一份档案读来的。有了它,多份档案才连得成一条链。
基准必须是同一个账号
增量的前提是「上一份档案抓的是同一个人的东西」。如果中途换过账号, 接上去的东西完全没有意义。所以规范要求基准档案的账号和用户名都必须对得上, 对不上就不给下界,从头重走一遍全量。
同样,有缺口的路线不提供下界。缺口意味着「这条路线上有一段没抓到」, 在它上面接一段增量,等于把缺口永久封在中间——上面是新的,下面是旧的, 缺的那一段以后再也没有理由回去补。
然后是那个 bug
覆盖率页有一个「链视图」,把多份档案接起来看整体完整性。它上线之后我拿四份真实档案试, 显示得很漂亮:一条完整的链,连续性 ✔ 已验证。
问题是那四份档案是四次互相独立的全量抓取,没有任何一份是从另一份接下来的。 链视图按时间排了个序就把它们串起来了,然后基于这条并不存在的链, 宣布完整性「已验证」。
这是最坏的一类 bug:它不报错,它给你一个更强的结论。用户看到「✔ 已验证」,
就不会再去检查了。修法是链关系只认 floor_from_bundle_id,
接不上就是四条独立的链,各自显示各自的覆盖率。
同一天还修了几个同族的:不连续的路线在抓完的档案里显示成「进行中」 (应该是「未验证」);单页路线永远走不完,于是每份档案都凭空带 6 处假缺口; 「连续性」那一列写的是我们对自己的信心而不是结论。 界面上任何一句比事实更强的话,都要当 bug 修。
只加了一本想读的书,却在抓游戏
最后一个值得记的:增量跑起来之后,我只在豆瓣上加了一本想读的书, 然后触发增量——它开始抓游戏的作品详情页。
原因是作品详情页是从标记列表派生的,而派生的时候没有区分 「这次增量真的新看到的条目」和「基准档案里已经有的条目」。 增量的语义得贯穿到派生出来的活上,不然省下来的那点请求又全花回去了。