匹配范围
*://*.autohome.com.cn/*autohome-series-score用途 读取汽车之家车系口碑页公开数据,返回指定车系的口碑评分、百车故障数、品牌、车系名、维度评分和车主印象。 执行前提 先打开对应车系的汽车之家口碑页,例如 https://k.autohome.com.cn/7538。脚本优先读取当前页面的 #_NEXTDATA__,当前页面不是目标车系时会尝试请求 https://k.autohome.com.cn/<…
*://*.autohome.com.cn/*dom.read1return await (async () => {2 const input = typeof params === "object" && params !== null ? params : {};3 const seriesId = String(input.seriesId || "").trim();4 5 if (!/^\d+$/.test(seriesId)) {6 throw new Error("params.seriesId is required and must contain digits only.");7 }8 9 const cleanText = (value) =>10 String(value || "")11 .replace(/\s+/g, " ")12 .replace(/\u00a0/g, " ")13 .trim();14 15 const toNumber = (value) => {16 if (value === null || value === undefined || value === "") return null;17 const number = Number(String(value).replace(/[^\d.-]/g, ""));18 return Number.isFinite(number) ? number : null;19 };20 21 const firstDefined = (...values) => values.find((value) => value !== undefined && value !== null && value !== "");22 23 const walkObjects = (root, visit, seen = new Set()) => {24 if (!root || typeof root !== "object" || seen.has(root)) return;25 seen.add(root);26 visit(root);27 28 if (Array.isArray(root)) {29 for (const item of root) walkObjects(item, visit, seen);30 return;31 }32 33 for (const value of Object.values(root)) {34 walkObjects(value, visit, seen);35 }36 };37 38 const parseNextData = (doc) => {39 const node = doc.querySelector("#__NEXT_DATA__");40 if (!node || !node.textContent.trim()) return null;41 return JSON.parse(node.textContent);42 };43 44 const getDocumentForSeries = async () => {45 const currentMatch = window.location.hostname === "k.autohome.com.cn" && window.location.pathname.includes(seriesId);46 if (currentMatch) return document;47 48 const response = await fetch(`https://k.autohome.com.cn/${seriesId}`, {49 credentials: "include",50 });51 52 if (!response.ok) {53 throw new Error(`Failed to fetch score page for series ${seriesId}: HTTP ${response.status}.`);54 }55 56 const html = await response.text();57 return new DOMParser().parseFromString(html, "text/html");58 };59 60 const doc = await getDocumentForSeries();61 const nextData = parseNextData(doc);62 63 let seriesData = null;64 let qualityData = null;65 let scoreList = [];66 67 if (nextData) {68 const candidates = [];69 walkObjects(nextData, (item) => candidates.push(item));70 71 seriesData =72 candidates.find(73 (item) =>74 String(firstDefined(item.seriesid, item.seriesId, item.seriesID)) === seriesId &&75 firstDefined(item.seriesname, item.seriesName, item.average, item.seriesAverage)76 ) || null;77 78 qualityData =79 candidates.find(80 (item) =>81 String(firstDefined(item.seriesId, item.seriesid, item.seriesID)) === seriesId &&82 firstDefined(item.pph, item.newCarPPH, item.userCount)83 ) || null;84 85 const scoreOwner = candidates.find((item) => Array.isArray(item.seriesScoreList));86 scoreList = Array.isArray(scoreOwner?.seriesScoreList) ? scoreOwner.seriesScoreList : [];87 }88 89 const titleText = cleanText(doc.querySelector("title")?.textContent);90 const headingText = cleanText(doc.querySelector("h1")?.textContent);91 const toolbarText = cleanText(doc.querySelector("[class*='toolbar'] a[href*='autohome.com.cn']")?.textContent);92 const brandFromToolbar = toolbarText.includes("-") ? cleanText(toolbarText.split("-")[0]) : null;93 const scoreText = cleanText(doc.body?.textContent);94 const scoreMatch = scoreText.match(/口碑评分\s*(\d+(?:\.\d+)?)/);95 const pphMatch = scoreText.match(/百车故障数\s*(\d+)/);96 97 const dimensions = scoreList.map((item) => ({98 name: cleanText(firstDefined(item.typeName, item.name)),99 score: toNumber(firstDefined(item.score, item.value)),100 rank: toNumber(item.rank),101 }));102 103 const impressions = Array.from(doc.querySelectorAll("[class*='score_tag'] li"))104 .map((item) => cleanText(item.textContent))105 .filter(Boolean)106 .map((text) => {107 const match = text.match(/^(.*?)(\d+)$/);108 return {109 text: cleanText(match ? match[1] : text),110 count: match ? toNumber(match[2]) : null,111 };112 });113 114 return {115 seriesId,116 seriesName: cleanText(firstDefined(seriesData?.seriesname, seriesData?.seriesName, headingText)) || null,117 brandName: cleanText(firstDefined(seriesData?.brandName, seriesData?.brandname, brandFromToolbar)) || null,118 overallScore: toNumber(firstDefined(seriesData?.average, seriesData?.seriesAverage, scoreMatch?.[1])),119 pph: toNumber(firstDefined(seriesData?.newCarPPH, qualityData?.pph, pphMatch?.[1])),120 reviewCount: toNumber(firstDefined(seriesData?.averagenum, seriesData?.rowcount, qualityData?.userCount)),121 priceRange: cleanText(firstDefined(seriesData?.pricerange, seriesData?.priceRange)) || null,122 levelName: cleanText(firstDefined(seriesData?.levelname, seriesData?.levelName)) || null,123 dimensions,124 impressions,125 pageTitle: titleText || null,126 sourceUrl: doc === document ? window.location.href : `https://k.autohome.com.cn/${seriesId}`,127 };128})();