From 99d16f1f704762469af110e8b52e015c9b12263b Mon Sep 17 00:00:00 2001 From: wxs Date: Fri, 13 Mar 2026 23:46:06 +0800 Subject: [PATCH] =?UTF-8?q?feat(pugongying):=20=E6=94=AF=E6=8C=81=E5=B0=8F?= =?UTF-8?q?=E7=BA=A2=E4=B9=A6=E7=9F=AD=E9=93=BE=E6=8E=A5=E8=BE=93=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 通过 GM_xmlhttpRequest HEAD 请求跟踪短链接重定向, 从 finalUrl 中提取达人 ID。支持 xhslink.com 域名。 Co-Authored-By: Claude Opus 4.6 --- pugongying/xhs-pgy-export.user.js | 86 ++++++++++++++++++++++--------- 1 file changed, 63 insertions(+), 23 deletions(-) diff --git a/pugongying/xhs-pgy-export.user.js b/pugongying/xhs-pgy-export.user.js index 8eb29d8..58bdb36 100644 --- a/pugongying/xhs-pgy-export.user.js +++ b/pugongying/xhs-pgy-export.user.js @@ -6,6 +6,7 @@ // @match https://pgy.xiaohongshu.com/* // @grant GM_xmlhttpRequest // @connect api.internal.intelligrow.cn +// @connect xhslink.com // @require https://cdn.jsdelivr.net/npm/xlsx@0.18.5/dist/xlsx.full.min.js // ==/UserScript== @@ -235,27 +236,28 @@ return baseTarget; } - function extractBloggerId(value) { - const raw = normalizeScalar(value); - if (!raw) { - return ""; - } + function resolveShortUrl(url) { + return new Promise((resolve) => { + if (typeof GM_xmlhttpRequest !== "function") { + resolve(url); + return; + } + GM_xmlhttpRequest({ + method: "HEAD", + url, + onload(res) { + resolve(res.finalUrl || url); + }, + onerror() { + resolve(url); + }, + }); + }); + } - if (/^[0-9a-f]{24}$/i.test(raw)) { - return raw; - } - - if (!/^https?:\/\//i.test(raw)) { - return ""; - } - - let parsedUrl; - try { - parsedUrl = new URL(raw); - } catch (error) { - return ""; - } + const SHORT_LINK_HOSTS = ["xhslink.com"]; + function extractIdFromUrl(parsedUrl) { const queryCandidates = ["id", "user_id", "userId", "bloggerId", "creatorId"]; for (const key of queryCandidates) { const queryValue = parsedUrl.searchParams.get(key); @@ -279,7 +281,45 @@ return ""; } - function parseCreatorInputs(rawInput) { + async function extractBloggerId(value) { + const raw = normalizeScalar(value); + if (!raw) { + return ""; + } + + if (/^[0-9a-f]{24}$/i.test(raw)) { + return raw; + } + + if (!/^https?:\/\//i.test(raw)) { + return ""; + } + + let parsedUrl; + try { + parsedUrl = new URL(raw); + } catch (error) { + return ""; + } + + const directId = extractIdFromUrl(parsedUrl); + if (directId) { + return directId; + } + + if (SHORT_LINK_HOSTS.some((h) => parsedUrl.hostname.endsWith(h))) { + const realUrl = await resolveShortUrl(raw); + try { + return extractIdFromUrl(new URL(realUrl)); + } catch (error) { + return ""; + } + } + + return ""; + } + + async function parseCreatorInputs(rawInput) { const values = normalizeScalar(rawInput) .split(/[\n,,\s]+/) .map((item) => item.trim()) @@ -288,8 +328,8 @@ const ids = []; const seen = new Set(); - for (const value of values) { - const id = extractBloggerId(value); + const resolved = await Promise.all(values.map((v) => extractBloggerId(v))); + for (const id of resolved) { if (!id || seen.has(id)) { continue; } @@ -508,7 +548,7 @@ return { async preview(rawInput, onProgress) { - const ids = parseCreatorInputs(rawInput); + const ids = await parseCreatorInputs(rawInput); if (!ids.length) { throw new Error("请输入至少一个有效的达人主页链接或达人 ID。"); }