So!azy

彻底重构了博客的豆瓣观影同步机制

ilya-pavlov-OqtafYT5kTw-unsplash

之前写《博客新增「Movies」页面》那篇的时候说过,我搭了一个 GitHub Actions,每天定点去拽豆瓣的观影记录,弄回来的文件就当博客「Movies」页面的数据源用。

本来这玩意儿放那儿自己跑一直挺正常的。结果这两天闲着没事点开 Actions 那个页面一看,好家伙,以前一排溜的绿勾现在全跟着黄色的 Warning 警告了,不过警告并不是运行失败,核心报错是:Node.js 20 actions will be deprecated。不仅如此,原本用来传豆瓣 Cookie 的参数也一直在报 Unexpected input 错误。

这其实是个历史遗留的「老大难」问题。我最早参考引用的核心抓取项目 Douban sync for GitHub Actions,最后一次实质性更新已经是快四年前的事了。这不仅意味着它用着即将被 GitHub 淘汰的旧版 Node.js 环境(GitHub 官方已经发了通牒,2026 年 6 月将强制切换到 Node 24),更要命的是,面对豆瓣越来越玄学的防爬策略,这种年久失修的项目就像个定时炸弹,指不定哪天就彻底罢工了。

再往下扒,发现连用来自动提交代码的第三方插件 EndBug/add-and-commit 也两三年没动静了。

本来想着,要不干脆把原项目 fork 出来自己修修补补?但点开源码一看,Docker 嵌套 Shell 再去调旧版 Node 的结构看着实在有些头大。想想也是,与其在一个快要塌的老房子里搞精装修,不如直接推倒重来。

于是,干脆自己和 AI 一起搓了一个全新的同步后端。对于这次重构,我的核心诉求只有一个:不管后端底层怎么换,最终输出的 movie.json 数据格式必须和原来 100% 一模一样。 这样我的博客前端和另一个负责拉取海报的仓库,就一行代码都不用改,实现无感切换。

折腾的过程大概分了这么几步:

🐍 第一步:抛弃历史包袱,用 Python 重新打底

旧项目其实走了一些弯路。既然都是发 HTTP 请求去拿 JSON,用 Python 显然是最轻量也最顺手的。

我直接抛弃了复杂的环境嵌套,用 Python 3.12 配合 httpx 写了底层的网络请求逻辑。核心还是利用 Cookie 来绕过豆瓣的登录验证。为了防止一次性请求太多被豆瓣风控,在脚本里加上了合理的分页逻辑(Pagination),每次拉取一页后礼貌性地 sleep 一会儿。测试了一下,全量拉取我那 800 多部已看电影,不仅非常稳定,速度也完全可以接受。

🪡 第二步:格式的 100% 像素级复刻

其实这步才是我最关心的点。豆瓣那个接口吐出来的数据其实挺杂的,什么分享文案啊、调色板配置啊,全塞在里面。而且原来那个项目的作者还自己加了一个图片代理的 URL 进去防盗链。

要想不折腾下游的渲染代码,最省事的办法就是坚决不去「清洗」这些数据。我在写 Python 处理这块的时候,直接把拿到的那一堆原始 JSON 结构连锅端了过来,甚至连旧版的图片替换逻辑都照着抄了一遍。果然,拿出来的新版 movie.json 文件跟之前完全一个模子刻出来的,前面渲染页面的时候连个磕巴都没打。

🧹 第三步:干掉所有过时的第三方依赖

既然决定重构,那就搞得彻底一点,把所有依赖旧版 Node 的隐患全拔了。

前面提到那个几年不更新的 add-and-commit 插件,仔细一想,GitHub Actions 的 Ubuntu 运行环境里本来就自带了原生的 Git 呀,为什么要用别人包好的二手工具呢?

索性直接把它删了,换成了一段几行的纯原生 Shell 脚本:配置一下机器人名义的用户名,用 git diff-index --quiet HEAD 判断一下数据文件有没有发生实质性的变更,有更新就 commitpush,没更新就直接结束。干脆利落,清清爽爽。最后顺手把 checkout 插件也升级到了原生支持 Node 24 的 v6 版本。

💡 写在最后

看着重构后的工作流跑完,终于迎来了一个没有任何黄标 Warning、干干净净的成功绿勾,强迫症得到了极大的满足。

我也顺便把触发频率从之前的每天深夜一次,调整到了每天均匀地跑四次。这样以后只要在豆瓣标记了看过,最迟几个小时内博客上就能同步显示出来,时效性上了一个大台阶。

这个新写的小项目我已经独立成了一个新的开源仓库。不过考虑到这类涉及抓取个人数据的爬虫工具容易被平台盯上,我在 README 里隐去了很多对抗性的描述,加了免责声明,就让它作为一个低调的个人数据备份工具安静地运行吧。

#daily