匹配范围
*://www.toutiao.com/*toutiao-hot-news今日头条热榜新闻 用途 读取今日头条首页热榜,返回热榜排名、标题、搜索词、热度、标签、页面地址和图片地址。 执行前提 先打开 https://www.toutiao.com/,再在当前标签页执行。热榜接口公开可访问,通常无需登录;若站点返回验证、限流、空结果或接口结构变化,脚本会直接报错,不会尝试绕过验证码、风控或访问限制。 参数 limit:可选,返回数…
*://www.toutiao.com/*dom.read1const rawParams = params && typeof params === "object" ? params : {};2 3function integerParam(name, fallback, min, max) {4 const raw = rawParams[name];5 const value = raw === undefined || raw === null || raw === "" ? fallback : Number(raw);6 if (!Number.isInteger(value) || value < min || value > max) {7 throw new Error(`${name} must be an integer between ${min} and ${max}`);8 }9 return value;10}11 12function assertToutiaoPage() {13 if (location.hostname !== "www.toutiao.com") {14 throw new Error("Open https://www.toutiao.com/ first, then run this script");15 }16}17 18function trimOrNull(value) {19 const text = String(value ?? "").trim();20 return text ? text : null;21}22 23function parseHotValue(value) {24 const number = Number(value);25 return Number.isFinite(number) && number >= 0 ? number : null;26}27 28function pickImage(item) {29 const direct = item?.Image?.url;30 if (typeof direct === "string" && direct) return direct;31 32 const list = Array.isArray(item?.Image?.url_list) ? item.Image.url_list : [];33 for (const entry of list) {34 const value = typeof entry === "string" ? entry : entry?.url;35 if (typeof value === "string" && value) return value;36 }37 38 return null;39}40 41function mapHotRow(item, index) {42 if (!item || typeof item !== "object") return null;43 const title = trimOrNull(item.Title);44 if (!title) return null;45 const groupId = trimOrNull(item.ClusterIdStr || (item.ClusterId !== undefined ? String(item.ClusterId) : null));46 47 return {48 rank: index + 1,49 group_id: groupId,50 title,51 query: trimOrNull(item.QueryWord) || title,52 hot_value: parseHotValue(item.HotValue),53 label: trimOrNull(item.Label),54 url: trimOrNull(item.Url),55 image_url: pickImage(item),56 };57}58 59async function readJson(response) {60 try {61 return await response.json();62 } catch {63 throw new Error("Response is not valid JSON");64 }65}66 67assertToutiaoPage();68const limit = integerParam("limit", 30, 1, 50);69 70const response = await fetch("https://www.toutiao.com/hot-event/hot-board/?origin=toutiao_pc", {71 headers: { Accept: "application/json" },72 credentials: "omit",73});74 75if (!response.ok) {76 throw new Error(`Hot news request failed with HTTP ${response.status}`);77}78 79const payload = await readJson(response);80 81if (payload?.status && payload.status !== "success") {82 throw new Error(`Hot news request returned status=${payload.status}`);83}84 85if (payload?.error || payload?.message) {86 throw new Error(`Hot news request returned error: ${payload.error || payload.message}`);87}88 89const rows = (Array.isArray(payload?.data) ? payload.data : [])90 .map(mapHotRow)91 .filter(Boolean)92 .slice(0, limit);93 94if (rows.length === 0) {95 throw new Error("No hot news data returned");96}97 98return rows.map((row, index) => ({ ...row, rank: index + 1 }));