# 淘宝/天猫选择器实测记录 > 实测日期:2026-08-11 > 页面:`detail.tmall.com/item.htm?id=960057430812`、`item.taobao.com/item.htm?id=1060253247160` > 方法:反向扫描(dump 页面实际类名前缀,而非猜名字去查) > 对应实现:`extension/src/profiles/taobao.ts` --- ## 1. 结论 **两站 DOM 完全一致**,同一套前端(`PageFramework--` / `tbpc-layout` / `keyInfo--` 骨架相同),一份 profile 覆盖淘宝与天猫。 类名形如 `mainTitle--HASH`,是 CSS Modules 产物:**语义前缀稳定,哈希后缀每次构建变**。因此选择器一律写 `[class*="前缀--"]`。 结尾的 `--` 不能省——它把父容器和子元素区分开:`generalParamsInfoItem--` 不会误命中 `generalParamsInfoItemTitle--`。 --- ## 2. 确证的选择器 | 目标 | 选择器 | 实测证据 | |---|---|---| | 标题 | `[class*="mainTitle--"]` | n=2 imgs=0,纯文本节点。天猫「迷你特工队玩具X弗特…」淘宝「对插双刀流发光双刃剑…」 | | 标题兜底 | `[class*="MainTitle--"]` `[class*="ItemTitle--"]` | 外层容器,天猫版带图标(imgs=2) | | 价格 | `[class*="highlightPrice--"]` | 淘宝 `¥5.2`,天猫 `秒杀价¥27.72` | | 价格兜底 | `[class*="priceWrap--"]` | 会带上「优惠前¥36.8」,故仅兜底 | | 主图 | `[class*="picGallery--"] img` / `#picGalleryEle` | 天猫 imgs=6,淘宝 imgs=7 | | 主图缩略 | `[class*="thumbnailPic--"]` | n=5/6 | | SKU 容器 | `[class*="valueItem--"]` | **n=22 imgs=22**(天猫),每项恰含一张 img | | SKU 规格名 | `[class*="valueItemText--"]` | 「特工x武器小【弗特】2种形态- 可变形」 | | 参数项 | `[class*="generalParamsInfoItem--"]` | Title=「品牌」SubTitle=「劣狐狐(模玩)」 | | 详情区 | `[class*="tabDetailWrap--"]` `[class*="detailInfo--"]` | 淘宝 imgs=7 | --- ## 3. 两个反直觉的点 ### 3.1 淘宝 SKU 是真实 ``,不是 CSS 背景图 **与 1688 相反。** 探测数据: ``` valueItem n=22 imgs=22 ← 容器,每项含 1 张 img valueItemImgWrap n=22 imgs=22 ← 图片包裹层 valueItemImg n=22 imgs=0 ← img 元素本身(querySelectorAll('img') 查自己得 0) valueItemText n=22 imgs=0 ← 规格名文本 ``` `imgs=0` 恰恰证明 `valueItemImg--` 就是 ``。所以淘宝 profile **不能**用 `srcProps: ['backgroundImage']`(1688 必须用)。 引擎为此补了一段兜底:选择器命中容器且未取到 URL 时,往下找一层 `querySelector('img')`(见 `collector/image.ts`)。 ### 3.2 主图组不设 minWidth `picGallery--` 里同时有大图和缩略图,缩略图 `naturalWidth` 只有 60 左右。按 `minWidth: 200` 过滤会把主图**全部误杀**。 不过滤是安全的,因为 `toOriginalUrl()` 会把两种尺寸都还原成同一个原图 URL,`dedupeKey` 相同即自动去重。 --- ## 4. 两个失效的既有假设 ### 4.1 页面上没有 `

` `document.querySelector('h1')` 返回 null。旧 profile 里的 `.tb-detail-hd h1`、`h1[data-spm]`、裸 `h1` 全部无效,`readySelectors` 也不能用 `h1` 探活。 ### 4.2 没有 JSON-LD 两站 `script[type="application/ld+json"]` 都是**空数组**。 `plan-revision.md` R3 里"JSON-LD 比 DOM 选择器稳定一个数量级"的推测**对淘宝不成立**(对 Ozon 仍待验证)。淘宝只能走 DOM。 --- ## 5. 待办 ### 5.1 desc 暂不采集 `detailInfo--` 容器里混着用户评价、参数信息、图文详情三块,`extract: 'join'` 出来是无法使用的一坨。考虑到 1688/淘宝的中文文案对 Ozon 价值本就低(需重写),一期跳过。 若以后要采,需先定位「图文详情」那个 `tabDetailItem--` 的稳定标识。 ### 5.2 详情图需要用户操作 图文详情是懒加载 + tab 切换。用户不点开「图文详情」tab 就采不到。`scan.ts` 已有 `stats.detail === 0` 的警告。 ### 5.3 内嵌数据待评估 `window` 上有一批可能有用的键,但**当前架构读不到**——MV3 content script 默认跑在 isolated world,看不见页面 `window`。要读需 `world: 'MAIN'` 或注入 script 标签。 值得关注的: ``` __general_skupanel_cache_data ← 可能含完整 SKU 结构 __ICE_DATA_LOADER__ ← ICE 框架的数据层 __itempage_openapi g_config ``` 如果 `__general_skupanel_cache_data` 真含 SKU 数据,比 DOM 抓 22 个 `valueItem--` 可靠得多。二期评估。 --- ## 6. 复测脚本 改版后重跑,对照本文档的证据列: ```js (() => { const KEY = /(title|name|main|pic|img|gallery|thumb|sku|value|desc|detail|param|price)/i; const pfx = new Map(); document.querySelectorAll('*').forEach(el => { if (typeof el.className !== 'string') return; el.className.split(/\s+/).forEach(c => { const m = c.match(/^([A-Za-z][A-Za-z0-9]*)--/); if (!m || !KEY.test(m[1])) return; const r = pfx.get(m[1]) ?? { prefix: m[1], n: 0, imgs: 0, sample: '' }; r.n++; r.imgs += el.querySelectorAll('img').length; if (!r.sample) r.sample = (el.textContent || '').trim().slice(0, 40); pfx.set(m[1], r); }); }); console.table([...pfx.values()].sort((a, b) => b.n - a.n).slice(0, 40)); })(); ```