匹配范围
*://xueqiu.com/**://*.xueqiu.com/*xueqiu-hot-stocks雪球热门股票榜 用途 读取雪球热门股票榜,返回股票排名、代码、名称、价格、涨跌幅、热度和雪球页面地址。 执行前提 先打开 https://xueqiu.com/ 或雪球站内页面。请求会使用当前浏览器 Cookie;接口可能受登录态、风控、Cookie 状态和访问频率影响,脚本不会绕过登录、验证码或风控。 参数 limit:可选,返回条数,默认 20,范围 …
*://xueqiu.com/**://*.xueqiu.com/*dom.read1const rawParams = typeof params === "object" && params !== null ? params : {};2 3function assertXueqiuPage() {4 if (!/(^|\.)xueqiu\.com$/i.test(location.hostname)) {5 throw new Error("请先打开 xueqiu.com 页面再执行脚本");6 }7}8 9function readLimit() {10 const value = rawParams.limit ?? 20;11 const limit = Number(value);12 if (!Number.isInteger(limit) || limit < 1 || limit > 50) {13 throw new Error("limit 必须是 1 到 50 的整数");14 }15 return limit;16}17 18function readType() {19 const type = String(rawParams.type ?? "10").trim();20 if (type !== "10" && type !== "12") {21 throw new Error("type 只允许 10(人气榜)或 12(关注榜)");22 }23 return type;24}25 26function firstValue() {27 for (const value of arguments) {28 if (value !== undefined && value !== null && value !== "") return value;29 }30 return null;31}32 33function numericOrText(value) {34 if (value === undefined || value === null || value === "") return null;35 const number = Number(value);36 return Number.isFinite(number) ? number : String(value);37}38 39async function readJson(response) {40 try {41 return await response.json();42 } catch {43 return null;44 }45}46 47function errorDetail(payload, fallback) {48 return firstValue(payload?.error_description, payload?.errorMessage, payload?.message, fallback);49}50 51assertXueqiuPage();52 53const limit = readLimit();54const type = readType();55const url = `https://stock.xueqiu.com/v5/stock/hot_stock/list.json?size=${encodeURIComponent(limit)}&type=${encodeURIComponent(type)}`;56 57const response = await fetch(url, {58 headers: { Accept: "application/json" },59 credentials: "include",60});61 62const payload = await readJson(response);63 64if (!response.ok) {65 throw new Error(`请求雪球热门股票榜失败:${errorDetail(payload, `HTTP ${response.status}`)}`);66}67 68if (payload?.error_code || payload?.error_description) {69 throw new Error(`请求雪球热门股票榜失败:${errorDetail(payload, payload.error_code)}`);70}71 72const items = Array.isArray(payload?.data?.items) ? payload.data.items : [];73if (!items.length) {74 throw new Error("没有读取到雪球热门股票榜数据");75}76 77return items.slice(0, limit).map((item, index) => {78 const symbol = String(firstValue(item.symbol, item.code, "")).trim().toUpperCase();79 return {80 rank: numericOrText(firstValue(item.rank, item.order, index + 1)),81 symbol,82 name: firstValue(item.name, item.stock_name, item.title),83 price: numericOrText(firstValue(item.current, item.price, item.last)),84 change: numericOrText(firstValue(item.chg, item.change_amount, item.changeAmount)),85 changePercent: numericOrText(firstValue(item.percent, item.percentage, item.change_percent, item.changePercent)),86 heat: numericOrText(firstValue(item.value, item.heat, item.hot_value, item.hotValue)),87 rankChange: numericOrText(firstValue(item.rank_change, item.rankChange, item.rank_chg, item.rankDiff, item.rank_diff)),88 url: symbol ? `https://xueqiu.com/S/${symbol}` : null,89 };90});