diff --git a/routes/douban_group.js b/routes/douban_group.js new file mode 100644 index 0000000..75c8d75 --- /dev/null +++ b/routes/douban_group.js @@ -0,0 +1,185 @@ +/** + * @author: x-dr + * @date: 2023年12月26日 + * @tags: [豆瓣讨论精选] + */ + + +const Router = require("koa-router"); +const doubanGroupNewRouter = new Router(); +const axios = require("axios"); +const cheerio = require("cheerio"); +const { get, set, del } = require("../utils/cacheData"); + +// 接口信息 +const routerInfo = { + name: "doubangroup", + title: "豆瓣讨论小组", + subtitle: "精选", +}; + + + +// 缓存键名 +const cacheKey = "doubanGroupData"; + +// 调用时间 +let updateTime = new Date().toISOString(); + +const url = "https://www.douban.com/group/explore"; + +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-language": "zh-CN,zh;q=0.9,en;q=0.8", + "cache-control": "max-age=0", + "sec-ch-ua": "\"Not_A Brand\";v=\"8\", \"Chromium\";v=\"120\", \"Google Chrome\";v=\"120\"", + "sec-ch-ua-mobile": "?0", + "sec-ch-ua-platform": "\"Windows\"", + "sec-fetch-dest": "document", + "sec-fetch-mode": "navigate", + "sec-fetch-site": "none", + "sec-fetch-user": "?1", + "upgrade-insecure-requests": "1", + // "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) => { + if (!data) return false; + const dataList = []; + const $ = cheerio.load(data); + try { + + $(`.channel-item`).each((i, e) => { + // console.log($(e).html()); + const item= cheerio.load($(e).html()) + const title = item("h3").text().replace(/(^\s*)|(\s*$)/g, "") + const url = item("h3 a").attr('href') + 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, "") + // console.log(title); + // console.log(url); + + dataList.push({ + title: title, + desc: desc, + url: url, + mobileUrl: url, + hot: hot, + source: source, + + }); + + }); + return dataList; + } catch (error) { + console.error("数据处理出错" + error); + return false; + } +}; + + + +// trending +doubanGroupNewRouter.get("/doubangroup", async (ctx) => { + console.log("获取豆瓣讨论小组精选"); + try { + // 从缓存中获取数据 + let data = await get(cacheKey); + const from = data ? "cache" : "server"; + if (!data) { + // 如果缓存中不存在数据 + console.log("从服务端重新豆瓣讨论小组精选"); + // 从服务器拉取数据 + 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: "获取失败", + }; + } +}); + + + + +// 豆瓣新片榜 - 获取最新数据 +doubanGroupNewRouter.get("/doubangroup/new", async (ctx) => { + console.log("获取豆瓣讨论小组精选 - 最新数据"); + try { + // 从服务器拉取最新数据 + const response = await axios.get(url, { 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); + // 将最新数据写入缓存 + 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: "获取失败", + }; + } + } +}); + +doubanGroupNewRouter.info = routerInfo; +module.exports = doubanGroupNewRouter; \ No newline at end of file diff --git a/routes/netease_music.js b/routes/netease_music.js new file mode 100644 index 0000000..f49dbcd --- /dev/null +++ b/routes/netease_music.js @@ -0,0 +1,169 @@ +/** + * @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; \ No newline at end of file diff --git a/routes/ngabbs.js b/routes/ngabbs.js new file mode 100644 index 0000000..81e06a4 --- /dev/null +++ b/routes/ngabbs.js @@ -0,0 +1,155 @@ +/** + * @author: x-dr + * @date: 2023年12月25日 + * @tags: [NGA论坛热帖] + */ + +const Router = require("koa-router"); +const ngabbsRouter = new Router(); +const axios = require("axios"); +const { get, set, del } = require("../utils/cacheData"); + + +// 接口信息 +const routerInfo = { name: "ngabbs", title: "NGA", subtitle: "论坛热帖" }; + +// 缓存键名 +const cacheKey = "ngabbsData"; + +// 调用时间 +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 headers = { + 'Host': 'ngabbs.com', + 'Content-Type': 'application/x-www-form-urlencoded', + 'Accept': '*/*', + 'Accept-Encoding': 'gzip, deflate, br', + 'Connection': 'keep-alive', + 'Content-Length': '11', + 'User-Agent': 'NGA/7.3.1 (iPhone; iOS 17.2.1; Scale/3.00)', + 'Accept-Language': 'zh-Hans-CN;q=1', + 'Referer': 'https://ngabbs.com/', + 'X-User-Agent': 'NGA_skull/7.3.1(iPhone13,2;iOS 17.2.1)', +}; +const postData = { '__output': '14' }; +// 数据处理 +const getData = (data) => { + if (!data) return []; + const dataList = []; + try { + + const result = data.result[0]; + result.forEach((result) => { + dataList.push({ + author: result.author, + desc: result.subject, + parent: result.parent['2'], + tid: result.tid, + comments: Number(result.replies), + url: `https://bbs.nga.cn/read.php?tid=${result.tid}`, + mobileUrl: `https://bbs.nga.cn/read.php?tid=${result.tid}`, + + }); + }); + return dataList; + } catch (error) { + console.error("数据处理出错" + error); + return false; + } +}; + +// NGA论坛热帖 +ngabbsRouter.get("/ngabbs", async (ctx) => { + console.log("获取NGA论坛热帖"); + try { + // 从缓存中获取数据 + let data = await get(cacheKey); + const from = data ? "cache" : "server"; + if (!data) { + // 如果缓存中不存在数据 + console.log("从服务端重新获取NGA论坛热帖"); + // 从服务器拉取数据 + const response = await axios.post(url, postData, { headers }); + 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, + message: "获取失败", + }; + } +}); + +// NGA论坛热帖 - 获取最新数据 +ngabbsRouter.get("/ngabbs/new", async (ctx) => { + console.log("获取NGA论坛热帖 - 最新数据"); + try { + // 从服务器拉取最新数据 + const response = await axios.post(url, postData, { headers }); + const newData = getData(response.data); + updateTime = new Date().toISOString(); + console.log("从服务端重新获取NGA论坛热帖"); + + // 返回最新数据 + ctx.body = { + code: 200, + message: "获取成功", + ...routerInfo, + total: newData.length, + updateTime, + 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: "获取失败", + }; + } + } +}); + +ngabbsRouter.info = routerInfo; +module.exports = ngabbsRouter; diff --git a/routes/qq_music.js b/routes/qq_music.js new file mode 100644 index 0000000..d924d9a --- /dev/null +++ b/routes/qq_music.js @@ -0,0 +1,170 @@ +/** + * @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; \ No newline at end of file diff --git a/routes/v2ex.js b/routes/v2ex.js new file mode 100644 index 0000000..ab7354e --- /dev/null +++ b/routes/v2ex.js @@ -0,0 +1,176 @@ +/** + * @author: x-dr + * @date: 2023年12月25日 + * @tags: [V2EX热帖] + */ + +const Router = require("koa-router"); +const v2exRouter = new Router(); +const axios = require("axios"); +const cheerio = require("cheerio"); +const { get, set, del } = require("../utils/cacheData"); + +// 接口信息 +const routerInfo = { + name: "v2ex", + title: "V2EX", + subtitle: "hot", +}; + + +// 缓存键名 +const cacheKey = "v2exbData"; + +// 调用时间 +let updateTime = new Date().toISOString(); + +const url = "https://www.v2ex.com/?tab=hot"; + +const headers = { + "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", + 'authority': 'www.v2ex.com', + 'referer': 'https://www.v2ex.com/' +}; + + + +// 数据处理 +const getData = (data) => { + if (!data) return false; + const dataList = []; + const $ = cheerio.load(data); + try { + $(`div[class="cell item"]`).each((i, e) => { + const item = cheerio.load($(e).html()) + const title = item('span[class="item_title"]').text().replace(/(^\s*)|(\s*$)/g, "") + const href = item('.item_title a').attr('href'); + const url = `https://www.v2ex.com${href}` + const comments = item('.count_livid').text().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({ + title: title, + url: url, + mobileUrl: url, + comments: comments, + member: member, + node: node, + avatar: avatar_img + + }); + + + }) + + return dataList; + } catch (error) { + console.error("数据处理出错" + error); + return false; + } +}; + + + +// trending +v2exRouter.get("/v2ex", async (ctx) => { + console.log("获取v2ex"); + try { + // 从缓存中获取数据 + let data = await get(cacheKey); + const from = data ? "cache" : "server"; + if (!data) { + // 如果缓存中不存在数据 + console.log("从服务端重新v2ex"); + // 从服务器拉取数据 + 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: "获取失败", + }; + } +}); + + + + +// 豆瓣新片榜 - 获取最新数据 +v2exRouter.get("/v2ex/new", async (ctx) => { + console.log("获取v2ex - 最新数据"); + try { + // 从服务器拉取最新数据 + const response = await axios.get(url, { headers }); + const newData = getData(response.data); + updateTime = new Date().toISOString(); + console.log("从服务端重新v2ex"); + + // 返回最新数据 + 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: "获取失败", + }; + } + } +}); + +v2exRouter.info = routerInfo; +module.exports = v2exRouter; \ No newline at end of file