Files
ozon-seller-kit/docs/extension/selectors-taobao.md
T
2026-08-11 17:09:23 +08:00

5.2 KiB
Raw Blame History

淘宝/天猫选择器实测记录

实测日期:2026-08-11 页面:detail.tmall.com/item.htm?id=960057430812item.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 是真实 <img>,不是 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-- 就是 <img>。所以淘宝 profile 不能srcProps: ['backgroundImage']1688 必须用)。

引擎为此补了一段兜底:选择器命中容器且未取到 URL 时,往下找一层 querySelector('img')(见 collector/image.ts)。

3.2 主图组不设 minWidth

picGallery-- 里同时有大图和缩略图,缩略图 naturalWidth 只有 60 左右。按 minWidth: 200 过滤会把主图全部误杀

不过滤是安全的,因为 toOriginalUrl() 会把两种尺寸都还原成同一个原图 URL,dedupeKey 相同即自动去重。


4. 两个失效的既有假设

4.1 页面上没有 <h1>

document.querySelector('h1') 返回 null。旧 profile 里的 .tb-detail-hd h1h1[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. 复测脚本

改版后重跑,对照本文档的证据列:

(() => {
  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));
})();