匹配范围
*://*.weibo.com/*weibo-hot-search微博热搜榜 用途 读取微博公开热搜接口,返回当前热搜榜条目,包括排名、关键词、热度、标签、话题链接和图标等信息。 执行前提 请先打开 https://weibo.com/ 或任意 *.weibo.com 页面后执行。脚本会在当前微博页面同源请求 /ajax/side/hotSearch,不绕过登录、验证码或访问策略。 如果微博要求登录、出现验证码、安全验证…
*://*.weibo.com/*dom.read1return await (async () => {2 const input = typeof params === "object" && params ? params : {};3 const cleanText = (value) =>4 typeof value === "string" ? value.replace(/\s+/g, " ").trim() : "";5 const toBoolean = (value, fallback = false) => {6 if (value == null || value === "") return fallback;7 if (typeof value === "boolean") return value;8 if (typeof value === "number") return value !== 0;9 if (typeof value === "string") {10 const normalized = value.trim().toLowerCase();11 if (["1", "true", "yes", "y", "on", "include", "是", "包含"].includes(normalized)) return true;12 if (["0", "false", "no", "n", "off", "exclude", "否", "不包含"].includes(normalized)) return false;13 }14 return Boolean(value);15 };16 const toNumber = (value) => {17 const number = Number(value);18 return Number.isFinite(number) ? number : null;19 };20 const normalizeUrl = (value, baseUrl = "https://weibo.com/") => {21 const text = cleanText(value);22 if (!text) return "";23 if (/^https?:\/\//i.test(text)) return text;24 if (text.startsWith("//")) return `https:${text}`;25 if (text.startsWith("/")) {26 try {27 return new URL(text, baseUrl).toString();28 } catch {29 return "";30 }31 }32 return "";33 };34 const normalizeIcon = (item) => {35 const candidates = [36 item.icon,37 item.icon_url,38 item.iconUrl,39 item.small_icon,40 item.smallIcon,41 item.pic,42 ];43 for (const candidate of candidates) {44 const url = normalizeUrl(candidate);45 if (url) return url;46 }47 return "";48 };49 const buildTopicUrl = (item, word) => {50 const candidates = [item.url, item.link, item.search_url, item.searchUrl, item.scheme_url, item.schemeUrl];51 for (const candidate of candidates) {52 const url = normalizeUrl(candidate, "https://weibo.com/");53 if (url) return url;54 }55 const keyword = cleanText(item.word || word);56 return keyword57 ? `https://s.weibo.com/weibo?q=${encodeURIComponent(keyword)}&Refer=top`58 : "https://s.weibo.com/top/summary";59 };60 const isCommercialItem = (item) => {61 const explicitFlags = [62 item.is_ad,63 item.isAd,64 item.ad,65 item.is_ad_pos,66 item.isAdPos,67 item.is_commercial,68 item.isCommercial,69 item.promotion,70 item.is_promotion,71 item.isPromotion,72 ];73 if (explicitFlags.some((flag) => flag === true || flag === 1 || flag === "1")) return true;74 if (item.ad_info || item.adInfo || item.promotion_info || item.promotionInfo) return true;75 const text = [76 item.category,77 item.label_name,78 item.label,79 item.flag_desc,80 item.icon_desc,81 item.subject_label,82 ].map(cleanText).join(" ");83 return /广告|商业|推广/.test(text);84 };85 86 const limitValue = toNumber(input.limit);87 const limit = Math.max(1, Math.min(limitValue == null ? 20 : Math.trunc(limitValue), 50));88 const includeAd = toBoolean(input.include_ad ?? input.includeAd, false);89 90 if (!/(^|\.)weibo\.com$/i.test(location.hostname)) {91 throw new Error("请在 weibo.com 域名下执行微博热搜榜脚本。");92 }93 94 const endpoint = new URL("/ajax/side/hotSearch", location.origin).toString();95 let response;96 try {97 response = await fetch(endpoint, {98 method: "GET",99 credentials: "include",100 cache: "no-store",101 headers: {102 Accept: "application/json, text/plain, */*",103 },104 });105 } catch (error) {106 throw new Error(`微博热搜接口请求失败:${error?.message || String(error)}`);107 }108 109 const bodyText = await response.text();110 if (!response.ok) {111 const limited = /登录|验证码|安全验证|访问受限|verify|captcha|forbidden|login/i.test(bodyText);112 const reason = limited113 ? "可能遇到登录、验证码或接口访问策略限制。"114 : "请稍后重试或检查微博页面是否可正常访问。";115 throw new Error(`微博热搜接口返回 ${response.status} ${response.statusText || ""},${reason}`);116 }117 118 let payload;119 try {120 payload = JSON.parse(bodyText);121 } catch {122 const limited = /登录|验证码|安全验证|访问受限|verify|captcha|forbidden|login/i.test(bodyText);123 const reason = limited124 ? "接口返回了登录、验证码或访问受限页面。"125 : "接口返回的不是 JSON。";126 throw new Error(`微博热搜接口不可用:${reason}`);127 }128 129 const realtime = payload?.data?.realtime;130 if (payload?.ok !== 1 || !Array.isArray(realtime)) {131 const message = cleanText(payload?.msg || payload?.message || payload?.error || "");132 throw new Error(`微博热搜接口返回异常${message ? `:${message}` : ",可能遇到登录、验证码或接口策略限制。"}`);133 }134 135 return realtime136 .map((item, index) => ({ item, index }))137 .filter(({ item }) => includeAd || !isCommercialItem(item))138 .map(({ item, index }, outputIndex) => {139 const word = cleanText(item.word || item.note || item.title || item.keyword);140 const note = cleanText(item.note || item.title || item.word || word);141 const rankValue = toNumber(item.realpos ?? item.rank ?? item.hotrank ?? item.rank_index);142 const scoreValue = toNumber(item.num ?? item.raw_hot ?? item.score ?? item.hot ?? item.hot_score);143 return {144 rank: rankValue && rankValue > 0 ? rankValue : outputIndex + 1,145 word,146 note,147 score: scoreValue == null ? 0 : scoreValue,148 label: cleanText(item.label_name || item.label || item.flag_desc || item.icon_desc || ""),149 url: buildTopicUrl(item, word),150 icon: normalizeIcon(item),151 topic_flag: item.topic_flag ?? item.flag ?? item.category ?? "",152 };153 })154 .filter((item) => item.word || item.note)155 .slice(0, limit);156})();