放错目录的四份档案
四份 7 月 31 日的档案散在下载目录根下,一直没被喂进解析器。并进去之后多出 43 个豆瓣改过名的作品——也让两条绿了几周的断言变红,而两条都是断言错了。
重新生成一遍站点之前先重新解析。而在数档案份数的时候发现:喂进去的那个目录里 有 13 份,另有 4 份 7 月 31 日的档案散在下载目录根下, 从来没进过。
它们是不是重复的?把两种都跑一遍对比,答案很干脆: 标记一条不多(2945 → 2950 那 5 条来自当天的新抓取), 广播条数一条不变,广播的修订却从 3411 涨到 3480。
69 处差异,全部在同一个字段上
广播是发出去就不能编辑的——这是整个项目最看重的性质之一: 每条广播都是一个带日期的、冻住的快照。那多出来的 69 条修订是什么?
逐条比对之后,答案漂亮得有点意外:69 组差异,全部只差
target_title 一个字段,一条都没碰正文、评分、发布时间。
勇敢者游戏3:开放世界 → 勇敢者的游戏3
F1:狂飙飞车 → F1
小丑2:双重妄想 → 小丑2
狮子王:木法沙传奇 → 狮子王前传:木法沙
败犬女主太多了! → 败北女角太多了!
43 个作品,豆瓣在这不到一个月里改了它们的名字。多半是片子上映前后 官方中文名定下来了。
而这正是这个项目存在的理由的一个缩影。 广播的正文是发布那一刻冻住的, 但挂在它下面那张作品卡不是——豆瓣是在你打开页面的那一刻现渲染它的。 所以 7 月抓到的那一份,是「那天它叫什么」唯一的记录。 豆瓣自己不保留这个历史。
顺带查了一件相关的事:那 7 个已经被豆瓣删掉的游戏, 7 月的档案里也没有名字。首页那句「名字只有在条目还在的时候 抓过才留得下」仍然成立。
然后两条测试红了,而两条都是测试错了
并档案之后跑测试,两条绿了几周的断言变红。查下来数据都是对的, 错的是断言——而它们错的方向恰好相反,放在一起看很有意思。
一条太强
广播不可编辑 —— 修订数恒等于记录数。前提听着无懈可击:
广播不能编辑,所以同一条广播的每次观测都该一模一样。
但那个前提只对一半。 不能编辑的是用户写的那部分; 记录里还有豆瓣的作品名,而它会变——就是上面那 43 个。
一比一那个写法,会把豆瓣自己的改名当成抽取器故障来报, 而那恰恰是这份档案最想留住的东西。 改成逐字段点名:正文、评分、发布时间、状态……一个都不许变; 允许变的只有豆瓣的作品名。这比原来那条更严,也更说得清。
一条是会误伤的代理
每一次修订都必须推进状态(想看 → 在看 → 看过)。
它想抓的是假修订:以前真出过两次,短评旁边的「(N 有用)」
从 5 变成 1、日记页脚的「1740人浏览」每抓一次涨一点——
豆瓣的计数器漏进了用户的字里,于是凭空多出一条「用户改了东西」。
那两种都不会让状态动一下,所以「状态必须推进」抓得住它们。
这次红的是一条 在看 → 在看。查出来是用户自己按豆瓣的习惯,
在原短评前面接了一句:
怎么京阿尼在这也能埋上音乐番的影子! //看完了两集,真的是奇幻…
在看的时候改一句短评,是再正常不过的事。 那条断言把一件正常的事判成了故障。
换成三条各自说得清的:不许有空修订;每条修订至少要变一个用户自己写的字段 (这样只有豆瓣目录数据变了,造不出一条假修订);状态不许倒退。 至于计数器那一类,直接照它的形状挡——更窄,而且不会误伤。
代理断言会继承代理和本体之间的每一处差别
「状态推进了」本来是替「用户真的改了东西」站岗。这两件事绝大多数时候一致, 等它们分开那天,测试反过来指控用户有 bug。
还有一处更难堪的:那条断言的注释先写了一整段
「判据不是有几条——档案会长,钉死一个会自然变化的数,测的是档案有没有变,
不是解析器对不对」,然后下一行就是 assert.equal(multi.length, 7)。
一条把理由写清楚、然后照旧钉死数字的测试,比没有注释更糟——
它让下一个人以为这个数被想过。
它们不是被读出来的,是被喂出来的
这两条断言没有一条是靠 review 发现的。它们绿了好几周, 因为那几周里喂给它们的数据恰好不包含反例。
让它们现形的不是更仔细地读代码,是四份放错了目录的档案。
同一天还删掉了一个不该删的文件:sitemap.xml 是样张仓库那边的 CI
生成的,部署脚本把它当上一次的残留清掉了。理由当时看着成立(「那边会重新生成」),
但那条链路要等人合并一个 PR,中间有一段真空。
规矩改成:清残留只能清自己上一次铺下去的东西——
别的流程放进仓库的产物,这里既不知道它怎么来的,也不知道它多久能回来。