/** * 淘宝 / 天猫采集配置 * * 选择器全部来自真实页面实测(2026-08-11,两个商品页各跑一轮反向探测): * 天猫 detail.tmall.com/item.htm?id=960057430812 * 淘宝 item.taobao.com/item.htm?id=1060253247160 * 两站 DOM 完全一致(同一套前端),一份 profile 覆盖。 * * 类名是 CSS Modules 的 `语义前缀--哈希` 形式,哈希每次构建都变, * 所以一律用 `[class*="前缀--"]` 前缀匹配。 * * 结尾那个 `--` 不能省——它把父容器和子元素区分开: * `generalParamsInfoItem--` 不会误命中 `generalParamsInfoItemTitle--`。 * * 实测证据见 docs/extension/selectors-taobao.md */ import type { SiteProfile } from './types'; export const profileTaobao: SiteProfile = { id: 'taobao', name: '淘宝/天猫', urlPatterns: [ /^https:\/\/item\.taobao\.com\/item\.htm/, /^https:\/\/detail\.tmall\.com\/item\.htm/, ], extractItemId: (url) => url.match(/[?&]id=(\d+)/)?.[1] ?? null, // 页面上没有

,别再拿它探活 readySelectors: [ '[class*="mainTitle--"]', '[class*="picGallery--"]', '#picGalleryEle', ], readyTimeoutMs: 10_000, // 阿里系 CDN 规则与 1688 相同 defaultSrcProps: ['data-lazyload-src', 'data-src', 'currentSrc', 'src'], refererOrigin: 'https://www.taobao.com', textRules: [ { kind: 'title', // mainTitle-- 是纯文本节点(探测里 imgs=0),最干净 // ItemTitle-- / MainTitle-- 是外层容器,带图标,作兜底 // 注意:属性选择器区分大小写,三个都得写 selectors: [ '[class*="mainTitle--"]', '[class*="MainTitle--"]', '[class*="ItemTitle--"]', ], extract: 'first', required: true, }, { kind: 'price', // highlightPrice-- 是当前实际售价,两站一致 // priceWrap-- 是外层,会把"优惠前¥36.8"一起带进来,只作兜底 selectors: [ '[class*="highlightPrice--"]', '[class*="priceWrap--"]', ], extract: 'first', }, { kind: 'params', // generalParamsInfoItem-- 每项含 Title(键) + SubTitle(值) selectors: ['[class*="generalParamsInfoItem--"]'], extract: 'table', tableKeySelector: '[class*="ParamsInfoItemTitle--"]', tableValueSelector: '[class*="ParamsInfoItemSubTitle--"]', }, // desc 故意不采:详情容器 detailInfo-- 里混着用户评价、参数、图文详情, // join 出来是一坨无法使用的字符串。1688/淘宝的中文文案对 Ozon 价值也低 // (见 docs/extension/1688-taobao-implementation.md 采集优先级)。 ], imageGroups: [ { key: 'main', name: '主图', type: 'img', // picGallery-- 内含大图 + 缩略图,同一张图的两种尺寸 // toOriginalUrl() 剥掉尺寸后缀后 dedupeKey 相同,会自动去重 selectors: [ '[class*="picGallery--"] img', '#picGalleryEle img', '[class*="thumbnailPic--"]', ], // 不设 minWidth:缩略图 naturalWidth 只有 60 左右, // 按 200 过滤会把主图全误杀(原图靠 toOriginalUrl 还原) }, { key: 'sku', name: 'SKU图片', type: 'img', // ★ 与 1688 不同:淘宝 SKU 是真实 ,不是 CSS 背景图 // 探测证据:valueItem-- n=22 imgs=22(每项恰含一张 img) // 所以这里不能用 srcProps: ['backgroundImage'] selectors: [ '[class*="valueItem--"]', '[class*="valueItemImgWrap--"]', ], nameSelectors: ['[class*="valueItemText--"]'], minWidth: 20, minHeight: 20, }, { key: 'detail', name: '详情图', type: 'img', // 图文详情是懒加载的,需用户点开「图文详情」tab 或滚到底 selectors: [ '[class*="tabDetailWrap--"] img', '[class*="detailInfo--"] img', ], // detailInfo-- 同时包着「用户评价」区,买家晒单图能有 400-800px, // 光靠 minWidth 滤不掉。这些图带水印、质量差,不能采 excludeWithin: [ '[class*="Comment--"]', '[class*="comments--"]', '[class*="userInfo--"]', '[class*="rate"]', ], minWidth: 300, minHeight: 100, }, { key: 'video', name: '视频', type: 'video', selectors: ['[class*="picGallery--"] video', 'video'], }, ], };