官方文档简洁清晰,再增加内容就显得臃肿了。故另外写一篇对新手友好的指南。

RSSHub是什么

万物皆可RSS,这个项目是一个将任意网页内容转换为RSS的框架。其开源性质其实是鼓励大家按个人需求自己动手写规则的。

确认是否有必要写新规则

有的网站是有提供RSS的,如果你只是需要全文输出可以结合full-text-rss来实现。
有的网站已经有RSSHub规则了,可以通过RSSHub RadarRSSBud快速发现和订阅。

Fork并更新项目

Fork然后将你的项目git clone到本地。
给你的项目增加上游:

1
git remote add upstream https://github.com/DIYgod/RSSHub

若以前已贡献过则需要从上游获取最新内容并强制更新:

1
2
3
4
git fetch upstream
git reset --hard upstream/master
git rebase upstream/master
git push -f origin master

安装npm

推荐用Node Version Manager灵活切换npm版本。

1
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.37.2/install.sh | bash

重启终端然后安装npm的稳定版:

1
nvm install stable

国内环境则推荐改为淘宝源(可选):

1
npm config set registry https://registry.npm.taobao.org

通过npm config get registry可检查是否更改成功。

开启开发环境

进入项目根目录。

1
2
npm install
npm run dev

然后浏览器打开http://localhost:1200dev模式下每次保存项目就会自动重启应用修改,方便实时调试。

编写新规则

这里以网易号(通用)为例解释如何添加新规则。
网易对大部分网易号是提供了api调取文章列表的,但有些网易号搜索页面搜不到的小众网易号(文章页面不含data-wemediaid)无法用api获取网页列表。
比如这个后厂村体工队

添加脚本路由

打开/lib/router.js,增加路由。

1
router.get('/netease/dy2/:id', require('./routes/netease/dy2'));

由前面网易号的主页可见,其链接最后html的文件名可用于区分网易号,故作为参数id传入脚本。若是可选参数则是:param?这样的格式。

编写脚本

/lib/routes/中的路由对应路径下创建新的js脚本。由前面路由可见新建dy2.js

import所需模块

1
2
3
const got = require('@/utils/got');
const cheerio = require('cheerio');
const iconv = require('iconv-lite');

这里got用来获取html信息,cheerio处理获取的数据。由于某些网易页面并不是用通用的utf8编码,故还需要iconv-lite进行格式转换。

获取主页数据并生成文章列表

优先用api方式获取,但由前所述此法不可行。故直接抓取主页信息。另外还有一种方法是使用puppeteer渲染页面,这里不涉及。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
//路由的参数通过`ctx.params.id`传入
const id = ctx.params.id;
//得到抓取的目标`url`
const url = `https://www.163.com/dy/media/${id}.html`;
//通过`got`获得HTML数据
const response = await got.get(url, { responseType: 'buffer' });
//获得的信息乱码,用`gbk`解码
const data = iconv.decode(response.data, 'gbk');
//用cheerio载入数据,因为前面已经解码,故这里设定decodeEntities为false
const $ = cheerio.load(data, { decodeEntities: false });

//用浏览器开发模式获取文章列表选择器的路径,列表通常以`li`结尾
const list = $('.media_articles ul li')
//为防反爬故只抓取最新的5篇文章
.slice(0, 5)
//使用map遍历数组,解析出每一个item的结果并生成可迭代的list
.map((_, item) => {
item = $(item);
//单篇文章块的信息,其中文字部分是标题,属性是文章链接
const a = item.find('h2.media_article_title a');
//发表日期
let pubDate = item.find('.media_article_date').text();
pubDate = new Date(pubDate).toUTCString();
//返回格式化数据
return {
title: a.text(),
link: a.attr('href'),
pubDate: pubDate,
};
})
.get();

具体如何用浏览器选择可见这节图文教程

遍历文章列表并抓取全文

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
const items = await Promise.all(
list.map(async (item) => {
let content;
//`www.163.com`下的文章用的是gbk编码
if (item.link.startsWith('https://www.163.com')) {
//抓取全文一定要通过`ctx.cache.tryGet`使用缓存,避免每次访问路由都去请求那么多接口触发反爬
const itemData = await ctx.cache.tryGet(item.link, async () =>
iconv.decode(
(
await got.get(item.link, {
responseType: 'buffer',
})
).data,
'gbk'
)
);
content = cheerio.load(itemData, { decodeEntities: false });
//其他三级域名下的文章则是utf8编码
} else {
const itemData = await ctx.cache.tryGet(
item.link,
async () =>
(
await got.get(item.link, {
responseType: 'buffer',
})
).data
);
content = cheerio.load(itemData, { decodeEntities: false });
}

//用浏览器开发模式获取正文内容的路径,然后用`.html()`渲染成网页信息作为RSS文章详情
const eDescription = content('.post_body').html();

//生成格式化数据
const single = {
title: item.title,
link: item.link,
description: eDescription,
pubDate: item.pubDate,
};
return Promise.resolve(single);
})
);

注:网易原来有部分网址是gbk编码的,现在全部改成utf-8了,处理更方便。更新代码见此处

生成RSS源

1
2
3
4
5
6
7
let link;
ctx.state.data = {
title: $('.media_info h1').text(),
link,
description: '',
item: items,
};

添加脚本文档

更新/docs/目录内对应的文档,可以执行npm run docs:dev查看文档效果。
这里网易号属于新媒体,故修改/docs/new-media.md

1
2
3
4
## 网易号(通用)
<Route author="mjysci" example="/netease/dy2/T1555591616739" path="/netease/dy2/:id" :paramsDesc="['id,该网易号主页网址最后一项html的文件名']" anticrawler="1"/>
优先使用方法一,若是网易号搜索页面搜不到的小众网易号(文章页面不含`data-wemediaid`)则可使用此法。
触发反爬会只抓取到标题,建议自建。

格式是markdown。其中:paramsDesc是通过数组解释路由中每个参数的作用和获取方式,其他项不言自明,参考这个例子填写即可。

资源

第三方RSSHub,对于反爬严格的网站还是推荐自建服务

参考资料

提交新的 RSSHub 规则
RSSHub文档