我使用的一些特殊性质的网站不提供任何更新订阅功能,从他们的网页内容中生成 RSS 应该是比较好的解决方案。
很多传统行业的网站虽然会发布一些内容,但是功能贫弱,甚至没有更新通知的功能,通常都是悄无声息的就发布了新内容。而通常这类网站的发布频率又比较低,因此从网页上生成 RSS 来订阅似乎是个不错的选择。从网页内容生成 RSS 其实已经是一个很成熟常见的操作,也有很多的相关工具,我咋研究了一些之后,发现他们大多有一些使用数量限制,考虑到我的需求可能会逐渐增长,再加上我访问的页面有些奇怪的情况,自己开发一个定制化的 RSS 生成工具是比较合适的选择。因此 RSS-it 诞生了。
为什么选择 Bun
最直接的原因是:all in one。
我对于 bun 的理解,它其实和 node、deno 并非完全一种东西,它提供了极高的 node 兼容性,在此基础上又额外实现了 all in one 开发能力。all in one 可以说是一把双刃剑,但在我将一些个人项目迁移到 Bun 或者使用 Bun 重构之后,我觉得至少在小型的个人项目这个方向上,Bun 的 all in one 能力解决了很多问题,提高了开发效率和维护性。all in one 也就意味着很多的能力都由 Bun 直接提供,不需要思考选哪个框架、库,又不用担心这些三方代码的维护问题,稳定性也有保证,突出一个简单高效。
应用设计
应用的基本功能:
- 获取网页内容
- 提取关键信息
- 生成 RSS
- 通过接口输出 RSS
获取网页内容以及提取关键信息
由于目标网页目前都是静态页面类型,内容可以直接从 HTML 中获取。Bun 内置了按照 Web 标准实现的 fetch 功能,用 fetch 来做一些基本的网络请求是非常方便的。
CSS 选择器毫无疑问是我们这个场景下最合适的目标定位方式,无论网页结构是什么样子的,通过 CSS 选择器总能准去定位到他。同时 CSS 选择器的扩展能力强,即使网页后续发生变化,在不改变应用逻辑的情况下,通过调整 CSS 选择器,就能应对变化。
这个应用不需要完整的网页渲染,因此选择了 Fast HTML Parser 这个库来做 HTML 解析提取,这个库实现了基础的 HTML 解析以及 HTML 节点操作,包括了关键的 querySelector,使得我们可以直接使用 CSS 选择器来选择节点,并且也可以通过 textContent 来获取节点内容。Bun 声明对 node 的完整兼容,因此 node 的库拿来直接用一般是没有问题的。
生成 RSS
使用 feed 这个库来生成 RSS 内容,我选择的是 RSS 2.0 的标准,关于 RSS 和 atom 的区别,这里就不展开了,现在常见的 feed(RSS)应用,应该都是同时支持两者的,对于我们这个应用的场景,二者是没有太大差异的,我更习惯于 RSS,因此选择了 RSS 2.0 的标准。
通过接口输出 RSS
Bun 内置的 serve 方法应对我们这个单一的接口场景绰绰有余。原生支持 async,并且内置了 Response 实现,返回数据流和文件内容都非常方便。
对于 Bun 的一些想法
结合上面写到的这些内容,可以看出,Bun 在 all in one 方面很重要的一个点就是实现了大量 Web api,能在服务端中使用 Web api 来开发对于一个以浏览器环境为主的 Web 前端开发来说是很有吸引力的。又或者说,对于一个网页(服务端)应用来说,使用 Web api 来开发的适配性天生就更高。
另外,Bun 在兼容 node 的基础上,额外内置了一些 api,这 api 最大的特点就是简化。如 Bun.file、Bun.write 的设计,在常见的文件操作场景,我觉得比 node 的 fs 模块更加简洁高效。能够用简洁的 api 来完成大部分代码逻辑,对于开发者来说会非常友好,这意味着我们不需要引入 improved 类型的库来解决语言、运行环境原生 api 难用、低效的问题(这在 node 中是非常常见的)。
low-leveled 的 api 设计可能也是一把双刃剑,如果不能做好功能性和易用性的平衡,开发时会非常痛苦。而 Bun 选择了易用性,也许是一个很好的选择,Bun 内置的 api 以高效、易用为目标,这些 api 能解决大部分需求即可。当真的需要扩展和功能性的时候,我们还可以使用兼容的 node api。
本篇介绍了 RSS-it 的创建背景和基础设计思路,并且也介绍了选择 Bun 的原因和它相对于 node 的特点。后续我会基于代码来详细介绍 Bun 的更多特点。