匹配范围
*://www.dongchedi.com/*dongchedi-series-overview用途 读取懂车帝车系公开页面的概览信息,返回品牌、指导价、经销商价、二手价、懂车分、评价数、排名、在售款型数和车系链接。 执行前提 需要在懂车帝网页中执行,建议打开目标车系页面,例如 https://www.dongchedi.com/auto/series/<id>。脚本读取公开页面数据,不需要登录。若未传入 seriesid,会尝试从当前页面 URL …
*://www.dongchedi.com/*dom.read1return await (async () => {2 const DCD_BASE = "https://www.dongchedi.com";3 const FALLBACK_SHELL_KEYS = ["__hasUrlCity", "is_gray", "has_gray", "clientIp", "sensitiveSeriesIdList"];4 5 const input = typeof params === "object" && params ? params : {};6 7 function clean(value) {8 return String(value == null ? "" : value).replace(/\s+/g, " ").trim();9 }10 11 function fail(message) {12 throw new Error(message);13 }14 15 function assertPlainObject(value, label) {16 if (!value || typeof value !== "object" || Array.isArray(value)) {17 fail(`${label} 返回的数据结构不符合预期,应为对象。`);18 }19 return value;20 }21 22 function requireText(value, label) {23 const text = clean(value);24 if (!text) {25 fail(`${label} 未包含稳定文本。`);26 }27 return text;28 }29 30 function parseScore(raw) {31 const n = Number(raw);32 if (!Number.isFinite(n) || n <= 0) return null;33 return Number((n / 100).toFixed(2));34 }35 36 function normalizeSeriesId(rawInput) {37 const raw = clean(rawInput);38 const match = raw.match(/series\/(\d+)/) || raw.match(/^(\d+)$/);39 if (!match) {40 fail("series_id 必须是车系数字 ID,或包含 /auto/series/<id> 的链接。");41 }42 return match[1];43 }44 45 function currentSeriesId() {46 const match = String(location.href).match(/series\/(\d+)/);47 return match ? match[1] : "";48 }49 50 function extractPageProps(html) {51 const match = String(html || "").match(/<script id="__NEXT_DATA__"[^>]*>([\s\S]*?)<\/script>/);52 if (!match) return null;53 try {54 const data = JSON.parse(match[1]);55 return data && data.props && data.props.pageProps ? data.props.pageProps : null;56 } catch {57 return null;58 }59 }60 61 function isFallbackShell(pageProps) {62 if (!pageProps || typeof pageProps !== "object") return true;63 return Object.keys(pageProps).filter((key) => !FALLBACK_SHELL_KEYS.includes(key)).length === 0;64 }65 66 async function fetchPageProps(path, label) {67 const response = await fetch(`${DCD_BASE}${path}`, {68 credentials: "include",69 headers: { "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8" },70 });71 if (!response.ok) {72 fail(`${label} 请求失败:HTTP ${response.status}`);73 }74 const pageProps = extractPageProps(await response.text());75 if (!pageProps) {76 fail(`${label} 未返回页面数据,页面结构可能已变化或请求被拦截。`);77 }78 if (isFallbackShell(pageProps)) {79 fail(`${label} 返回空壳页面,车系 ID 可能不存在或页面入口已变化。`);80 }81 return pageProps;82 }83 84 function countOnSaleModels(carModelsData) {85 const tabs = carModelsData?.tab_list;86 if (!Array.isArray(tabs)) return null;87 const tab = tabs.find((item) => item?.tab_key === "online_all") || tabs[0];88 const data = tab?.data;89 if (!Array.isArray(data)) return null;90 return data.filter((item) => item?.info?.car_id ?? item?.info?.id).length;91 }92 93 function formatRank(section) {94 const top = section?.list?.[0];95 if (!top || !top.rank) return "";96 const name = clean(section.rank_name || top.rank_name);97 return name ? `${name} 第${top.rank}名` : `第${top.rank}名`;98 }99 100 function parseSeries(pageProps, seriesId) {101 const head = assertPlainObject(pageProps?.seriesHomeHead, "车系概览");102 const score = pageProps.scoreSimpleInfo || {};103 const rank = pageProps.rankData || {};104 const usedPrice = head.sh_low_Price || head.sh_high_price105 ? `${head.sh_low_Price ?? "?"}-${head.sh_high_price ?? "?"}万`106 : "";107 108 return [109 { field: "series_id", value: seriesId },110 { field: "name", value: requireText(head.series_name, "车系名称") },111 { field: "brand", value: requireText(head.brand_name, "品牌名称") },112 { field: "sub_brand", value: clean(head.sub_brand_name) },113 { field: "official_price", value: head.has_official_price ? clean(head.official_price) : "" },114 { field: "dealer_price", value: head.has_dealer_price ? clean(head.dealer_price) : "" },115 { field: "used_price", value: usedPrice },116 { field: "score", value: parseScore(score.score) },117 {118 field: "review_count",119 value: Number.isFinite(Number(score.total_review_count)) ? Number(score.total_review_count) : null,120 },121 { field: "sale_rank", value: formatRank(rank.sale) },122 { field: "score_rank", value: formatRank(rank.score) },123 { field: "models", value: countOnSaleModels(pageProps.carModelsData) },124 { field: "url", value: `${DCD_BASE}/auto/series/${seriesId}` },125 ];126 }127 128 const seriesId = normalizeSeriesId(input.series_id ?? input.seriesId ?? input.url ?? currentSeriesId());129 const pageProps = await fetchPageProps(`/auto/series/${seriesId}`, `车系 ${seriesId}`);130 return parseSeries(pageProps, seriesId);131})();