RSS 阅读器是我的内容消费神器,因为它免受平台限制,把散落于各大内容平台之外的珍贵资源汇集到一处,这类内容往往来自个人或公司的高质量创作,而非单纯的营销信息。
但是,我发现一些内容原生并不支持 RSS 订阅功能。这就促使我手动构建一个订阅服务,以便能够在我的 RSS 阅读器中订阅这些内容。
本文将介绍如何利用 serverless 模式搭建 www.arcblock.io/blog/ 的代理 RSS 订阅服务。
代码已开源:github.com/linchen1987...
RSS 系统结构
首先,了解一下 RSS 系统的三大组成部分:RSS 阅读器、RSS 源、和 RSS 发布器。
- RSS 阅读器是我们直接用来阅读内容的客户端,如 Feedly feedly.com/
- RSS 源则是一个可以公开访问的 XML 文件,例如阮一峰的博客 RSS 源 www.ruanyifeng.com/blog/atom.x...
- RSS 发布器的职责是更新新内容到 RSS 源中,如阮一峰发布新文章时,他的 RSS 发布器便会将文章更新到其博客的 RSS 源中。
Serverless 的本质
Serverless 概念的核心是无需自己管理服务器。例如,你在自己的服务器上搭建并维护一个博客,这就需要服务器。相反,如果您将文章发布到掘金,可以把掘金视为为您的文章提供了 serverless 服务。
实现步骤
以订阅 www.arcblock.io/blog/ 的内容为例,下面是实现步骤:
- 获取信息源 :想要订阅的内容列表页即是信息源。因为列表页总会按文章发布时间排序,把最新发布的文章展示在最前面。我想订阅的信息源是 www.arcblock.io/blog/
- 解析信息源 :找到内容列表页后,先查看是否有 API 可用(通过浏览器的开发者工具查看网络请求)。如果有 API,可以直接从 API 接口获取内容。如果没有,就需要抓取网页内容,并解析出文章。例如,我找到了 Arcblock Blog 的 API:www.arcblock.io/blog/api/bl...
js
const thirdPartyApiUrl = 'https://www.arcblock.io/blog/api/blogs?page=1&size=20';
const response = await axios.get(thirdPartyApiUrl);
const data = response.data.data || [];
const blogs = [];
// 为每个博客条目添加到feed
data.forEach((item) => {
blogs.push({
title: blog.title,
description: blog.excerpt,
guid: blog.id,
url: `https://www.arcblock.io/blog/${blog.slug}`,
date: blog.publishTime,
});
});
- 将信息解析成 XML 格式:处理获取到的信息,将其转换成适合 RSS 阅读器读取的 XML 格式。
js
import RSS from 'rss';
var feed = new RSS({
title: 'ArcBlock Blog',
description: 'Latest blogs from arcblock.io',
feed_url:
'https://link-general-public.s3.ap-northeast-3.amazonaws.com/arcblock-blog-feed.xml',
site_url: 'https://www.arcblock.io/blog',
});
blogs.forEach((blog) => {
feed.item(blog);
});
- 发布 XML 至公网:无需搭建自己的服务器,只需将 XML 文件发布到任何支持静态内容托管的服务,如 GitHub、AWS S3、Cloudflare R2、阿里云 OSS 等。在本例中,我将 XML 发布到了 AWS S3。
js
import fs from 'node:fs';
import path from 'node:path';
import 'dotenv-flow/config';
import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';
export default async function upload(file) {
const s3Client = new S3Client({});
const bucketName = process.env.AWS_BUCKET;
const fileStream = fs.createReadStream(file);
await s3Client.send(
new PutObjectCommand({
Bucket: bucketName,
Key: path.basename(file),
Body: fileStream,
ACL: 'public-read',
ContentType: 'application/xml',
})
);
}
- 定时更新:同样,无需自建服务来实现定时更新功能。在本例中,我使用了 GitHub Actions 来定期执行更新操作,每天更新一次。
yml
name: Schedule
on:
schedule:
- cron: '0 0 * * *'
jobs:
main:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0
- uses: pnpm/action-setup@v2.0.1
with:
version: latest
- name: Use Node.js
uses: actions/setup-node@v2
with:
node-version: '18'
cache: 'pnpm'
- name: Setup
run: npm add -g @antfu/ni
- name: Install
run: nci
- name: Run
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_BUCKET: ${{ secrets.AWS_BUCKET }}
AWS_REGION: ${{ secrets.AWS_REGION }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
run: 'node tools/fetch-arcblock-blog.js'
总结
通过上述步骤,你就能搭建起一个能够代理不支持 RSS 的内容源的订阅服务,享受在 RSS 阅读器中阅读这些内容的便捷。
我们不仅要学会如何写代码,还要学会如何使用工具,站在巨人的肩膀上快速搭建我们想要的功能。