diff --git a/src/routes/huxiu.ts b/src/routes/huxiu.ts old mode 100644 new mode 100755 index 7a04b32..38b21ee --- a/src/routes/huxiu.ts +++ b/src/routes/huxiu.ts @@ -1,7 +1,7 @@ import type { RouterData } from "../types.js"; import type { RouterType } from "../router.types.js"; -import { get } from "../utils/getData.js"; import { getTime } from "../utils/getTime.js"; +import axios from "axios"; export const handleRoute = async (_: undefined, noCache: boolean) => { const listData = await getList(noCache); @@ -16,36 +16,39 @@ export const handleRoute = async (_: undefined, noCache: boolean) => { return routeData; }; -// 标题处理 -const titleProcessing = (text: string) => { - const paragraphs = text.split("

"); - const title = paragraphs.shift()?.replace(/。$/, ""); - const intro = paragraphs.join("

"); - return { title, intro }; -}; - const getList = async (noCache: boolean) => { - const url = `https://www.huxiu.com/moment/`; - const result = await get({ - url, - noCache, + // PC 端接口 + const url = `https://moment-api.huxiu.com/web-v3/moment/feed?platform=www`; + const res = await axios.get(url, { + headers: { + "User-Agent": "Mozilla/5.0", + Referer: "https://www.huxiu.com/moment/", + }, + timeout: 10000, }); - // 正则查找 - const pattern = - /"); + const cutIndex = + sentinelA !== -1 && sentinelB !== -1 ? Math.min(sentinelA, sentinelB) : Math.max(sentinelA, sentinelB); + if (cutIndex === -1) { + throw new Error("快手页面结构变更,未找到 APOLLO_STATE 结束标记"); + } + const raw = scriptSlice.slice(0, cutIndex).trim().replace(/;$/, ""); + let jsonObject; + try { + // 快手返回的 JSON 末尾常带 undefined/null,需要截断到最后一个 '}' 出现 + const lastBrace = raw.lastIndexOf("}"); + const cleanRaw = lastBrace !== -1 ? raw.slice(0, lastBrace + 1) : raw; + jsonObject = JSON.parse(cleanRaw)["defaultClient"]; + } catch (err) { + const msg = + err instanceof Error + ? `${err.message} | snippet=${raw.slice(0, 200)}...` + : "未知错误"; + throw new Error(`快手数据解析失败: ${msg}`); + } // 获取所有分类 - const allItems = jsonObject['$ROOT_QUERY.visionHotRank({"page":"home"})']["items"]; + const allItems = + jsonObject['$ROOT_QUERY.visionHotRank({"page":"home"})']?.items || + jsonObject['$ROOT_QUERY.visionHotRank({"page":"home","platform":"web"})'] + ?.items || + []; // 获取全部热榜 - allItems?.forEach((item: { id: string }) => { + allItems.forEach((item: { id: string }) => { // 基础数据 const hotItem: RouterType["kuaishou"] = jsonObject[item.id]; + if (!hotItem) return; const id = hotItem.photoIds?.json?.[0]; + const hotValue = hotItem.hotValue ?? ""; + const poster = hotItem.poster ? decodeURIComponent(hotItem.poster) : undefined; listData.push({ id: hotItem.id, title: hotItem.name, - cover: decodeURIComponent(hotItem.poster), - hot: parseChineseNumber(hotItem.hotValue), + cover: poster, + hot: parseChineseNumber(String(hotValue)), timestamp: undefined, url: `https://www.kuaishou.com/short-video/${id}`, mobileUrl: `https://www.kuaishou.com/short-video/${id}`, diff --git a/src/routes/linuxdo.ts b/src/routes/linuxdo.ts index cab1a87..d541bf4 100644 --- a/src/routes/linuxdo.ts +++ b/src/routes/linuxdo.ts @@ -1,16 +1,7 @@ import type { RouterData } from "../types.js"; import { get } from "../utils/getData.js"; import { getTime } from "../utils/getTime.js"; - -interface Topic { - id: number; - title: string; - excerpt: string; - last_poster_username: string; - created_at: string; - views: number; - like_count: number; -} +import { parseRSS } from "../utils/parseRSS.js"; export const handleRoute = async (_: undefined, noCache: boolean) => { const listData = await getList(noCache); @@ -19,7 +10,7 @@ export const handleRoute = async (_: undefined, noCache: boolean) => { title: "Linux.do", type: "热门文章", description: "Linux 技术社区热搜", - link: "https://linux.do/hot", + link: "https://linux.do/top/weekly", total: listData.data?.length || 0, ...listData, }; @@ -27,31 +18,34 @@ export const handleRoute = async (_: undefined, noCache: boolean) => { }; const getList = async (noCache: boolean) => { - const url = "https://linux.do/top/weekly.json"; + const url = "https://linux.do/top.rss?period=weekly"; const result = await get({ url, noCache, headers: { - "Accept": "application/json", - } + "Accept": "application/rss+xml, application/xml;q=0.9, */*;q=0.8", + "User-Agent": + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36", + }, }); - - const topics = result.data.topic_list.topics as Topic[]; - const list = topics.map((topic) => { + + const items = await parseRSS(result.data); + const list = items.map((item, index) => { + const link = item.link || ""; return { - id: topic.id, - title: topic.title, - desc: topic.excerpt, - author: topic.last_poster_username, - timestamp: getTime(topic.created_at), - url: `https://linux.do/t/${topic.id}`, - mobileUrl: `https://linux.do/t/${topic.id}`, - hot: topic.views || topic.like_count + id: item.guid || link || index, + title: item.title || "", + desc: item.contentSnippet?.trim() || item.content?.trim() || "", + author: item.author, + timestamp: getTime(item.pubDate || 0), + url: link, + mobileUrl: link, + hot: undefined, }; }); return { ...result, - data: list + data: list, }; -}; \ No newline at end of file +};