feat: 新增部分接口

This commit is contained in:
imsyy 2024-01-02 11:20:50 +08:00
parent fa8fb5a47f
commit 2b91f3f32b
No known key found for this signature in database
GPG Key ID: 5D959EAB73CA095D
12 changed files with 754 additions and 652 deletions

View File

@ -16,33 +16,70 @@
> 🟠 可能失效 > 🟠 可能失效
> ❌ 无法使用 > ❌ 无法使用
| **站点** | **类别** | **调用名称** | **状态** | | **站点** | **类别** | **调用名称** | **状态** |
| ------------ | -------- | ------------------- | -------- | | ------------ | -------- | --------------------- | -------- |
| 哔哩哔哩 | 热门榜 | bilibili | 🟢 | | 哔哩哔哩 | 热门榜 | bilibili | 🟢 |
| 微博 | 热搜榜 | weibo | 🟢 | | 微博 | 热搜榜 | weibo | 🟢 |
| 知乎 | 热榜 | zhihu | 🟢 | | 知乎 | 热榜 | zhihu | 🟢 |
| 百度 | 热搜榜 | baidu | 🟢 | | 百度 | 热搜榜 | baidu | 🟢 |
| 抖音 | 热点榜 | douyin / douyin_new | 🟢 | | 抖音 | 热点榜 | douyin / douyin_new | 🟢 |
| 抖音 | 热歌榜 | douyin_music | 🟢 | | 抖音 | 热歌榜 | douyin_music | 🟢 |
| 豆瓣 | 新片榜 | douban_new | 🟢 | | 豆瓣 | 新片榜 | douban_new | 🟢 |
| 百度贴吧 | 热议榜 | tieba | 🟢 | | 豆瓣讨论小组 | 讨论精选 | douban_group | 🟢 |
| 少数派 | 热榜 | sspai | 🟢 | | 百度贴吧 | 热议榜 | tieba | 🟢 |
| IT 之家 | 热榜 | ithome | 🟠 | | 少数派 | 热榜 | sspai | 🟢 |
| 澎湃新闻 | 热榜 | thepaper | 🟢 | | IT 之家 | 热榜 | ithome | 🟠 |
| 今日头条 | 热榜 | toutiao | 🟢 | | 澎湃新闻 | 热榜 | thepaper | 🟢 |
| 36 氪 | 热榜 | 36kr | 🟢 | | 今日头条 | 热榜 | toutiao | 🟢 |
| 稀土掘金 | 热榜 | juejin | 🟢 | | 36 氪 | 热榜 | 36kr | 🟢 |
| 腾讯新闻 | 热点榜 | newsqq | 🟢 | | 稀土掘金 | 热榜 | juejin | 🟢 |
| 网易新闻 | 热点榜 | netease | 🟢 | | 腾讯新闻 | 热点榜 | newsqq | 🟢 |
| 英雄联盟 | 更新公告 | lol | 🟢 | | 网易新闻 | 热点榜 | netease | 🟢 |
| 原神 | 最新消息 | genshin | 🟢 | | 英雄联盟 | 更新公告 | lol | 🟢 |
| 微信读书 | 飙升榜 | weread | 🟢 | | 原神 | 最新消息 | genshin | 🟢 |
| 快手 | 热榜 | kuaishou | 🟢 | | 微信读书 | 飙升榜 | weread | 🟢 |
| Github | Trending | github | 🟢 | | 快手 | 热榜 | kuaishou | 🟢 |
| 历史上的今天 | 指定日期 | calendar | 🟢 | | 网易云音乐 | 排行榜 | netease_music_toplist | 🟢 |
| QQ音乐 | 排行榜 | qq_music_toplist | 🟢 |
| NGA | 热帖 | ngabbs | 🟢 |
| Github | Trending | github | 🟢 |
| V2EX | 热榜 | v2ex | 🟠 |
| 历史上的今天 | 指定日期 | calendar | 🟢 |
### 特殊接口说明 ### 特殊接口说明
#### 网易云音乐
调用网易云音乐排行榜需要传入指定榜单类别
| 参数名 | 参数值 | 说明 |
| ------ | ------ | ------ |
| type | 1 | 飙升榜 |
| type | 2 | 新歌榜 |
| type | 3 | 原创榜 |
| type | 4 | 热歌榜 |
```http
GET https://example.com/netease_music_toplist?type=1
```
#### QQ音乐
调用QQ音乐排行榜需要传入指定榜单类别
| 参数名 | 参数值 | 说明 |
| ------ | ------ | ---------------- |
| type | 1 | 飙升榜 |
| type | 2 | 热歌榜 |
| type | 3 | 新歌榜 |
| type | 4 | 流行指数榜 |
| type | 5 | 腾讯音乐人原创榜 |
| type | 6 | 听歌识曲榜 |
```http
GET https://example.com/qq_music_toplist?type=1
```
#### 获取全部接口信息 #### 获取全部接口信息
获取除了下方特殊接口外的全部接口列表 获取除了下方特殊接口外的全部接口列表
@ -62,10 +99,10 @@ GET https://example.com/calendar/date?month=06&day=01
## 部署 ## 部署
```bash ```bash
// 安装依赖 # 安装依赖
pnpm install pnpm install
// 运行 # 运行
pnpm start pnpm start
``` ```
@ -76,18 +113,18 @@ pnpm start
### 本地构建 ### 本地构建
```bash ```bash
// 构建 # 构建
docker build -t dailyhot-api . docker build -t dailyhot-api .
// 运行 # 运行
docker run -p 6688:6688 -d dailyhot-api docker run -p 6688:6688 -d dailyhot-api
``` ```
### 在线部署 ### 在线部署
```bash ```bash
// 拉取 # 拉取
docker pull imsyy/dailyhot-api:1.0.5 docker pull imsyy/dailyhot-api:1.0.5
// 运行 # 运行
docker run -p 6688:6688 -d imsyy/dailyhot-api:1.0.5 docker run -p 6688:6688 -d imsyy/dailyhot-api:1.0.5
``` ```

View File

@ -1,7 +1,7 @@
{ {
"name": "dailyhot_api", "name": "dailyhot_api",
"version": "1.0.5", "version": "1.0.7",
"description": "一个今日热榜", "description": "An api on Today's Hot list",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
"format": "prettier --write .", "format": "prettier --write .",

View File

@ -1,10 +1,10 @@
/** /**
* @author: x-dr * @author: x-dr
* @date: 2023年12月26日 * @date: 2023-12-26
* @tags: [豆瓣讨论精选] * @customEditors: imsyy
* @lastEditTime: 2024-01-02
*/ */
const Router = require("koa-router"); const Router = require("koa-router");
const doubanGroupNewRouter = new Router(); const doubanGroupNewRouter = new Router();
const axios = require("axios"); const axios = require("axios");
@ -13,13 +13,11 @@ const { get, set, del } = require("../utils/cacheData");
// 接口信息 // 接口信息
const routerInfo = { const routerInfo = {
name: "doubangroup", name: "douban_group",
title: "豆瓣讨论小组", title: "豆瓣讨论小组",
subtitle: "精选", subtitle: "精选",
}; };
// 缓存键名 // 缓存键名
const cacheKey = "doubanGroupData"; const cacheKey = "doubanGroupData";
@ -29,12 +27,13 @@ let updateTime = new Date().toISOString();
const url = "https://www.douban.com/group/explore"; const url = "https://www.douban.com/group/explore";
const headers = { const headers = {
"accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7", accept:
"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
"accept-language": "zh-CN,zh;q=0.9,en;q=0.8", "accept-language": "zh-CN,zh;q=0.9,en;q=0.8",
"cache-control": "max-age=0", "cache-control": "max-age=0",
"sec-ch-ua": "\"Not_A Brand\";v=\"8\", \"Chromium\";v=\"120\", \"Google Chrome\";v=\"120\"", "sec-ch-ua": '"Not_A Brand";v="8", "Chromium";v="120", "Google Chrome";v="120"',
"sec-ch-ua-mobile": "?0", "sec-ch-ua-mobile": "?0",
"sec-ch-ua-platform": "\"Windows\"", "sec-ch-ua-platform": '"Windows"',
"sec-fetch-dest": "document", "sec-fetch-dest": "document",
"sec-fetch-mode": "navigate", "sec-fetch-mode": "navigate",
"sec-fetch-site": "none", "sec-fetch-site": "none",
@ -43,27 +42,31 @@ const headers = {
// "cookie": "bid=lLpb6D1JLuw; douban-fav-remind=1; _pk_id.100001.8cb4=e7d91ae46530fd1d.1680518589.; ll=\"118281\"; _pk_ref.100001.8cb4=%5B%22%22%2C%22%22%2C1703602972%2C%22http%3A%2F%2Fnew.xianbao.fun%2F%22%5D; _pk_ses.100001.8cb4=1; ap_v=0,6.0" // "cookie": "bid=lLpb6D1JLuw; douban-fav-remind=1; _pk_id.100001.8cb4=e7d91ae46530fd1d.1680518589.; ll=\"118281\"; _pk_ref.100001.8cb4=%5B%22%22%2C%22%22%2C1703602972%2C%22http%3A%2F%2Fnew.xianbao.fun%2F%22%5D; _pk_ses.100001.8cb4=1; ap_v=0,6.0"
}; };
// 数据处理 // 数据处理
const getData = (data) => { const getData = (data) => {
if (!data) return false; if (!data) return false;
const dataList = []; const dataList = [];
const $ = cheerio.load(data); const $ = cheerio.load(data);
try { try {
$(`.channel-item`).each((i, e) => { $(`.channel-item`).each((i, e) => {
// console.log($(e).html()); // console.log($(e).html());
const item= cheerio.load($(e).html()) const item = cheerio.load($(e).html());
const title = item("h3").text().replace(/(^\s*)|(\s*$)/g, "") const title = item("h3")
const url = item("h3 a").attr('href') .text()
const hot= item('div[class="likes"]').text().replace(/(^\s*)|(\s*$)/g, "") .replace(/(^\s*)|(\s*$)/g, "");
const desc = item('div[class="block"]').text().replace(/(^\s*)|(\s*$|\n)/g, "") const url = item("h3 a").attr("href");
const source = item('div[class="source"] a').text().replace(/(^\s*)|(\s*$)/g, "") const hot = item('div[class="likes"]')
.text()
.replace(/(^\s*)|(\s*$)/g, "");
const desc = item('div[class="block"]')
.text()
.replace(/(^\s*)|(\s*$|\n)/g, "");
const source = item('div[class="source"] a')
.text()
.replace(/(^\s*)|(\s*$)/g, "");
// const excerpt = item('.channel-item-desc').text().replace(/(^\s*)|(\s*$)/g, "") // const excerpt = item('.channel-item-desc').text().replace(/(^\s*)|(\s*$)/g, "")
// console.log(title); // console.log(title);
// console.log(url); // console.log(url);
dataList.push({ dataList.push({
title: title, title: title,
desc: desc, desc: desc,
@ -71,9 +74,7 @@ const getData = (data) => {
mobileUrl: url, mobileUrl: url,
hot: hot, hot: hot,
source: source, source: source,
});
});
}); });
return dataList; return dataList;
} catch (error) { } catch (error) {
@ -82,10 +83,8 @@ const getData = (data) => {
} }
}; };
// trending // trending
doubanGroupNewRouter.get("/doubangroup", async (ctx) => { doubanGroupNewRouter.get("/douban_group", async (ctx) => {
console.log("获取豆瓣讨论小组精选"); console.log("获取豆瓣讨论小组精选");
try { try {
// 从缓存中获取数据 // 从缓存中获取数据
@ -130,11 +129,8 @@ doubanGroupNewRouter.get("/doubangroup", async (ctx) => {
} }
}); });
// 豆瓣新片榜 - 获取最新数据 // 豆瓣新片榜 - 获取最新数据
doubanGroupNewRouter.get("/doubangroup/new", async (ctx) => { doubanGroupNewRouter.get("/douban_group/new", async (ctx) => {
console.log("获取豆瓣讨论小组精选 - 最新数据"); console.log("获取豆瓣讨论小组精选 - 最新数据");
try { try {
// 从服务器拉取最新数据 // 从服务器拉取最新数据

View File

@ -13,7 +13,7 @@ const { get, set, del } = require("../utils/cacheData");
// 接口信息 // 接口信息
const routerInfo = { const routerInfo = {
name: "douban", name: "douban_new",
title: "豆瓣", title: "豆瓣",
subtitle: "新片榜", subtitle: "新片榜",
}; };

View File

@ -12,7 +12,7 @@ const { get, set, del } = require("../utils/cacheData");
// 接口信息 // 接口信息
const routerInfo = { const routerInfo = {
name: "douyin", name: "douyin_music",
title: "抖音", title: "抖音",
subtitle: "热歌榜", subtitle: "热歌榜",
}; };

View File

@ -36,7 +36,7 @@ const getData = (data) => {
const dataList = []; const dataList = [];
const $ = cheerio.load(data); const $ = cheerio.load(data);
try { try {
$(".rank-name").each(function () { $(".rank-name").each(() => {
const type = $(this).data("rank-type"); const type = $(this).data("rank-type");
const newListHtml = $(this).next(".rank-box").html(); const newListHtml = $(this).next(".rank-box").html();
cheerio cheerio

View File

@ -1,169 +0,0 @@
/**
* @author: x-dr
* @date: 2023年12月27日
* @tags: [网易云音乐飙升榜]
*/
const URL = require('url');
const Router = require("koa-router");
const neteaseMusicRouter = new Router();
const axios = require("axios");
const cheerio = require("cheerio");
const { get, set, del } = require("../utils/cacheData");
// 接口信息
const routerInfo = {
name: "neteasemusic",
title: "网易云音乐",
subtitle: "飙升榜",
};
// 缓存键名
const cacheKey = "neteasemusicData";
// 调用时间
let updateTime = new Date().toISOString();
const url = "https://music.163.com/discover/toplist?id=19723756";
// const url = "https://music.163.com/#/discover/toplist?id=19723756";
const headers = {
'authority': 'music.163.com',
'referer': 'https://music.163.com/',
};
// 数据处理
const getData = (data) => {
if (!data) return false;
const dataList = [];
const $ = cheerio.load(data);
try {
$('.m-sgitem').each((i, e) => {
const urlString = $(e).attr('href')
const parsedUrl = URL.parse(urlString, true);
const urlidValue = parsedUrl.query.id;
const item = cheerio.load($(e).html())
const author = item('div[class="f-thide sginfo"]').text().replace(/(^\s*)|(\s*$)/g, "")
const title = item('div[class="f-thide sgtl"]').text().replace(/(^\s*)|(\s*$)/g, "")
dataList.push({
title: title,
desc: author,
url: `https://music.163.com/#/song?id=${urlidValue}`,
mobileUrl: `https://music.163.com/m/song?id=${urlidValue}`,
});
});
return dataList;
} catch (error) {
console.error("数据处理出错" + error);
return false;
}
};
//
neteaseMusicRouter.get("/neteasemusic", async (ctx) => {
console.log("获取neteasemusic ");
try {
// 从缓存中获取数据
let data = await get(cacheKey);
const from = data ? "cache" : "server";
if (!data) {
// 如果缓存中不存在数据
console.log("从服务端重新neteasemusic ");
// 从服务器拉取数据
const response = await axios.get(url, { headers });
// console.log(response.data);
data = getData(response.data);
updateTime = new Date().toISOString();
if (!data) {
ctx.body = {
code: 500,
...routerInfo,
message: "获取失败",
};
return false;
}
// 将数据写入缓存
await set(cacheKey, data);
}
ctx.body = {
code: 200,
message: "获取成功",
...routerInfo,
from,
total: data.length,
updateTime,
data,
};
} catch (error) {
console.error(error);
ctx.body = {
code: 500,
...routerInfo,
message: "获取失败",
};
}
});
// 豆瓣新片榜 - 获取最新数据
neteaseMusicRouter.get("/neteasemusic/new", async (ctx) => {
console.log("获取neteasemusic - 最新数据");
try {
// 从服务器拉取最新数据
const response = await axios.get(url, { headers });
const newData = getData(response.data);
updateTime = new Date().toISOString();
console.log("从服务端重新neteasemusic ");
// 返回最新数据
ctx.body = {
code: 200,
message: "获取成功",
...routerInfo,
updateTime,
total: newData.length,
data: newData,
};
// 删除旧数据
await del(cacheKey);
// 将最新数据写入缓存
await set(cacheKey, newData);
} catch (error) {
// 如果拉取最新数据失败,尝试从缓存中获取数据
console.error(error);
const cachedData = await get(cacheKey);
if (cachedData) {
ctx.body = {
code: 200,
message: "获取成功",
...routerInfo,
total: cachedData.length,
updateTime,
data: cachedData,
};
} else {
// 如果缓存中也没有数据,则返回错误信息
ctx.body = {
code: 500,
...routerInfo,
message: "获取失败",
};
}
}
});
neteaseMusicRouter.info = routerInfo;
module.exports = neteaseMusicRouter;

View File

@ -0,0 +1,198 @@
/**
* @author: x-dr
* @date: 2023-12-27
* @customEditors: imsyy
* @lastEditTime: 2024-01-02
*/
const URL = require("url");
const Router = require("koa-router");
const neteaseMusicRouter = new Router();
const axios = require("axios");
const cheerio = require("cheerio");
const { get, set, del } = require("../utils/cacheData");
// 接口信息
const routerInfo = {
name: "netease_music_toplist",
title: "网易云音乐",
subtitle: "排行榜",
};
// 缓存键名
const cacheKey = "neteasemusicToplistData";
// 调用时间
let updateTime = new Date().toISOString();
const url = "https://music.163.com/discover/toplist?id=";
const headers = {
authority: "music.163.com",
referer: "https://music.163.com/",
};
// 榜单类别
const listType = {
1: {
id: 19723756,
name: "飙升榜",
},
2: {
id: 3779629,
name: "新歌榜",
},
3: {
id: 2884035,
name: "原创榜",
},
4: {
id: 3778678,
name: "热歌榜",
},
};
// 数据处理
const getData = (data) => {
if (!data) return false;
const dataList = [];
const $ = cheerio.load(data);
try {
$(".m-sgitem").each((i, e) => {
const urlString = $(e).attr("href");
const parsedUrl = URL.parse(urlString, true);
const urlidValue = parsedUrl.query.id;
const item = cheerio.load($(e).html());
const author = item('div[class="f-thide sginfo"]')
.text()
.replace(/(^\s*)|(\s*$)/g, "");
const title = item('div[class="f-thide sgtl"]')
.text()
.replace(/(^\s*)|(\s*$)/g, "");
dataList.push({
title: title,
desc: author,
url: `https://music.163.com/#/song?id=${urlidValue}`,
mobileUrl: `https://music.163.com/m/song?id=${urlidValue}`,
});
});
return dataList;
} catch (error) {
console.error("数据处理出错" + error);
return false;
}
};
// 网易云音乐排行榜
neteaseMusicRouter.get("/netease_music_toplist", async (ctx) => {
console.log("获取网易云音乐排行榜");
try {
// 获取参数
const { type } = ctx.query;
const typeNum = Number(type);
if (!typeNum || typeNum > 4 || typeNum < 1) {
ctx.body = { code: 400, ...routerInfo, message: "参数不完整或不正确" };
return false;
}
// 更改名称
routerInfo.subtitle = listType[typeNum].name;
// 从缓存中获取数据
let data = await get(cacheKey + listType[typeNum].id);
const from = data ? "cache" : "server";
if (!data) {
// 如果缓存中不存在数据
console.log("从服务端重新获取网易云音乐排行榜");
// 从服务器拉取数据
const response = await axios.get(url + listType[typeNum].id, { headers });
// console.log(response.data);
data = getData(response.data);
updateTime = new Date().toISOString();
if (!data) {
ctx.body = {
code: 500,
...routerInfo,
message: "获取失败",
};
return false;
}
// 将数据写入缓存
await set(cacheKey + listType[typeNum].id, data);
}
ctx.body = {
code: 200,
message: "获取成功",
...routerInfo,
from,
total: data.length,
updateTime,
data,
};
} catch (error) {
console.error(error);
ctx.body = {
code: 500,
...routerInfo,
message: "获取失败",
};
}
});
// 网易云音乐排行榜 - 获取最新数据
neteaseMusicRouter.get("/netease_music_toplist/new", async (ctx) => {
console.log("获取网易云音乐排行榜 - 最新数据");
try {
// 获取参数
const { type } = ctx.query;
const typeNum = Number(type);
if (!typeNum || typeNum > 4 || typeNum < 1) {
ctx.body = { code: 400, ...routerInfo, message: "参数不完整或不正确" };
return false;
}
// 更改名称
routerInfo.subtitle = listType[typeNum].name;
// 从服务器拉取最新数据
const response = await axios.get(url + listType[typeNum].id, { headers });
const newData = getData(response.data);
updateTime = new Date().toISOString();
console.log("从服务端重新获取网易云音乐排行榜");
// 返回最新数据
ctx.body = {
code: 200,
message: "获取成功",
...routerInfo,
updateTime,
total: newData.length,
data: newData,
};
// 删除旧数据
await del(cacheKey + listType[typeNum].id);
// 将最新数据写入缓存
await set(cacheKey + listType[typeNum].id, newData);
} catch (error) {
// 如果拉取最新数据失败,尝试从缓存中获取数据
console.error(error);
const cachedData = await get(cacheKey);
if (cachedData) {
ctx.body = {
code: 200,
message: "获取成功",
...routerInfo,
total: cachedData.length,
updateTime,
data: cachedData,
};
} else {
// 如果缓存中也没有数据,则返回错误信息
ctx.body = {
code: 500,
...routerInfo,
message: "获取失败",
};
}
}
});
neteaseMusicRouter.info = routerInfo;
module.exports = neteaseMusicRouter;

View File

@ -1,7 +1,8 @@
/** /**
* @author: x-dr * @author: x-dr
* @date: 2023年12月25日 * @date: 2023-12-25
* @tags: [NGA论坛热帖] * @customEditors: imsyy
* @lastEditTime: 2024-01-02
*/ */
const Router = require("koa-router"); const Router = require("koa-router");
@ -9,7 +10,6 @@ const ngabbsRouter = new Router();
const axios = require("axios"); const axios = require("axios");
const { get, set, del } = require("../utils/cacheData"); const { get, set, del } = require("../utils/cacheData");
// 接口信息 // 接口信息
const routerInfo = { name: "ngabbs", title: "NGA", subtitle: "论坛热帖" }; const routerInfo = { name: "ngabbs", title: "NGA", subtitle: "论坛热帖" };
@ -19,136 +19,136 @@ const cacheKey = "ngabbsData";
// 调用时间 // 调用时间
let updateTime = new Date().toISOString(); let updateTime = new Date().toISOString();
const url = 'https://ngabbs.com/nuke.php?__lib=load_topic&__act=load_topic_reply_ladder2&opt=1&all=1'; const url =
"https://ngabbs.com/nuke.php?__lib=load_topic&__act=load_topic_reply_ladder2&opt=1&all=1";
const headers = { const headers = {
'Host': 'ngabbs.com', Host: "ngabbs.com",
'Content-Type': 'application/x-www-form-urlencoded', "Content-Type": "application/x-www-form-urlencoded",
'Accept': '*/*', Accept: "*/*",
'Accept-Encoding': 'gzip, deflate, br', "Accept-Encoding": "gzip, deflate, br",
'Connection': 'keep-alive', Connection: "keep-alive",
'Content-Length': '11', "Content-Length": "11",
'User-Agent': 'NGA/7.3.1 (iPhone; iOS 17.2.1; Scale/3.00)', "User-Agent": "NGA/7.3.1 (iPhone; iOS 17.2.1; Scale/3.00)",
'Accept-Language': 'zh-Hans-CN;q=1', "Accept-Language": "zh-Hans-CN;q=1",
'Referer': 'https://ngabbs.com/', Referer: "https://ngabbs.com/",
'X-User-Agent': 'NGA_skull/7.3.1(iPhone13,2;iOS 17.2.1)', "X-User-Agent": "NGA_skull/7.3.1(iPhone13,2;iOS 17.2.1)",
}; };
const postData = { '__output': '14' }; const postData = { __output: "14" };
// 数据处理 // 数据处理
const getData = (data) => { const getData = (data) => {
if (!data) return []; if (!data) return [];
const dataList = []; const dataList = [];
try { try {
const result = data.result[0];
const result = data.result[0]; result.forEach((result) => {
result.forEach((result) => { dataList.push({
dataList.push({ author: result.author,
author: result.author, desc: result.subject,
desc: result.subject, parent: result.parent["2"],
parent: result.parent['2'], tid: result.tid,
tid: result.tid, comments: Number(result.replies),
comments: Number(result.replies), url: `https://bbs.nga.cn/read.php?tid=${result.tid}`,
url: `https://bbs.nga.cn/read.php?tid=${result.tid}`, mobileUrl: `https://bbs.nga.cn/read.php?tid=${result.tid}`,
mobileUrl: `https://bbs.nga.cn/read.php?tid=${result.tid}`, });
});
}); return dataList;
}); } catch (error) {
return dataList; console.error("数据处理出错" + error);
} catch (error) { return false;
console.error("数据处理出错" + error); }
return false;
}
}; };
// NGA论坛热帖 // NGA论坛热帖
ngabbsRouter.get("/ngabbs", async (ctx) => { ngabbsRouter.get("/ngabbs", async (ctx) => {
console.log("获取NGA论坛热帖"); console.log("获取NGA论坛热帖");
try { try {
// 从缓存中获取数据 // 从缓存中获取数据
let data = await get(cacheKey); let data = await get(cacheKey);
const from = data ? "cache" : "server"; const from = data ? "cache" : "server";
if (!data) { if (!data) {
// 如果缓存中不存在数据 // 如果缓存中不存在数据
console.log("从服务端重新获取NGA论坛热帖"); console.log("从服务端重新获取NGA论坛热帖");
// 从服务器拉取数据 // 从服务器拉取数据
const response = await axios.post(url, postData, { headers }); const response = await axios.post(url, postData, { headers });
data = getData(response.data); data = getData(response.data);
updateTime = new Date().toISOString(); updateTime = new Date().toISOString();
if (!data) { if (!data) {
ctx.body = {
code: 500,
...routerInfo,
message: "获取失败",
};
return false;
}
// 将数据写入缓存
await set(cacheKey, data);
}
ctx.body = { ctx.body = {
code: 200, code: 500,
message: "获取成功", ...routerInfo,
...routerInfo, message: "获取失败",
from,
total: data.length,
updateTime,
data,
};
} catch (error) {
console.error(error);
ctx.body = {
code: 500,
message: "获取失败",
}; };
return false;
}
// 将数据写入缓存
await set(cacheKey, data);
} }
ctx.body = {
code: 200,
message: "获取成功",
...routerInfo,
from,
total: data.length,
updateTime,
data,
};
} catch (error) {
console.error(error);
ctx.body = {
code: 500,
message: "获取失败",
};
}
}); });
// NGA论坛热帖 - 获取最新数据 // NGA论坛热帖 - 获取最新数据
ngabbsRouter.get("/ngabbs/new", async (ctx) => { ngabbsRouter.get("/ngabbs/new", async (ctx) => {
console.log("获取NGA论坛热帖 - 最新数据"); console.log("获取NGA论坛热帖 - 最新数据");
try { try {
// 从服务器拉取最新数据 // 从服务器拉取最新数据
const response = await axios.post(url, postData, { headers }); const response = await axios.post(url, postData, { headers });
const newData = getData(response.data); const newData = getData(response.data);
updateTime = new Date().toISOString(); updateTime = new Date().toISOString();
console.log("从服务端重新获取NGA论坛热帖"); console.log("从服务端重新获取NGA论坛热帖");
// 返回最新数据 // 返回最新数据
ctx.body = { ctx.body = {
code: 200, code: 200,
message: "获取成功", message: "获取成功",
...routerInfo, ...routerInfo,
total: newData.length, total: newData.length,
updateTime, updateTime,
data: newData, data: newData,
}; };
// 删除旧数据 // 删除旧数据
await del(cacheKey); await del(cacheKey);
// 将最新数据写入缓存 // 将最新数据写入缓存
await set(cacheKey, newData); await set(cacheKey, newData);
} catch (error) { } catch (error) {
// 如果拉取最新数据失败,尝试从缓存中获取数据 // 如果拉取最新数据失败,尝试从缓存中获取数据
console.error(error); console.error(error);
const cachedData = await get(cacheKey); const cachedData = await get(cacheKey);
if (cachedData) { if (cachedData) {
ctx.body = { ctx.body = {
code: 200, code: 200,
message: "获取成功", message: "获取成功",
...routerInfo, ...routerInfo,
total: cachedData.length, total: cachedData.length,
updateTime, updateTime,
data: cachedData, data: cachedData,
}; };
} else { } else {
// 如果缓存中也没有数据,则返回错误信息 // 如果缓存中也没有数据,则返回错误信息
ctx.body = { ctx.body = {
code: 500, code: 500,
...routerInfo, ...routerInfo,
message: "获取失败", message: "获取失败",
}; };
}
} }
}
}); });
ngabbsRouter.info = routerInfo; ngabbsRouter.info = routerInfo;

View File

@ -1,170 +0,0 @@
/**
* @author: x-dr
* @date: 2023年12月27日
* @tags: [QQ音乐热歌榜]
*/
// const fs = require("fs");
const Router = require("koa-router");
const qqMusicRouter = new Router();
const axios = require("axios");
const cheerio = require("cheerio");
const { get, set, del } = require("../utils/cacheData");
// 接口信息
const routerInfo = {
name: "qqmusic",
title: "QQ音乐",
subtitle: "热歌榜",
};
// 缓存键名
const cacheKey = "qqmusicData";
// 调用时间
let updateTime = new Date().toISOString();
const url = "https://y.qq.com/n/ryqq/toplist/26";
const headers = {
'authority': 'y.qq.com',
'referer': 'https://www.google.com/',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
};
// 数据处理
const getData = (data) => {
if (!data) return false;
const dataList = [];
const $ = cheerio.load(data);
// fs.writeFileSync('qq.html', $.html());
try {
$('.songlist__item').each((i, e) => {
const item = cheerio.load($(e).html())
const title = item('a[class="songlist__cover"]').attr('title')
const urlPath = item('a[class="songlist__cover"]').attr('href')
const author = item('div[class="songlist__artist"]').text().replace(/(^\s*)|(\s*$)/g, "")
const songtime = item('div[class="songlist__time"]').text().replace(/(^\s*)|(\s*$)/g, "")
// const title = item('div[class="f-thide sgtl"]').text().replace(/(^\s*)|(\s*$)/g, "")
dataList.push({
title: title,
desc: author,
songtime: songtime,
url: `https://y.qq.com${urlPath}`,
mobileUrl: `https://y.qq.com${urlPath}`,
});
});
return dataList;
} catch (error) {
console.error("数据处理出错" + error);
return false;
}
};
//
qqMusicRouter.get("/qqmusic", async (ctx) => {
console.log("获取QQ音乐热歌榜 ");
try {
// 从缓存中获取数据
let data = await get(cacheKey);
const from = data ? "cache" : "server";
if (!data) {
// 如果缓存中不存在数据
console.log("从服务端重新QQ音乐热歌榜 ");
// 从服务器拉取数据
const response = await axios.get(url, { headers });
// console.log(response.data);
data = getData(response.data);
updateTime = new Date().toISOString();
if (!data) {
ctx.body = {
code: 500,
...routerInfo,
message: "获取失败",
};
return false;
}
// 将数据写入缓存
await set(cacheKey, data);
}
ctx.body = {
code: 200,
message: "获取成功",
...routerInfo,
from,
total: data.length,
updateTime,
data,
};
} catch (error) {
console.error(error);
ctx.body = {
code: 500,
...routerInfo,
message: "获取失败",
};
}
});
// 豆瓣新片榜 - 获取最新数据
qqMusicRouter.get("/qqmusic/new", async (ctx) => {
console.log("获取QQ音乐热歌榜 - 最新数据");
try {
// 从服务器拉取最新数据
const response = await axios.get(url, { headers });
const newData = getData(response.data);
updateTime = new Date().toISOString();
console.log("从服务端重新QQ音乐热歌榜 ");
// 返回最新数据
ctx.body = {
code: 200,
message: "获取成功",
...routerInfo,
updateTime,
total: newData.length,
data: newData,
};
// 删除旧数据
await del(cacheKey);
// 将最新数据写入缓存
await set(cacheKey, newData);
} catch (error) {
// 如果拉取最新数据失败,尝试从缓存中获取数据
console.error(error);
const cachedData = await get(cacheKey);
if (cachedData) {
ctx.body = {
code: 200,
message: "获取成功",
...routerInfo,
total: cachedData.length,
updateTime,
data: cachedData,
};
} else {
// 如果缓存中也没有数据,则返回错误信息
ctx.body = {
code: 500,
...routerInfo,
message: "获取失败",
};
}
}
});
qqMusicRouter.info = routerInfo;
module.exports = qqMusicRouter;

211
routes/qq_music_toplist.js Normal file
View File

@ -0,0 +1,211 @@
/**
* @author: x-dr
* @date: 2023-12-27
* @customEditors: imsyy
* @lastEditTime: 2024-01-02
*/
// const fs = require("fs");
const Router = require("koa-router");
const qqMusicRouter = new Router();
const axios = require("axios");
const cheerio = require("cheerio");
const { get, set, del } = require("../utils/cacheData");
// 接口信息
const routerInfo = {
name: "qq_music_toplist",
title: "QQ音乐",
subtitle: "排行榜",
};
// 缓存键名
const cacheKey = "qqmusicData";
// 调用时间
let updateTime = new Date().toISOString();
const url = "https://y.qq.com/n/ryqq/toplist/";
const headers = {
authority: "y.qq.com",
referer: "https://www.google.com/",
"user-agent":
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
};
// 榜单类别
const listType = {
1: {
id: 62,
name: "飙升榜",
},
2: {
id: 26,
name: "热歌榜",
},
3: {
id: 27,
name: "新歌榜",
},
4: {
id: 4,
name: "流行指数榜",
},
5: {
id: 52,
name: "腾讯音乐人原创榜",
},
6: {
id: 67,
name: "听歌识曲榜",
},
};
// 数据处理
const getData = (data) => {
if (!data) return false;
const dataList = [];
const $ = cheerio.load(data);
// fs.writeFileSync('qq.html', $.html());
try {
$(".songlist__item").each((i, e) => {
const item = cheerio.load($(e).html());
const title = item('a[class="songlist__cover"]').attr("title");
const urlPath = item('a[class="songlist__cover"]').attr("href");
const author = item('div[class="songlist__artist"]')
.text()
.replace(/(^\s*)|(\s*$)/g, "");
const songtime = item('div[class="songlist__time"]')
.text()
.replace(/(^\s*)|(\s*$)/g, "");
// const title = item('div[class="f-thide sgtl"]').text().replace(/(^\s*)|(\s*$)/g, "")
dataList.push({
title: title,
desc: author,
songtime: songtime,
url: `https://y.qq.com${urlPath}`,
mobileUrl: `https://y.qq.com${urlPath}`,
});
});
return dataList;
} catch (error) {
console.error("数据处理出错" + error);
return false;
}
};
// 获取QQ音乐排行榜
qqMusicRouter.get("/qq_music_toplist", async (ctx) => {
console.log("获取QQ音乐排行榜");
try {
// 获取参数
const { type } = ctx.query;
const typeNum = Number(type);
if (!typeNum || typeNum > 6 || typeNum < 1) {
ctx.body = { code: 400, ...routerInfo, message: "参数不完整或不正确" };
return false;
}
// 更改名称
routerInfo.subtitle = listType[typeNum].name;
// 从缓存中获取数据
let data = await get(cacheKey + listType[typeNum].id);
const from = data ? "cache" : "server";
if (!data) {
// 如果缓存中不存在数据
console.log("从服务端重新QQ音乐排行榜");
// 从服务器拉取数据
const response = await axios.get(url + listType[typeNum].id, { headers });
// console.log(response.data);
data = getData(response.data);
updateTime = new Date().toISOString();
if (!data) {
ctx.body = {
code: 500,
...routerInfo,
message: "获取失败",
};
return false;
}
// 将数据写入缓存
await set(cacheKey + listType[typeNum].id, data);
}
ctx.body = {
code: 200,
message: "获取成功",
...routerInfo,
from,
total: data.length,
updateTime,
data,
};
} catch (error) {
console.error(error);
ctx.body = {
code: 500,
...routerInfo,
message: "获取失败",
};
}
});
// 获取QQ音乐排行榜 - 获取最新数据
qqMusicRouter.get("/qq_music_toplist/new", async (ctx) => {
console.log("获取QQ音乐排行榜 - 最新数据");
try {
// 获取参数
const { type } = ctx.query;
const typeNum = Number(type);
if (!typeNum || typeNum > 4 || typeNum < 1) {
ctx.body = { code: 400, ...routerInfo, message: "参数不完整或不正确" };
return false;
}
// 更改名称
routerInfo.subtitle = listType[typeNum].name;
// 从服务器拉取最新数据
const response = await axios.get(url + listType[typeNum].id, { headers });
const newData = getData(response.data);
updateTime = new Date().toISOString();
console.log("从服务端重新QQ音乐排行榜");
// 返回最新数据
ctx.body = {
code: 200,
message: "获取成功",
...routerInfo,
updateTime,
total: newData.length,
data: newData,
};
// 删除旧数据
await del(cacheKey + listType[typeNum].id);
// 将最新数据写入缓存
await set(cacheKey + listType[typeNum].id, newData);
} catch (error) {
// 如果拉取最新数据失败,尝试从缓存中获取数据
console.error(error);
const cachedData = await get(cacheKey);
if (cachedData) {
ctx.body = {
code: 200,
message: "获取成功",
...routerInfo,
total: cachedData.length,
updateTime,
data: cachedData,
};
} else {
// 如果缓存中也没有数据,则返回错误信息
ctx.body = {
code: 500,
...routerInfo,
message: "获取失败",
};
}
}
});
qqMusicRouter.info = routerInfo;
module.exports = qqMusicRouter;

View File

@ -1,7 +1,8 @@
/** /**
* @author: x-dr * @author: x-dr
* @date: 2023年12月25日 * @date: 2023-12-25
* @tags: [V2EX热帖] * @customEditors: imsyy
* @lastEditTime: 2024-01-02
*/ */
const Router = require("koa-router"); const Router = require("koa-router");
@ -12,14 +13,13 @@ const { get, set, del } = require("../utils/cacheData");
// 接口信息 // 接口信息
const routerInfo = { const routerInfo = {
name: "v2ex", name: "v2ex",
title: "V2EX", title: "V2EX",
subtitle: "hot", subtitle: "hot",
}; };
// 缓存键名 // 缓存键名
const cacheKey = "v2exbData"; const cacheKey = "v2exData";
// 调用时间 // 调用时间
let updateTime = new Date().toISOString(); let updateTime = new Date().toISOString();
@ -27,149 +27,148 @@ let updateTime = new Date().toISOString();
const url = "https://www.v2ex.com/?tab=hot"; const url = "https://www.v2ex.com/?tab=hot";
const headers = { const headers = {
"Content-Type": "application/json", "Content-Type": "application/json",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36", "User-Agent":
'authority': 'www.v2ex.com', "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36",
'referer': 'https://www.v2ex.com/' authority: "www.v2ex.com",
referer: "https://www.v2ex.com/",
}; };
// 数据处理 // 数据处理
const getData = (data) => { const getData = (data) => {
if (!data) return false; if (!data) return false;
const dataList = []; const dataList = [];
const $ = cheerio.load(data); const $ = cheerio.load(data);
try { try {
$(`div[class="cell item"]`).each((i, e) => { $(`div[class="cell item"]`).each((i, e) => {
const item = cheerio.load($(e).html()) const item = cheerio.load($(e).html());
const title = item('span[class="item_title"]').text().replace(/(^\s*)|(\s*$)/g, "") const title = item('span[class="item_title"]')
const href = item('.item_title a').attr('href'); .text()
const url = `https://www.v2ex.com${href}` .replace(/(^\s*)|(\s*$)/g, "");
const comments = item('.count_livid').text().replace(/(^\s*)|(\s*$)/g, "") const href = item(".item_title a").attr("href");
const member = item('.topic_info strong a:first').text().replace(/(^\s*)|(\s*$)/g, "") const url = `https://www.v2ex.com${href}`;
const node = item('.topic_info .node').text().replace(/(^\s*)|(\s*$)/g, "") const comments = item(".count_livid")
const avatar_img = item('.avatar').attr('src'); .text()
// console.log( url); .replace(/(^\s*)|(\s*$)/g, "");
const member = item(".topic_info strong a:first")
.text()
.replace(/(^\s*)|(\s*$)/g, "");
const node = item(".topic_info .node")
.text()
.replace(/(^\s*)|(\s*$)/g, "");
const avatar_img = item(".avatar").attr("src");
// console.log( url);
dataList.push({ dataList.push({
title: title, title: title,
url: url, url: url,
mobileUrl: url, mobileUrl: url,
comments: comments, comments: comments,
member: member, member: member,
node: node, node: node,
avatar: avatar_img avatar: avatar_img,
});
});
}); return dataList;
} catch (error) {
console.error("数据处理出错" + error);
}) return false;
}
return dataList;
} catch (error) {
console.error("数据处理出错" + error);
return false;
}
}; };
// v2ex
// trending
v2exRouter.get("/v2ex", async (ctx) => { v2exRouter.get("/v2ex", async (ctx) => {
console.log("获取v2ex"); console.log("获取v2ex");
try { try {
// 从缓存中获取数据 // 从缓存中获取数据
let data = await get(cacheKey); let data = await get(cacheKey);
const from = data ? "cache" : "server"; const from = data ? "cache" : "server";
if (!data) { if (!data) {
// 如果缓存中不存在数据 // 如果缓存中不存在数据
console.log("从服务端重新v2ex"); console.log("从服务端重新获取v2ex");
// 从服务器拉取数据 // 从服务器拉取数据
const response = await axios.get(url, { headers }); const response = await axios.get(url, { headers });
// console.log(response.data); // console.log(response.data);
data = getData(response.data); data = getData(response.data);
updateTime = new Date().toISOString(); updateTime = new Date().toISOString();
if (!data) { if (!data) {
ctx.body = {
code: 500,
...routerInfo,
message: "获取失败",
};
return false;
}
// 将数据写入缓存
await set(cacheKey, data);
}
ctx.body = { ctx.body = {
code: 200, code: 500,
message: "获取成功", ...routerInfo,
...routerInfo, message: "获取失败",
from,
total: data.length,
updateTime,
data,
};
} catch (error) {
console.error(error);
ctx.body = {
code: 500,
...routerInfo,
message: "获取失败",
}; };
return false;
}
// 将数据写入缓存
await set(cacheKey, data);
} }
ctx.body = {
code: 200,
message: "获取成功",
...routerInfo,
from,
total: data.length,
updateTime,
data,
};
} catch (error) {
console.error(error);
ctx.body = {
code: 500,
...routerInfo,
message: "获取失败",
};
}
}); });
// v2ex - 获取最新数据
// 豆瓣新片榜 - 获取最新数据
v2exRouter.get("/v2ex/new", async (ctx) => { v2exRouter.get("/v2ex/new", async (ctx) => {
console.log("获取v2ex - 最新数据"); console.log("获取v2ex - 最新数据");
try { try {
// 从服务器拉取最新数据 // 从服务器拉取最新数据
const response = await axios.get(url, { headers }); const response = await axios.get(url, { headers });
const newData = getData(response.data); const newData = getData(response.data);
updateTime = new Date().toISOString(); updateTime = new Date().toISOString();
console.log("从服务端重新v2ex"); console.log("从服务端重新获取v2ex");
// 返回最新数据 // 返回最新数据
ctx.body = { ctx.body = {
code: 200, code: 200,
message: "获取成功", message: "获取成功",
...routerInfo, ...routerInfo,
updateTime, updateTime,
total: newData.length, total: newData.length,
data: newData, data: newData,
}; };
// 删除旧数据 // 删除旧数据
await del(cacheKey); await del(cacheKey);
// 将最新数据写入缓存 // 将最新数据写入缓存
await set(cacheKey, newData); await set(cacheKey, newData);
} catch (error) { } catch (error) {
// 如果拉取最新数据失败,尝试从缓存中获取数据 // 如果拉取最新数据失败,尝试从缓存中获取数据
console.error(error); console.error(error);
const cachedData = await get(cacheKey); const cachedData = await get(cacheKey);
if (cachedData) { if (cachedData) {
ctx.body = { ctx.body = {
code: 200, code: 200,
message: "获取成功", message: "获取成功",
...routerInfo, ...routerInfo,
total: cachedData.length, total: cachedData.length,
updateTime, updateTime,
data: cachedData, data: cachedData,
}; };
} else { } else {
// 如果缓存中也没有数据,则返回错误信息 // 如果缓存中也没有数据,则返回错误信息
ctx.body = { ctx.body = {
code: 500, code: 500,
...routerInfo, ...routerInfo,
message: "获取失败", message: "获取失败",
}; };
}
} }
}
}); });
v2exRouter.info = routerInfo; v2exRouter.info = routerInfo;