灵猴市集Linghou Market

微博热搜榜

微博weibo-hot-search

微博热搜榜 用途 读取微博公开热搜接口,返回当前热搜榜条目,包括排名、关键词、热度、标签、话题链接和图标等信息。 执行前提 请先打开 https://weibo.com/ 或任意 *.weibo.com 页面后执行。脚本会在当前微博页面同源请求 /ajax/side/hotSearch,不绕过登录、验证码或访问策略。 如果微博要求登录、出现验证码、安全验证…

热搜榜单公开读取
版本1.0.0
扫描passed
更新2026-06-28
超时30000ms

匹配范围

*://*.weibo.com/*

排除范围

未声明

能力声明

dom.read

代码

源码已展开
1return 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})();