Back

DailyHotAPI 自定义 RSS 订阅接口

By Ming 一月 14, 2025 VPS

今日热榜 API,一个聚合热门数据的 API 接口,支持 RSS 模式 及 Vercel 部署,并含有配套的前端项目

拉取项目

1
2
git clone https://github.com/imsyy/DailyHotApi.git
cd DailyHotApi

安装依赖

1
npm install

修改配置

复制 /.env.example 文件并重命名为 /.env 并修改配置,包括Redis端口密码等

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
...
# 允许的域名
ALLOWED_DOMAIN = "*"

# 允许的主域名,填写格式为 imsyy.top
## 若填写该项,将忽略 ALLOWED_DOMAIN
ALLOWED_HOST="jianbing.tk"

# ROBOT
DISALLOW_ROBOT = true

# Redis
REDIS_HOST="127.0.0.1"
REDIS_PORT=6379
REDIS_PASSWORD="Your Password"
...

开发

1
npm run dev

成功启动后程序会在控制台输出可访问的地址

编译运行

1
2
npm run build
npm run start

成功启动后程序会在控制台输出可访问的地址

自定义RSS订阅

自定义RSS订阅通常只要新增两个部分的内容:

1.src\routes路径下的xxx.ts文件,比如nodeseek.ts,用来处理与 NodeSeek相关的路由和数据获取逻辑。

2.src\router.type.d.ts文件中的特定数据结构的类型接口。例如,"36kr" 类型接口,在36kr.ts中使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
return {
...result,
data: list.map((v: RouterType["36kr"]) => { //此处的RouterType
const item = v.templateMaterial;
return {
id: v.itemId,
title: item.widgetTitle,
cover: item.widgetImage,
author: item.authorName,
timestamp: getTime(v.publishTime),
hot: item.statCollect || undefined,
url: `https://www.36kr.com/p/${v.itemId}`,
mobileUrl: `https://m.36kr.com/p/${v.itemId}`,
};
}),
};

当然也可以不定义新的类型接口,只添加xxx.ts文件,借助parseRSS处理:

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
45
46
47
48
49
50
51
52
/* parseRSS.ts */
import RSSParser from "rss-parser";
import logger from "./logger.js";

/**
* 提取 RSS 内容
* @param content HTML 内容
* @returns RSS 内容
*/
export const extractRss = (content: string): string | null => {
// 匹配 <rss> 标签及内容
const rssRegex = /(<rss[\s\S]*?<\/rss>)/i;
const matches = content.match(rssRegex);
return matches ? matches[0] : null;
};

/**
* 解析 RSS 内容
* @param rssContent RSS 内容
* @returns 解析后的 RSS 内容
*/
export const parseRSS = async (rssContent: string) => {
const parser = new RSSParser();
// 是否为网址
const isUrl = (url: string) => {
try {
new URL(url);
return true;
} catch (error) {
return false;
}
};
try {
const feed = isUrl(rssContent)
? await parser.parseURL(rssContent)
: await parser.parseString(rssContent);
const items = feed.items.map((item) => ({
title: item.title,
link: item.link,
pubDate: item.pubDate,
author: item.creator ?? item.author,
content: item.content,
contentSnippet: item.contentSnippet,
guid: item.guid,
categories: item.categories,
}));
return items;
} catch (error) {
logger.error("❌ [RSS] An error occurred while parsing RSS content");
throw error;
}
};

一般网站的RSS订阅都能借助src\utils\parseRSS.ts文件正确解析:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const getList = async (noCache: boolean) => {
const url = `https://rss.nodeseek.com/`;
const result = await get({ url, noCache });
const list = await parseRSS(result.data);
return {
...result,
data: list.map((v, i) => ({
id: v.guid || i,
title: v.title || "",
desc: v.content?.trim() || "",
author: v.author,
timestamp: getTime(v.pubDate || 0),
hot: undefined,
url: v.link || "",
mobileUrl: v.link || "",
})),
};
};

部分订阅需要自己处理请求返回的数据,同时新增RouterType,且不借助parseRSS处理result.data

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
const getList = async (options: Options, noCache: boolean) => {
const { type } = options;
const url = `https://linux.do/${type}.json`;
const result = await get({ url, noCache });
const list = result.data?.topic_list?.topics;
const filteredIds = [5, 293017, 298988];
return {
fromCache: result.fromCache,
updateTime: result.updateTime,
data: list
.filter((v: RouterType["linuxdo"]) => !filteredIds.includes(v.id))
.map((v: RouterType["linuxdo"]) => ({
id: v.id,
title: v.title,
timestamp: v.last_comment_at || v.created_at,
hot: v.highest_post_number,
url: `https://linux.do/t/topic/${v.id}`,
mobileUrl: `https://linux.do/t/topic/${v.id}`,
})),
};
};

注意:除了自定义RSS订阅以外,还可以自行修改DailyHotApi项目的icon、页脚等信息,其中Home.tsx中的<img>标签中的图片为base64格式,需自行转码。

Docker部署

将本地开发好的项目部署至自己的服务器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 列出所有正在运行的容器
docker ps

# 列出所有镜像,并筛选出名称包含 dailyhot-api 的镜像
docker images | grep dailyhot-api

# 停止名为 dailyhot-api 的容器(可以使用容器名称或 ID)
docker stop dailyhot-api

# 删除名为 dailyhot-api 的容器(可以使用容器名称或 ID)
docker rm dailyhot-api

# 构建镜像,使用当前目录的 Dockerfile,并将镜像命名为 dailyhot-api
docker build -t dailyhot-api .

# 运行容器,设置自动重启,映射主机的 6688 端口到容器的 6688 端口,并在后台运行
docker run --restart always -p 6688:6688 -d --name dailyhot-api dailyhot-api

# 或使用 Docker Compose 启动服务,后台运行
docker-compose up -d

注意:原项目中包含Redis缓存配置,需与服务器中端口和密码保持一致。

此外,Redis 缓存时长默认为 3600 秒(即一小时),你可以在 config.ts 文件中通过修改环境变量 CACHE_TTL 的值来调整该时长。

成品展示

Demo

预览:神马值得看

参考

许可协议

本文由 Ming 原创,采用 CC BY-NC-SA 4.0 协议。转载请注明出处。

PERMALINK

https://iming.eu.org/2025/01/14/dailyhotapi-rss/

Comments