diff --git a/README.md b/README.md
index 5d225cd..7104286 100644
--- a/README.md
+++ b/README.md
@@ -29,6 +29,9 @@
> 🟢 状态正常 / 🟠 可能失效 / ❌ 无法使用 / ⚠️ 需要科学上网
+
+查看全部接口状态
+
| **站点** | **类别** | **调用名称** | **状态** |
| ---------------- | ------------ | -------------- | -------- |
| 哔哩哔哩 | 热门榜 | bilibili | 🟢 |
@@ -52,10 +55,13 @@
| CSDN | 排行榜 | csdn | 🟢 |
| 稀土掘金 | 热榜 | juejin | 🟢 |
| 腾讯新闻 | 热点榜 | qq-news | 🟢 |
+| 新浪网 | 热榜 | sina | 🟢 |
+| 新浪新闻 | 热点榜 | sina-news | 🟢 |
| 网易新闻 | 热点榜 | netease-news | 🟢 |
| 吾爱破解 | 榜单 | 52pojie | ❌ |
| 全球主机交流 | 榜单 | hostloc | ❌ |
| 虎嗅 | 24小时 | huxiu | 🟢 |
+| 虎扑 | 步行街热帖 | hupu | 🟢 |
| 爱范儿 | 快讯 | ifanr | 🟢 |
| 英雄联盟 | 更新公告 | lol | 🟢 |
| 原神 | 最新消息 | genshin | 🟢 |
@@ -69,6 +75,8 @@
| 中国地震台 | 地震速报 | earthquake | 🟢 |
| 历史上的今天 | 月-日 | history | 🟢 |
+
+
## ⚙️ 使用
本项目支持 `Node.js` 调用,可在安装完成后调用 `serveHotApi` 来开启服务器
diff --git a/package.json b/package.json
index c763052..d399b06 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "dailyhot-api",
- "version": "2.0.2",
+ "version": "2.0.3",
"description": "An Api on Today's Hot list",
"keywords": [
"API",
diff --git a/src/router.types.d.ts b/src/router.types.d.ts
index f644399..144ead0 100644
--- a/src/router.types.d.ts
+++ b/src/router.types.d.ts
@@ -277,4 +277,34 @@ export type RouterType = {
cover: string;
pic_share: string;
};
+ hupu: {
+ tid: number;
+ title: string;
+ replies: number;
+ username: string;
+ time: string;
+ url: string;
+ };
+ sina: {
+ base: {
+ base: {
+ uniqueId: string;
+ url: string;
+ };
+ };
+ info: {
+ hotValue: string;
+ title: string;
+ };
+ };
+ "sina-news": {
+ id: string;
+ title: string;
+ media: string;
+ url: string;
+ create_date: string;
+ create_time: string;
+ top_num: string;
+ time: string;
+ };
};
diff --git a/src/routes/36kr.ts b/src/routes/36kr.ts
index cc5bbcf..0286c68 100644
--- a/src/routes/36kr.ts
+++ b/src/routes/36kr.ts
@@ -10,7 +10,7 @@ export const handleRoute = async (c: ListContext, noCache: boolean) => {
name: "36kr",
title: "36氪",
type: "热榜",
- parame: {
+ params: {
type: {
name: "热榜分类",
type: {
diff --git a/src/routes/52pojie.ts b/src/routes/52pojie.ts
index d30c73a..ec3bf79 100644
--- a/src/routes/52pojie.ts
+++ b/src/routes/52pojie.ts
@@ -11,7 +11,7 @@ export const handleRoute = async (c: ListContext, noCache: boolean) => {
name: "52pojie",
title: "吾爱破解",
type: "榜单",
- parame: {
+ params: {
type: {
name: "榜单分类",
type: {
diff --git a/src/routes/acfun.ts b/src/routes/acfun.ts
index 0466c0a..d38fe81 100644
--- a/src/routes/acfun.ts
+++ b/src/routes/acfun.ts
@@ -12,7 +12,7 @@ export const handleRoute = async (c: ListContext, noCache: boolean) => {
title: "AcFun",
type: "排行榜",
description: "AcFun是一家弹幕视频网站,致力于为每一个人带来欢乐。",
- parame: {
+ params: {
type: {
name: "频道",
type: {
diff --git a/src/routes/baidu.ts b/src/routes/baidu.ts
index 18f6ea3..43646f7 100644
--- a/src/routes/baidu.ts
+++ b/src/routes/baidu.ts
@@ -9,7 +9,7 @@ export const handleRoute = async (c: ListContext, noCache: boolean) => {
name: "baidu",
title: "百度",
type: "热搜榜",
- parame: {
+ params: {
type: {
name: "热搜类别",
type: {
diff --git a/src/routes/bilibili.ts b/src/routes/bilibili.ts
index 4175599..0ad9c42 100644
--- a/src/routes/bilibili.ts
+++ b/src/routes/bilibili.ts
@@ -12,7 +12,7 @@ export const handleRoute = async (c: ListContext, noCache: boolean) => {
title: "哔哩哔哩",
type: "热门榜",
description: "你所热爱的,就是你的生活",
- parame: {
+ params: {
type: {
name: "排行榜分区",
type: {
diff --git a/src/routes/douban-movie.ts b/src/routes/douban-movie.ts
index f0ba724..3e7f5ca 100644
--- a/src/routes/douban-movie.ts
+++ b/src/routes/douban-movie.ts
@@ -18,14 +18,14 @@ export const handleRoute = async (_: undefined, noCache: boolean) => {
};
// 数据处理
-const getNumbers = (text: string | undefined) => {
- if (!text) return 10000000;
+const getNumbers = (text: string | undefined): number => {
+ if (!text) return 0;
const regex = /\d+/;
const match = text.match(regex);
if (match) {
return Number(match[0]);
} else {
- return 10000000;
+ return 0;
}
};
@@ -44,7 +44,8 @@ const getList = async (noCache: boolean) => {
const listData = listDom.toArray().map((item) => {
const dom = $(item);
const url = dom.find("a").attr("href") || undefined;
- const score = dom.find(".rating_nums").text() ?? "0.0";
+ const scoreDom = dom.find(".rating_nums");
+ const score = scoreDom.length > 0 ? scoreDom.text() : "0.0";
return {
id: getNumbers(url),
title: `【${score}】${dom.find("a").attr("title")}`,
diff --git a/src/routes/earthquake.ts b/src/routes/earthquake.ts
index d96b61a..de0b606 100644
--- a/src/routes/earthquake.ts
+++ b/src/routes/earthquake.ts
@@ -33,7 +33,7 @@ export const handleRoute = async (c: ListContext, noCache: boolean) => {
name: "earthquake",
title: "中国地震台",
type: "地震速报",
- parame: {
+ params: {
type: {
name: "速报分类",
type: {
diff --git a/src/routes/genshin.ts b/src/routes/genshin.ts
index d0b9bb0..41b5a4f 100644
--- a/src/routes/genshin.ts
+++ b/src/routes/genshin.ts
@@ -10,7 +10,7 @@ export const handleRoute = async (c: ListContext, noCache: boolean) => {
name: "genshin",
title: "原神",
type: "最新动态",
- parame: {
+ params: {
type: {
name: "榜单分类",
type: {
diff --git a/src/routes/hellogithub.ts b/src/routes/hellogithub.ts
index 08b054d..2c34420 100644
--- a/src/routes/hellogithub.ts
+++ b/src/routes/hellogithub.ts
@@ -11,7 +11,7 @@ export const handleRoute = async (c: ListContext, noCache: boolean) => {
title: "HelloGitHub",
type: "热门仓库",
description: "分享 GitHub 上有趣、入门级的开源项目",
- parame: {
+ params: {
sort: {
name: "排行榜分区",
type: {
diff --git a/src/routes/history.ts b/src/routes/history.ts
index 92e0397..36b4580 100644
--- a/src/routes/history.ts
+++ b/src/routes/history.ts
@@ -6,14 +6,14 @@ import { getCurrentDateTime } from "../utils/getTime.js";
export const handleRoute = async (c: ListContext, noCache: boolean) => {
// 获取日期
- const day = c.req.query("day") || getCurrentDateTime().day;
- const month = c.req.query("month") || getCurrentDateTime().month;
+ const day = c.req.query("day") || getCurrentDateTime(true).day;
+ const month = c.req.query("month") || getCurrentDateTime(true).month;
const { fromCache, data, updateTime } = await getList({ month, day }, noCache);
const routeData: RouterData = {
name: "history",
title: "历史上的今天",
type: `${month}-${day}`,
- parame: {
+ params: {
month: "月份",
day: "日期",
},
diff --git a/src/routes/honkai.ts b/src/routes/honkai.ts
index 37c7f56..0be7dc3 100644
--- a/src/routes/honkai.ts
+++ b/src/routes/honkai.ts
@@ -10,7 +10,7 @@ export const handleRoute = async (c: ListContext, noCache: boolean) => {
name: "honkai",
title: "崩坏3",
type: "最新动态",
- parame: {
+ params: {
type: {
name: "榜单分类",
type: {
diff --git a/src/routes/hostloc.ts b/src/routes/hostloc.ts
index 7c26054..84d4635 100644
--- a/src/routes/hostloc.ts
+++ b/src/routes/hostloc.ts
@@ -11,7 +11,7 @@ export const handleRoute = async (c: ListContext, noCache: boolean) => {
name: "hostloc",
title: "全球主机交流",
type: "榜单",
- parame: {
+ params: {
type: {
name: "榜单分类",
type: {
diff --git a/src/routes/hupu.ts b/src/routes/hupu.ts
new file mode 100644
index 0000000..fc1c333
--- /dev/null
+++ b/src/routes/hupu.ts
@@ -0,0 +1,51 @@
+import type { RouterData, ListContext, Options } from "../types.js";
+import type { RouterType } from "../router.types.js";
+import { get } from "../utils/getData.js";
+
+export const handleRoute = async (c: ListContext, noCache: boolean) => {
+ const type = c.req.query("type") || "1";
+ const { fromCache, data, updateTime } = await getList({ type }, noCache);
+ const routeData: RouterData = {
+ name: "hupu",
+ title: "虎扑",
+ type: "步行街热帖",
+ params: {
+ type: {
+ name: "榜单分类",
+ type: {
+ 1: "主干道",
+ 6: "恋爱区",
+ 11: "校园区",
+ 12: "历史区",
+ 612: "摄影区",
+ },
+ },
+ },
+ link: "https://bbs.hupu.com/all-gambia",
+ total: data?.length || 0,
+ updateTime,
+ fromCache,
+ data,
+ };
+ return routeData;
+};
+
+const getList = async (options: Options, noCache: boolean) => {
+ const { type } = options;
+ const url = `https://m.hupu.com/api/v2/bbs/topicThreads?topicId=${type}&page=1`;
+ const result = await get({ url, noCache });
+ const list = result.data.data.topicThreads;
+ return {
+ fromCache: result.fromCache,
+ updateTime: result.updateTime,
+ data: list.map((v: RouterType["hupu"]) => ({
+ id: v.tid,
+ title: v.title,
+ author: v.username,
+ hot: v.replies,
+ timestamp: null,
+ url: `https://bbs.hupu.com/${v.tid}.html`,
+ mobileUrl: v.url,
+ })),
+ };
+};
diff --git a/src/routes/sina-news.ts b/src/routes/sina-news.ts
new file mode 100644
index 0000000..720d9a7
--- /dev/null
+++ b/src/routes/sina-news.ts
@@ -0,0 +1,137 @@
+import type { RouterData, ListContext, Options } from "../types.js";
+import type { RouterType } from "../router.types.js";
+import { getTime, getCurrentDateTime } from "../utils/getTime.js";
+import { get } from "../utils/getData.js";
+
+// 榜单类别
+const listType = {
+ "1": {
+ name: "总排行",
+ www: "news",
+ params: "www_www_all_suda_suda",
+ },
+ "2": {
+ name: "视频排行",
+ www: "news",
+ params: "video_news_all_by_vv",
+ },
+ "3": {
+ name: "图片排行",
+ www: "news",
+ params: "total_slide_suda",
+ },
+ "4": {
+ name: "国内新闻",
+ www: "news",
+ params: "news_china_suda",
+ },
+ "5": {
+ name: "国际新闻",
+ www: "news",
+ params: "news_world_suda",
+ },
+ "6": {
+ name: "社会新闻",
+ www: "news",
+ params: "news_society_suda",
+ },
+ "7": {
+ name: "体育新闻",
+ www: "sports",
+ params: "sports_suda",
+ },
+ "8": {
+ name: "财经新闻",
+ www: "finance",
+ params: "finance_0_suda",
+ },
+ "9": {
+ name: "娱乐新闻",
+ www: "ent",
+ params: "ent_suda",
+ },
+ "10": {
+ name: "科技新闻",
+ www: "tech",
+ params: "tech_news_suda",
+ },
+ "11": {
+ name: "军事新闻",
+ www: "news",
+ params: "news_mil_suda",
+ },
+};
+
+export const handleRoute = async (c: ListContext, noCache: boolean) => {
+ const type = c.req.query("type") || "1";
+ const { fromCache, data, updateTime } = await getList({ type }, noCache);
+ const routeData: RouterData = {
+ name: "sina-news",
+ title: "新浪新闻",
+ type: listType[type].name,
+ params: {
+ type: {
+ name: "榜单分类",
+ type: Object.fromEntries(Object.entries(listType).map(([key, value]) => [key, value.name])),
+ },
+ },
+ link: "https://sinanews.sina.cn/",
+ total: data?.length || 0,
+ updateTime,
+ fromCache,
+ data,
+ };
+ return routeData;
+};
+
+// JSONP 处理
+const parseData = (data: string) => {
+ // 移除前后多余空白
+ if (!data) throw new Error("Input data is empty or invalid");
+ // 提取 JSON 字符串部分
+ const prefix = "var data = ";
+ if (!data.startsWith(prefix))
+ throw new Error("Input data does not start with the expected prefix");
+ let jsonString = data.slice(prefix.length).trim();
+ // 确保字符串以 ';' 结尾并移除它
+ if (jsonString.endsWith(";")) {
+ jsonString = jsonString.slice(0, -1).trim();
+ } else {
+ throw new Error("Input data does not end with a semicolon");
+ }
+ // 格式是否正确
+ if (jsonString.startsWith("{") && jsonString.endsWith("}")) {
+ // 解析为 JSON 对象
+ try {
+ const jsonData = JSON.parse(jsonString);
+ return jsonData;
+ } catch (error) {
+ throw new Error("Failed to parse JSON: " + error.message);
+ }
+ } else {
+ throw new Error("Invalid JSON format");
+ }
+};
+
+const getList = async (options: Options, noCache: boolean) => {
+ const { type } = options;
+ // 必要数据
+ const { params, www } = listType[type];
+ const { year, month, day } = getCurrentDateTime(true);
+ const url = `https://top.${www}.sina.com.cn/ws/GetTopDataList.php?top_type=day&top_cat=${params}&top_time=${year + month + day}&top_show_num=50`;
+ const result = await get({ url, noCache });
+ const list = parseData(result.data).data;
+ return {
+ fromCache: result.fromCache,
+ updateTime: result.updateTime,
+ data: list.map((v: RouterType["sina-news"]) => ({
+ id: v.id,
+ title: v.title,
+ author: v.media || null,
+ hot: parseFloat(v.top_num.replace(/,/g, "")),
+ timestamp: getTime(v.create_date + " " + v.create_time),
+ url: v.url,
+ mobileUrl: v.url,
+ })),
+ };
+};
diff --git a/src/routes/sina.ts b/src/routes/sina.ts
new file mode 100644
index 0000000..2f08654
--- /dev/null
+++ b/src/routes/sina.ts
@@ -0,0 +1,62 @@
+import type { RouterData, ListContext, Options } from "../types.js";
+import type { RouterType } from "../router.types.js";
+import { parseChineseNumber } from "../utils/getNum.js";
+import { get } from "../utils/getData.js";
+
+export const handleRoute = async (c: ListContext, noCache: boolean) => {
+ const type = c.req.query("type") || "1";
+ const { fromCache, data, updateTime } = await getList({ type }, noCache);
+ const routeData: RouterData = {
+ name: "sina",
+ title: "新浪网",
+ type: "热榜太多,一个就够",
+ params: {
+ type: {
+ name: "榜单分类",
+ type: {
+ all: "新浪热榜",
+ hotcmnt: "热议榜",
+ minivideo: "视频热榜",
+ ent: "娱乐热榜",
+ ai: "AI热榜",
+ auto: "汽车热榜",
+ mother: "育儿热榜",
+ fashion: "时尚热榜",
+ travel: "旅游热榜",
+ esg: "ESG热榜",
+ },
+ },
+ },
+ link: "https://sinanews.sina.cn/",
+ total: data?.length || 0,
+ updateTime,
+ fromCache,
+ data,
+ };
+ return routeData;
+};
+
+const getList = async (options: Options, noCache: boolean) => {
+ const { type } = options;
+ const url = `https://newsapp.sina.cn/api/hotlist?newsId=HB-1-snhs%2Ftop_news_list-${type}`;
+ const result = await get({ url, noCache });
+ const list = result.data.data.hotList;
+ return {
+ fromCache: result.fromCache,
+ updateTime: result.updateTime,
+ data: list.map((v: RouterType["sina"]) => {
+ const base = v.base;
+ const info = v.info;
+ return {
+ id: base.base.uniqueId,
+ title: info.title,
+ desc: null,
+ author: null,
+ timestamp: null,
+ hot: parseChineseNumber(info.hotValue),
+ url: base.base.url,
+ mobileUrl: base.base.url,
+ };
+ }),
+ };
+};
diff --git a/src/routes/sspai.ts b/src/routes/sspai.ts
index adf519d..58f1344 100644
--- a/src/routes/sspai.ts
+++ b/src/routes/sspai.ts
@@ -10,7 +10,7 @@ export const handleRoute = async (c: ListContext, noCache: boolean) => {
name: "sspai",
title: "少数派",
type: "热榜",
- parame: {
+ params: {
type: {
name: "分类",
type: ["热门文章", "应用推荐", "生活方式", "效率技巧", "少数派播客"],
diff --git a/src/routes/starrail.ts b/src/routes/starrail.ts
index ca0bfa0..c63ddda 100644
--- a/src/routes/starrail.ts
+++ b/src/routes/starrail.ts
@@ -10,7 +10,7 @@ export const handleRoute = async (c: ListContext, noCache: boolean) => {
name: "starrail",
title: "崩坏:星穹铁道",
type: "最新动态",
- parame: {
+ params: {
type: {
name: "榜单分类",
type: {
diff --git a/src/routes/v2ex.ts b/src/routes/v2ex.ts
index 706501b..e0f875b 100644
--- a/src/routes/v2ex.ts
+++ b/src/routes/v2ex.ts
@@ -9,7 +9,7 @@ export const handleRoute = async (c: ListContext, noCache: boolean) => {
name: "v2ex",
title: "V2EX",
type: "主题榜",
- parame: {
+ params: {
type: {
name: "榜单分类",
type: {
diff --git a/src/routes/weatheralarm.ts b/src/routes/weatheralarm.ts
index 2c4d749..83063de 100644
--- a/src/routes/weatheralarm.ts
+++ b/src/routes/weatheralarm.ts
@@ -10,7 +10,7 @@ export const handleRoute = async (c: ListContext, noCache: boolean) => {
name: "weatheralarm",
title: "中央气象台",
type: type || "全国气象预警",
- parame: {
+ params: {
province: {
name: "预警区域",
value: "省份名称( 例如:广东省 )",
diff --git a/src/types.d.ts b/src/types.d.ts
index 6102234..ca851f5 100644
--- a/src/types.d.ts
+++ b/src/types.d.ts
@@ -22,7 +22,7 @@ export type RouterData = {
title: string;
type: string;
description?: string;
- parame?: Record;
+ params?: Record;
total: number;
link?: string;
updateTime: string;
diff --git a/src/utils/getNum.ts b/src/utils/getNum.ts
new file mode 100644
index 0000000..1b2bad7
--- /dev/null
+++ b/src/utils/getNum.ts
@@ -0,0 +1,20 @@
+export const parseChineseNumber = (chineseNumber: string): number => {
+ // 单位对照表
+ const units: { [key: string]: number } = {
+ 亿: 1e8,
+ 万: 1e4,
+ 千: 1e3,
+ 百: 1e2,
+ };
+
+ // 遍历单位对照表
+ for (const unit in units) {
+ if (chineseNumber.includes(unit)) {
+ // 转换为数字
+ const numberPart = parseFloat(chineseNumber.replace(unit, ""));
+ return numberPart * units[unit];
+ }
+ }
+
+ return parseFloat(chineseNumber);
+};
diff --git a/src/utils/getTime.ts b/src/utils/getTime.ts
index 1df59c4..6ebe4d0 100644
--- a/src/utils/getTime.ts
+++ b/src/utils/getTime.ts
@@ -1,12 +1,12 @@
import dayjs from "dayjs";
interface CurrentDateTime {
- year: number;
- month: number;
- day: number;
- hour: number;
- minute: number;
- second: number;
+ year: string;
+ month: string;
+ day: string;
+ hour: string;
+ minute: string;
+ second: string;
}
export const getTime = (timeInput: string | number): number | null => {
try {
@@ -65,15 +65,18 @@ export const getTime = (timeInput: string | number): number | null => {
}
};
-export const getCurrentDateTime = (): CurrentDateTime => {
+export const getCurrentDateTime = (padZero: boolean = false): CurrentDateTime => {
const now = dayjs();
+ // 补零
+ const pad = (num: number): string => (num < 10 ? `0${num}` : `${num}`);
+
return {
- year: now.year(),
- month: now.month() + 1,
- day: now.date(),
- hour: now.hour(),
- minute: now.minute(),
- second: now.second(),
+ year: now.year().toString(),
+ month: padZero ? pad(now.month() + 1) : (now.month() + 1).toString(),
+ day: padZero ? pad(now.date()) : now.date().toString(),
+ hour: padZero ? pad(now.hour()) : now.hour().toString(),
+ minute: padZero ? pad(now.minute()) : now.minute().toString(),
+ second: padZero ? pad(now.second()) : now.second().toString(),
};
};