mirror of
https://github.com/imsyy/DailyHotApi.git
synced 2026-01-12 13:14:55 +08:00
Support Github trending, hackernews and producthunt
Now support source from Github Trending, hackernews and producthunt
This commit is contained in:
25
src/router.types.d.ts
vendored
25
src/router.types.d.ts
vendored
@@ -385,4 +385,29 @@ export type RouterType = {
|
|||||||
desc: string;
|
desc: string;
|
||||||
timestamp: string;
|
timestamp: string;
|
||||||
};
|
};
|
||||||
|
hackernews: {
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
hot: number | undefined;
|
||||||
|
timestamp: number | undefined;
|
||||||
|
url: string;
|
||||||
|
mobileUrl: string;
|
||||||
|
};
|
||||||
|
github: {
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
desc?: string;
|
||||||
|
hot: number | undefined;
|
||||||
|
timestamp: number | undefined;
|
||||||
|
url: string;
|
||||||
|
mobileUrl: string;
|
||||||
|
};
|
||||||
|
producthunt: {
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
hot: number | undefined;
|
||||||
|
timestamp: number | undefined;
|
||||||
|
url: string;
|
||||||
|
mobileUrl: string;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
61
src/routes/github.ts
Normal file
61
src/routes/github.ts
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
import type { RouterData } from "../types.js";
|
||||||
|
import { get } from "../utils/getData.js";
|
||||||
|
import { load } from "cheerio";
|
||||||
|
import type { RouterType } from "../router.types.js";
|
||||||
|
|
||||||
|
export const handleRoute = async (_: undefined, noCache: boolean) => {
|
||||||
|
const listData = await getList(noCache);
|
||||||
|
const routeData: RouterData = {
|
||||||
|
name: "github",
|
||||||
|
title: "GitHub",
|
||||||
|
type: "Trending",
|
||||||
|
description: "See what the GitHub community is most excited about today",
|
||||||
|
link: "https://github.com/trending",
|
||||||
|
total: listData.data?.length || 0,
|
||||||
|
...listData,
|
||||||
|
};
|
||||||
|
return routeData;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getList = async (noCache: boolean) => {
|
||||||
|
const baseUrl = "https://github.com";
|
||||||
|
const result = await get({
|
||||||
|
url: `${baseUrl}/trending?spoken_language_code=`,
|
||||||
|
noCache,
|
||||||
|
headers: {
|
||||||
|
userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
const $ = load(result.data);
|
||||||
|
const stories: RouterType["github"][] = [];
|
||||||
|
|
||||||
|
$("main .Box div[data-hpc] > article").each((_, el) => {
|
||||||
|
const a = $(el).find(">h2 a");
|
||||||
|
const title = a.text().replace(/\n+/g, "").trim();
|
||||||
|
const path = a.attr("href");
|
||||||
|
const star = $(el).find("[href$=stargazers]").text().replace(/\s+/g, "").trim();
|
||||||
|
const desc = $(el).find(">p").text().replace(/\n+/g, "").trim();
|
||||||
|
|
||||||
|
if (path && title) {
|
||||||
|
stories.push({
|
||||||
|
id: path.slice(1), // 移除开头的 /
|
||||||
|
title,
|
||||||
|
desc,
|
||||||
|
hot: parseInt(star.replace(/,/g, "")) || undefined,
|
||||||
|
timestamp: undefined,
|
||||||
|
url: `${baseUrl}${path}`,
|
||||||
|
mobileUrl: `${baseUrl}${path}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
...result,
|
||||||
|
data: stories,
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`Failed to parse GitHub Trending HTML: ${error}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
63
src/routes/hackernews.ts
Normal file
63
src/routes/hackernews.ts
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
import type { RouterData } from "../types.js";
|
||||||
|
import { get } from "../utils/getData.js";
|
||||||
|
import { load } from "cheerio";
|
||||||
|
import type { RouterType } from "../router.types.js";
|
||||||
|
|
||||||
|
export const handleRoute = async (_: undefined, noCache: boolean) => {
|
||||||
|
const listData = await getList(noCache);
|
||||||
|
const routeData: RouterData = {
|
||||||
|
name: "hackernews",
|
||||||
|
title: "Hacker News",
|
||||||
|
type: "Popular",
|
||||||
|
description: "News about hacking and startups",
|
||||||
|
link: "https://news.ycombinator.com/",
|
||||||
|
total: listData.data?.length || 0,
|
||||||
|
...listData,
|
||||||
|
};
|
||||||
|
return routeData;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getList = async (noCache: boolean) => {
|
||||||
|
const baseUrl = "https://news.ycombinator.com";
|
||||||
|
const result = await get({
|
||||||
|
url: baseUrl,
|
||||||
|
noCache,
|
||||||
|
headers: {
|
||||||
|
userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
const $ = load(result.data);
|
||||||
|
const stories: RouterType["hackernews"][] = [];
|
||||||
|
|
||||||
|
$(".athing").each((_, el) => {
|
||||||
|
const item = $(el);
|
||||||
|
const id = item.attr("id") || "";
|
||||||
|
const title = item.find(".titleline a").first().text().trim();
|
||||||
|
const url = item.find(".titleline a").first().attr("href");
|
||||||
|
|
||||||
|
// 获取分数并转换为数字
|
||||||
|
const scoreText = $(`#score_${id}`).text().match(/\d+/)?.[0];
|
||||||
|
const hot = scoreText ? parseInt(scoreText, 10) : undefined;
|
||||||
|
|
||||||
|
if (id && title) {
|
||||||
|
stories.push({
|
||||||
|
id,
|
||||||
|
title,
|
||||||
|
hot,
|
||||||
|
timestamp: undefined,
|
||||||
|
url: url || `${baseUrl}/item?id=${id}`,
|
||||||
|
mobileUrl: url || `${baseUrl}/item?id=${id}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
...result,
|
||||||
|
data: stories,
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`Failed to parse HackerNews HTML: ${error}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
60
src/routes/producthunt.ts
Normal file
60
src/routes/producthunt.ts
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
import type { RouterData } from "../types.js";
|
||||||
|
import { get } from "../utils/getData.js";
|
||||||
|
import { load } from "cheerio";
|
||||||
|
import type { RouterType } from "../router.types.js";
|
||||||
|
|
||||||
|
export const handleRoute = async (_: undefined, noCache: boolean) => {
|
||||||
|
const listData = await getList(noCache);
|
||||||
|
const routeData: RouterData = {
|
||||||
|
name: "producthunt",
|
||||||
|
title: "Product Hunt",
|
||||||
|
type: "Today",
|
||||||
|
description: "The best new products, every day",
|
||||||
|
link: "https://www.producthunt.com/",
|
||||||
|
total: listData.data?.length || 0,
|
||||||
|
...listData,
|
||||||
|
};
|
||||||
|
return routeData;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getList = async (noCache: boolean) => {
|
||||||
|
const baseUrl = "https://www.producthunt.com";
|
||||||
|
const result = await get({
|
||||||
|
url: baseUrl,
|
||||||
|
noCache,
|
||||||
|
headers: {
|
||||||
|
userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
const $ = load(result.data);
|
||||||
|
const stories: RouterType["producthunt"][] = [];
|
||||||
|
|
||||||
|
$("[data-test=homepage-section-0] [data-test^=post-item]").each((_, el) => {
|
||||||
|
const a = $(el).find("a").first();
|
||||||
|
const path = a.attr("href");
|
||||||
|
const title = $(el).find("a[data-test^=post-name]").text().trim();
|
||||||
|
const id = $(el).attr("data-test")?.replace("post-item-", "");
|
||||||
|
const vote = $(el).find("[data-test=vote-button]").text().trim();
|
||||||
|
|
||||||
|
if (path && id && title) {
|
||||||
|
stories.push({
|
||||||
|
id,
|
||||||
|
title,
|
||||||
|
hot: parseInt(vote) || undefined,
|
||||||
|
timestamp: undefined,
|
||||||
|
url: `${baseUrl}${path}`,
|
||||||
|
mobileUrl: `${baseUrl}${path}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
...result,
|
||||||
|
data: stories,
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`Failed to parse Product Hunt HTML: ${error}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user