拯救网页收藏

Save my web collection

Posted by Lv Hui on September 14, 2024

起因

对于收藏夹中的失效链接,有时我们根本不知道它是在什么时候消失的,只有在有需要时候想去看的时候才发现链接已经失效了。有的可能仅仅是文章被删除了,有的可能连整个网站都已经不存在了。这种现象在中国互联网中更加严重,当个人博客也需要ICP备案之后,大量的个人博客以及非盈利性的个人网站消失了,有的可能只是无心再维持,而像微信公众号等依托于特定平台的文章,则是会随着内容审查规则的变动,以及各种敏感词的产生,随时都可能被平台删掉。所以在2022年产生了将网页永久保存的想法。

旧的尝试

当时看了很多可以收藏网页的方法,首先就排除了将数据放在服务商的各种方案,由于当时在服务器上有php的运行环境,所以就部署了wallabag。使用体验尚可,就是有的网页他采集不下来,还有一些格式不是很好看,和原网页差异比较大,但是基本上不影响阅读。后来在更换服务器时,也多次将该服务迁移,并且数据的导出也是相对方便的。

新的想法

最近发现使用 wallabag 时不能成功采集的网页实在是太多,所以又萌生了更换收藏方案的想法。

保存离线页面

第一个想到的就是使用简阅,因为它自带下载离线 html 的功能,但是下载的页面格式,是简悦自带的阅读模式的样式,并不能保证页面的 “原汁原味”。这又让我想到了之前看过的一篇对抗 link rot的文章。上面提到了一个叫作 SingleFile 的浏览器扩展,可将网页保存成单个 html 文件。还有一个扩展 webscrapbook 的功能类似,这里对已使用的功能稍作对比。

  • SingleFile 生成的 html 文件体积更小,除了保存在本地,支持更多的服务,比如存放在 GitHub、WebDAV服务器、Google Drive、Amazon S3等。最重要的是它支持 REST API,可以将数据通过 api 上传到服务器上。也有缺点就是个别的网站的样式或者图片错位了,不排除是我开了压缩HTML内容的选项。
  • webscrapbook 生成的 html 体积要大的多,尤其是对于比较复杂的页面。除了存放本地,支持的其他方式较少。保存页面时,支持的自定义操作更加丰富。兼容性更好,基本没有样式及图片出现错误的问题。

管理收藏文件

页面是收藏了,但是如何管理这些文件也成了问题,总不能放文件夹里,有需要的时候去找吧。所以我就想找支持导入单个 html 文件的存档服务,找来找去发现 ArchiveBox 就有这个功能。在部署之前还是考虑了一下存储的问题,一个网页文件的体积也是不小的,一个文件可能从几兆到几十兆不等,数量上来后体积也是很可观的,只能先考虑将文件放在一台大盘鸡上,由于机器的配置较低2C2G,遂放弃 ArchiveBox,去找一个更加轻量的服务来做管理。但是没找到什么能支持读取本地文件的书签管理工具,只能采用另一种替代方法,就是通过 Web 服务为每个 html 页面生成一个链接,然后在书签管理工具中存入页面对应的链接。最后决定在 ShaarliLinkding 中二选一,其实本来是更倾向于 Linkding 的,主要是它的页面更加符合现代审美,Shaarli 对系统资源占用的要更低,所以不得不妥协,选用了 Shaarli,但是不排除之后会换成 Linkding 的可能。

SingleFile 结合 Shaarli

上传文件

对于如何将页面文件上传到服务器上,我写了一个简单的 Web 服务用于处理 SingleFile 提交上来的文件信息,最好使用 https 协议,并使用 Bearer Token 来增加验证。该服务也同样用于展示页面,每个页面对应一个链接。上传成功后,调用 Shaarli 的 api 生成一条新的书签并存入该页面的链接,同时我将该页面的源链接存在了书签的描述当中,并为这条书签增加一个名为 singlefile 的标签,通过标签可以便于我们筛选出由 singlefile 生成的书签。为了不存入重复的书签,在添加新书签前可以通过 Sharrli 的 api 查询已存在的书签,如果查到相同链接的书签则不新增书签。

删除书签

上传到服务器的文件在书签被删除时并不会被及时的清理掉,而且我在 html 文件上传后并没有使用网页的标题作为文件名,所以手动删除是很麻烦的。Shaari 中并没有相关的功能,但是它支持自定义插件,只需要把插件放到plugins目录中,并在插件管理中启用插件就可以实现删除文件的功能。所以我写了一个在删除书签时同时删除链接所对应文件的插件。Shaari 提供一个名为delete_link的钩子,方便在书签删除前进行一些操作。当删除书签时会自动调用插件中绑定了这个钩子的函数,Shaari 中是用函数名来进行识别的,如函数名叫hook_插件名_delete_link就可以自动被调用了。被删除的书签信息会以参数的方式传入到函数中,再通过判断该书签是否具有 singlefile 标签来判断是否需要删除文件。

手动上传单文件

之前说过有的网页通过 SingleFile 生成的文件阅读效果不是很好,我们可以先用 webscrapbook 生成文件,再手动上传到服务器。这里我单独写了一个 api 用于手动上传本地文件,以便于自定义书签的标题以及原始链接地址。

备份

没有什么数据是绝对安全的,服务器可能会有宕机的情况,尤其是像我用的这种廉价大盘鸡,数据丢失后基本是很难找回的。所以数据备份就显得尤为重要,要备份的数据有两部分,一部分是 Shaari 的书签数据,只需要备份项目根目录下的data文件夹,另一部分是我们的离线文件数据,将这个存放数据的文件夹也进行备份。可以定时备份到各种云盘或者对象存储,只要是能存储文件的地方就可以,让你不至于在服务器宕机时抓狂。

总结

这套方案让我最满意的就是它能以最接近原页面的形式进行保存,即使原始的链接失效了,依然能看到它曾经样貌,就好像时间定格了一样。