mirror of
https://github.com/imsyy/DailyHotApi.git
synced 2026-01-12 05:04:56 +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;
|
||||
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