制作自己的 RSSHub 路由
如前所述,我们以 GitHub 仓库 Issues 为例制作 RSS 源。我们将展示前面提到的四种数据获取方法:
通过 API
查看 API 文档
不同的站点有不同的 API。您可以查看要为其制作 RSS 源的站点的 API 文档。在本例中,我们将使用 GitHub Issues API。
创建主文件
打开您的代码编辑器并创建一个新文件。由于我们要为 GitHub 仓库 Issues 制作 RSS 源,因此建议将文件命名为 issue.js。
以下是让您开始的基本代码:
- issue.js
// 导入所需模组
const got = require('@/utils/got'); // 自订的 got
const { parseDate } = require('@/utils/parse-date');
module.exports = async (ctx) => {
// 在此处编写您的逻辑
ctx.state.data = {
// 在此处输出您的 RSS
};
};
获取用户输入
如前所述,我们需要从用户输入中获取 GitHub 用户名和仓库名称。如果请求 URL 中未提供仓库名称,则应默认为 RSSHub。您可以使用以下代码实现:
- 解构赋值
- 传统赋值
module.exports = async (ctx) => {
const { user, repo = 'RSSHub' } = ctx.params;
ctx.state.data = {
// 在此处输出您的 RSS
};
};
module.exports = async (ctx) => {
const user = ctx.params.user;
const repo = ctx.params.repo ?? 'RSSHub';
ctx.state.data = {
// 在此处输出您的 RSS
};
};
这两个代码片段都执行相同的操作。第一个使用对象解构将 user 和 repo 变量赋值,而第二个使用传统赋值和空值合并运算符在请求 URL 中未提供它的情况下将 repo 变量分配默认值 RSSHub。
从 API 获取数据
在获取用户输入后,我们可以使用它向 API 发送请求。大多数情况下,您需要使用 @/utils/got 中的 got(一个自订的 got 包装函数)发送 HTTP 请求。有关更多信息,请参阅 got 文档。
- 解构赋值
- 传统赋值
module.exports = async (ctx) => {
const { user, repo = 'RSSHub' } = ctx.params;
// 发送 HTTP GET 请求到 API 并解构返回的数据对象
const { data } = await got(`https://api.github.com/repos/${user}/${repo}/issues`, {
headers: {
// 为简单起见,此示例使用 HTML 而不是推荐的 'application/vnd.github+json',
// 因后者返回 Markdown 并需要进一步处理
accept: 'application/vnd.github.html+json',
},
searchParams: {
// 这允许用户设置条数限制
per_page: ctx.query.limit ? parseInt(ctx.query.limit, 10) : 30,
},
});
ctx.state.data = {
// 在此处输出您的 RSS
};
};
module.exports = async (ctx) => {
const user = ctx.params.user;
const repo = ctx.params.repo ?? 'RSSHub';
// 发送 HTTP GET 请求到 API
const response = await got(`https://api.github.com/repos/${user}/${repo}/issues`, {
headers: {
accept: 'application/vnd.github.html+json',
},
searchParams: {
per_page: ctx.query.limit ? parseInt(ctx.query.limit, 10) : 30,
},
});
// response.data 是上述请求返回的数据对象
const data = response.data;
ctx.state.data = {
// 在此处输出您的 RSS
};
};
生成 RSS 源
一旦我们从 API 获取到数据,我们需要进一步处理它以生成符合 RSS 规范的 RSS 源。 具体来说,我们需要提取源标题、源链接、文章标题、文章链接、文章正文和文章发布日期。
为此,我们可以将相关数据赋值给 ctx.state.data 对象,RSSHub 的中间件将处理其余部分。
以下是应有的最终代码:
- 最终代码
- 替代代码
const got = require('@/utils/got');
const { parseDate } = require('@/utils/parse-date');
module.exports = async (ctx) => {
const { user, repo = 'RSSHub' } = ctx.params;
const { data } = await got(`https://api.github.com/repos/${user}/${repo}/issues`, {
headers: {
accept: 'application/vnd.github.html+json',
},
searchParams: {
per_page: ctx.query.limit ? parseInt(ctx.query.limit, 10) : 30,
},
});
// 从 API 响应中提取相关数据
const items = data.map((item) => ({
// 文章标题
title: item.title,
// 文章链接
link: item.html_url,
// 文章正文
description: item.body_html,
// 文章发布日期
pubDate: parseDate(item.created_at),
// 如果有的话,文章作者
author: item.user.login,
// 如果有的话,文章分类
category: item.labels.map((label) => label.name),
}));
ctx.state.data = {
// 源标题
title: `${user}/${repo} issues`,
// 源链接
link: `https://github.com/${user}/${repo}/issues`,
// 源文章
item: items,
};
};
const got = require('@/utils/got');
const { parseDate } = require('@/utils/parse-date');
module.exports = async (ctx) => {
const { user, repo = 'RSSHub' } = ctx.params;
const { data } = await got(`https://api.github.com/repos/${user}/${repo}/issues`, {
headers: {
accept: 'application/vnd.github.html+json',
},
searchParams: {
per_page: ctx.query.limit ? parseInt(ctx.query.limit, 10) : 30,
},
});
ctx.state.data = {
// 源标题
title: `${user}/${repo} issues`,
// 源链接
link: `https://github.com/${user}/${repo}/issues`,
// 遍历所有此前获取的数据
item: data.map((item) => ({
// 文章标题
title: item.title,
// 文章链接
link: item.html_url,
// 文章正文
description: item.body_html,
// 文章发布日期
pubDate: parseDate(item.created_at),
// 如果有的话,文章作者
author: item.user.login,
// 如果有的话,文章分类
category: item.labels.map((label) => label.name),
}));
};
};
通过 got 从 HTML 获取数据
创建主文件
打开您的代码编辑器并创建一个新文件。由于我们要为 GitHub 仓库 Issues 制作 RSS 源,因此建议将文件命名为 issue.js。
以下是让您开始的基本代码:
// 导入必要的模组
const got = require('@/utils/got'); // 自订的 got
const cheerio = require('cheerio'); // 可以使用类似 jQuery 的 API HTML 解析器
const { parseDate } = require('@/utils/parse-date');
module.exports = async (ctx) => {
// 在此处编写您的逻辑
ctx.state.data = {
// 在此处输出您的 RSS
};
};
parseDate 函数是 RSSHub 提供的一个工具函数,在代码的后面我们会用到它来解析日期。
您需要添加自己的代码来从 HTML 文档中提取数据、处理数据并以 RSS 格式输出。在下一步中,我们将详细介绍此过程的细节。
获取用户输入
如前所述,我们需要从用户输入中获取 GitHub 用户名和仓库名称。如果请求 URL 中未提供仓库名称,则应默认为 RSSHub。您可以使用以下代码实现:
module.exports = async (ctx) => {
// 从 URL 参数中获取用户名和仓库名称
const { user, repo = 'RSSHub' } = ctx.params;
ctx.state.data = {
// 在此处输出您的 RSS
};
};
在这段代码中,user 将被设置为 user 参数的值,如果存在 repo 参数,则 repo 将被设置为该参数的值,否则为 RSSHub。
从网页获取数据
在获取了用户输入之后,我们需要向网页发起请求,以检索所需的信息。在大多数情况下,我们将使用 @/utils/got 中的 got(一个自订的 got 包装函数)发送 HTTP 请求。您可以在 got 文档 中找到有关如何使用 got 的更多信息。
首先,我们将向 API 发送 HTTP GET 请求,并将 HTML 响应加载到 Cheerio 中,Cheerio 是一个帮助我们解析和操作 HTML 的库。
const baseUrl = 'https://github.com';
const { user, repo = 'RSSHub' } = ctx.params;
// 注意,".data" 属性包含了请求返回的目标页面的完整 HTML 源代码
const { data: response } = await got(`${baseUrl}/${user}/${repo}/issues`);
const $ = cheerio.load(response);
接下来,我们将使用 Cheerio 选择器选择相关的 HTML 元素,解析我们需要的数据,并将其转换为数组。
// 我们使用 Cheerio 选择器选择所有带类名“js-navigation-container”的“div”元素,
// 其中包含带类名“flex-auto”的子元素。
const items = $('div.js-navigation-container .flex-auto')
// 使用“toArray()”方法将选择的所有 DOM 元素以数组的形式返回。
.toArray()
// 使用“map()”方法遍历数组,并从每个元素中解析需要的数据。
.map((item) => {
item = $(item);
const a = item.find('a').first();
return {
title: a.text(),
// `link` 需要一个绝对 URL,但 `a.attr('href')` 返回一个相对 URL。
link: `${baseUrl}${a.attr('href')}`,
pubDate: parseDate(item.find('relative-time').attr('datetime')),
author: item.find('.opened-by a').text(),
category: item
.find('a[id^=label]')
.toArray()
.map((item) => $(item).text()),
};
});
ctx.state.data = {
// 在此处输出您的 RSS
};