开发日志
记录开发历程、关键技术决策以及踩过的坑。 具体代码改动可参阅 Git 提交历史。
-
同一张海报,换个服务器地址,档案里就多一条「改过」
111 条作品修订是凭空的,两版之间只差一个字符。而去修它之前先重量了当初记下的两个数字——两个都是错的,其中一个还把问题的性质说反了。
-
桌面浏览器放行了规范禁止的东西,于是那条路看起来是通的
安卓 Edge 上一按开始就抛出一句规范原文。是我上一版引入的:两条分支都用了动态加载,而只有一条需要——桌面 Chrome 开了口子,所以本机全绿、CI 全绿、真浏览器里跑得好好的。
-
Firefox 能抓、能存,就是交不出一个文件夹
「Firefox 的存储接口不太一样,不好移植」这句判断对了一半,而且指的是错的那一半:存储那一格原样可用,卡住的是把字节交到磁盘上那一步。
-
他不是没登录,是豆瓣把他送去了手机版
安卓 Edge 上一按开始就是「无法判断登录状态」。拒绝本身是对的——错的是那句话把人指向了一条修不好的路:他会一遍遍重新登录,去修一个不存在的登录问题。
-
一个复选框,三处静默
它本该默认藏着,却从第一天起就露在外面——三次检查都通过了,三次问的都是「代码里写了吗」,没有一次问「用户看到的是什么」。最后是一张顺手贴来的截图破的案。
-
他花了一个小时锁日记
一条长毛象抱怨:日记导进 NeoDB 之后全公开了。其实一直能改——只是那个办法要把 2961 条标记连坐,贵到没人会付。
-
一句注释拦不住漂移
同一份档案,命令行导出 2966 条标记,扩展导出 2967 条——合并逻辑写在了「读文件」那一侧,而扩展读的是浏览器里的存储。那个函数的注释写着「照抄它的定义,不要另发明」,而漂的是上游。
-
豆瓣把它锁了,不是你把它藏了
一篇日记显示为「仅自己可见」,豆瓣在页面中明确标注了违规理由。归档虽保留了正文,却漏掉了「内容被平台封禁」这一关键事实——而初版方案差点让归档在本地也替平台顺水推舟地进行隐匿。
-
那七个字不是最长的动作词,是「在第一个链接前刹住」
广播动作的七字上限看着像量过最长的那个词,实际是在第一个链接前停下——而链接里装的正是这句话的宾语。放宽到 18 能接住漏掉的 33 条,代价是给其中 11 条写进垃圾。
-
出处这枚角标,改了三版
整行时间戳做成链接太重;缩成一个箭头又白占掉一整行,每条广播 35 px。第三版才落到时间戳右端——而第四版是因为漏了首页:那 3493 枚量过的角标,全在广播页里。
-
全绿,只是没在查
四个检查都写了,也都写得不错,没有一个在该响的地方响:一个长在别的仓库里,一个长在已经不存在的路径后面,一个长在永远有常驻项的清单上,还有一个长在最后那句话前面。
-
同一份档案放了两处,出处就多了九千条
下载文件夹里同一份档案躺了两遍,逐字节相同。产出的记录一条不差——错的是出处:2933 条标记声称同一次抓取看见过它两次。
-
重标一次,2018 年那条就没了
把一部电影删掉重标,站点上 2018 年的短评、标签、那条广播的链接和封面一起消失。而 git 里零新增、零删除——这种消失是看不见的。
-
v1.3.2,以及为什么格式版本跳到了 1.4
两组修复正式发布,并说明
bundle/1.4格式调整——仅增补了一个可选字段。文末附有版本兼容矩阵,明确指出具体受限的组合情况。 -
档案是全的,声明是错的
抓取报「有 1 处缺口」,点了重试,抓完还是「有 1 处缺口」——而那张图明明就在档案里,542163 字节。缺口只增不减,唯独这一种缺口能被自己证伪。
-
响应头到了,不等于这次请求完了
一次抓取卡了 8494 秒。超时只盖住了半次往返——而它能卡这么久,是因为另外三道保护各自堵住了自救的一段。
-
名字里一个关键词都没有
商店标题里一个关键词都没有,而这一格改不了后台——它取自 manifest.json。顺带发现自家英文 README 把两组不同的「8」并成了一句。
-
页脚写着「零外部请求」,而那一版有 15 页在联网
接上老档案重新生成,样张站上 15 个页面把豆瓣的图片地址印了出来。规矩在同一份代码里写过四遍,一条测试正钉着这个 bug,而告警说了实情却没说后果。
-
一个目录里十份档案,只读了一份
扫描器取了第一个匹配的清单,剩下九份、573 条捕获无声消失——而校验器只验了那一份,然后说「通过」。同一天还有个形状一模一样的:
unknown不是账号编号。 -
校验器一直都在,只是没人跑
校验器功能完备且运行仅需数秒,但因 24 份真实档案中有 4 份持续报错,导致此前始终未被纳入常规流程。动手修复前通过实测比对,排除了两条貌似合理但并不成立的先验推测。
-
接上 2022 年的老档案:多的不是记录,是二十个月的编辑史
前代命令行工具 2022–2024 存下 782 MB 页面。转成档案接进来之后,记录只多了 7 条——而「改过不止一次」的标记从 8 条变成 147 条。
-
把下游搬进扩展,靠的是抄而不是重写
解析、导出、建站现在扩展里点几下就能做完。做的时候差点重写一遍图片判定——三处全错,三处都不报错。
-
把代码删光之后,证据反而更硬了
上游说「这个 PR 只做合并」,那个子类就没有存在的理由了。删到一行生产代码都不剩,而它的证据比之前更强。
-
自己跟自己对账,永远是平的
整份档案 17305 条真导进了 NeoDB。本地测试全绿,导进去第一眼就看见一个把同一件事写成两行的 bug——因为对账的两边都是自己写的。
-
广播里存着一段豆瓣自己都不留的历史
NeoDB 的维护者问能不能出 NDJSON。换过去之后多带走的不只是豆列——3411 条广播拼出一条豆瓣自己已经不显示的状态时间线。
-
真导一次才知道
NeoDB 导出真的导了一次。读源码读对的那一处得到了唯一的实证,而一条注定失败的记录暴露出一个不该有的默认行为。
-
放错目录的四份档案
四份档案一直没被喂进解析器。并进去多出 43 个豆瓣改过名的作品,也让两条绿了几周的断言变红——两条都是断言错了。
-
走的时候不用求谁
导出到 NeoDB、Letterboxd、Goodreads 做完了。原先写着「要先和 NeoDB 维护者谈」——那是把 API 当成唯一入口时的判断。
-
抓完之后,先让你看见自己的东西
扩展 1.2.0 档案面板新增「查看内容」功能。核心提取逻辑直接复用解析器模块而非重复实现——编写 220 行胶水适配代码的意义在于保证业务逻辑仅有一处真实源头。
-
验一验:能不能读将来的档案
规范给读者立过两条义务,而在此之前没有任何东西验证过它们。以及过程中两次写出了恒真的断言。
-
私密的那一份,目录页上也得带锁
豆列渲染进站点。以及一份名单抄在三个入口里,加东西时漏改两处——不报错,只是少了几十页。
-
豆列:值钱的不是清单,是你写在每条上的那句话
扩展 1.1.0 开始抓豆列。以及一份豆列被别的豆列翻到头给停掉了——一个真的静默截断路径。
-
上架当晚,别人替我逮到了两个 bug
三处全都需要一个和我不一样的账号、或者一次和我不一样的操作。装的人多一个,样本就多一个。
-
1.0.0,以及上架
装法从「clone 整个仓库」变成点一下。以及四条在浏览器里从来就没成立过的测试,是 Node 一直宽松才让它们绿着。
-
「导出之后可以放心删掉」——唯有支持导入恢复,这项承诺才算成立
导入功能正式实现。为了使其成为第二条合法的归档写入路径,我们重新审视了「只允许单一写入路径」这一架构原则所守护的核心边界。
-
打了分的广播,正文一律没抓到
2200 条只抽到 789 条,漏掉的 1411 条每一条都带评分。以及广播里那颗星:一份豆瓣自己都不保留的评分变化史。
-
又名是搜索里最值钱的一栏
记得《重返沉默之丘》而不记得《寂静岭2》的人,没有又名就什么都搜不到。而按裸斜杠切开会存下 176 个不存在的片名。
-
两个整整齐齐的零
音乐 84 条标记 0 条短评,舞台剧 5 条也是 0 条。一句告警都没有——因为「这条本来就没写短评」是合法的。
-
一按「开始抓取」就报错:那个 API 在这里根本不存在
档案里写着「这是哪份代码抓的」。那个数字曾经有三份互不认识的副本,全都写死成 0.0.1。
-
双击 index.html,那就是你的豆瓣
站点生成器做完了,sample.doubak.com 上线。以及一个把用户自己写的字悄悄吃掉的 bug。
-
解析器能跑了,以及两个只有换种跑法才会露出来的 bug
豆瓣改演员表被记成「你编辑了这条标记」,页脚的浏览计数变成了编辑历史——两个都不报错,产出看起来完全正常。
-
照片永远不会在增量抓取里出现
增量只看水位线以上,而照片是从广播页面派生的。补抓那一趟:重读 176 个已存页面,抓下 121 张图,零个页面请求。
-
最不可替代的那批字节:自己上传的图
广播附图有三种写法,一个都猜不出来;以及第四次犯同一个错——从手上的样本推出一个封闭集合。
-
公开测试开始:豆备 0.0.1
能跑什么、不能跑什么,以及为什么在还缺一块的时候就开放测试。
-
档案里第一次有了真的图片
作品封面进档案,bundle 格式随之升到 1.1:surface 多了一个 asset。
-
一堆档案不等于一条链
增量抓取上线,以及一个把四次独立全量误接成一条链、把完整性说成「已验证」的 bug。
-
豆瓣的封禁页也返回 200
拿 6341 个真实页面校准判定器,以及豆瓣把「我的广播」改名成「我的动态」那天。
-
豆瓣自己报的数量,不能用来证明抓全了
同一次抓取里,电影的计数是准的,游戏的计数少了 5 个——而且缺口在列表中间。
-
为什么是 WARC 和 JSON Schema,而不是 protobuf
上一版这个项目用 Go + Bazel + protobuf 写的。这一版全部推翻了,理由是 2040 年。