Files
wehub-resource-sync 9b395f5cc3
E2E Headed Chrome / e2e-headed (macos-15) (push) Has been cancelled
E2E Headed Chrome / e2e-headed (ubuntu-latest) (push) Has been cancelled
E2E Headed Chrome / e2e-headed (windows-latest) (push) Has been cancelled
CI / build (macos-latest) (push) Has been cancelled
CI / build (ubuntu-latest) (push) Has been cancelled
CI / build (windows-latest) (push) Has been cancelled
CI / unit-test (push) Has been cancelled
CI / bun-test (push) Has been cancelled
CI / adapter-test (push) Has been cancelled
CI / smoke-test (macos-latest) (push) Has been cancelled
CI / smoke-test (ubuntu-latest) (push) Has been cancelled
Security Audit / audit (push) Has been cancelled
Build Chrome Extension / build (push) Has been cancelled
Trigger Website Rebuild (Docs Updated) / dispatch (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:39:48 +08:00

104 lines
3.5 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* autohome score — 口碑 (owner-rating) summary for a car series.
*
* Reads `__NEXT_DATA__.props.pageProps.baseData` (+ `qualityData`) from the
* koubei page `k.autohome.com.cn/<seriesId>`: overall rating, per-dimension
* scores, level, guide price, the reliability PPH (每百辆车故障数), and the
* competitor comparison. All unsigned, login-free. Returns a key/value sheet.
*
* Note: Autohome's per-review TEXT list loads from a separate signed XHR and
* is intentionally not scraped — this command surfaces the aggregate only.
*/
import { cli, Strategy } from '@jackwener/opencli/registry';
import {
AH_KOUBEI_BASE,
SCORE_COLUMNS,
CommandExecutionError,
EmptyResultError,
assertPlainObject,
ahFetch,
clean,
extractPageProps,
normalizeSeriesId,
} from './utils.js';
/** Number or null. */
function num(v) {
const n = Number(v);
return Number.isFinite(n) ? n : null;
}
/**
* Pure parser: koubei pageProps → field/value rows. Exported for unit tests.
*/
export function parseScore(pp, seriesId) {
const bd = assertPlainObject(pp?.baseData, 'autohome baseData');
const qd = (pp && pp.qualityData) || {};
const competitors = (Array.isArray(bd.cmpSeriesScore) ? bd.cmpSeriesScore : [])
.map((c) => {
const name = clean(c.seriesname || c.seriesName);
const s = c.average || c.score;
return name ? `${name}(${s})` : '';
})
.filter(Boolean)
.slice(0, 4)
.join(', ');
const fields = [
['series_id', String(seriesId)],
['name', clean(bd.seriesname)],
['brand', clean(bd.brandName)],
['level', clean(bd.levelname)],
['guide_price', bd.pricerange ? `${clean(bd.pricerange)}万` : ''],
['overall', num(bd.average ?? bd.seriesAverage)],
];
for (const axis of (Array.isArray(bd.seriesScoreList) ? bd.seriesScoreList : [])) {
const label = clean(axis.typeName);
if (label) fields.push([label, num(axis.score)]);
}
fields.push(['pph_每百车故障', num(qd.pph)]);
fields.push(['review_users', num(qd.userCount)]);
fields.push(['competitors', competitors]);
fields.push(['url', `${AH_KOUBEI_BASE}/${seriesId}`]);
return fields.map(([field, value]) => ({ field, value }));
}
cli({
site: 'autohome',
name: 'score',
access: 'read',
aliases: ['koubei', 'rating'],
description: '汽车之家车系口碑评分(总分 + 各维度 + 故障率PPH + 竞品对比,免登录)',
strategy: Strategy.PUBLIC,
browser: false,
args: [
{ name: 'series_id', required: true, positional: true, help: '车系 ID(来自 brand 的 series_id,或 k.autohome.com.cn/<id> URL' },
],
columns: SCORE_COLUMNS,
func: async (args) => {
const seriesId = normalizeSeriesId(args.series_id);
const html = await ahFetch(`${AH_KOUBEI_BASE}/${seriesId}`, `score ${seriesId}`);
const pp = extractPageProps(html);
if (!pp) {
throw new CommandExecutionError(
`autohome score ${seriesId}`,
'No koubei data found — the series id may be wrong, or Autohome changed its page.',
);
}
const rows = parseScore(pp, seriesId);
const map = Object.fromEntries(rows.map((r) => [r.field, r.value]));
if (!map.name && map.overall == null) {
throw new EmptyResultError(
`autohome score ${seriesId}`,
'This series has no koubei rating yet.',
);
}
return rows;
},
});