引言
很多开发者在问:"如何稳定采集淘宝商品数据?""浏览器方案到底是怎么实现的?"
淘宝作为国内最大的电商平台,反爬机制极为复杂。本文从技术角度,完整讲解浏览器方案实现淘宝商品数据采集的每一个环节。
一、整体架构设计
text
┌─────────────────────────────────────────────────────────────┐
│ 淘宝商品采集流程 │
├─────────────────────────────────────────────────────────────┤
│ │
│ URL输入 ──→ 浏览器加载 ──→ 等待策略 ──→ DOM提取 │
│ │ │ │ │ │
│ ▼ ▼ ▼ ▼ │
│ 链接验证 Chromium内核 三重等待 素材识别 │
│ │
│ ───────────────────────────────────────────────────────── │
│ │
│ DOM提取 ──→ 智能分类 ──→ 图片处理 ──→ 下载保存 │
│ │ │ │ │ │
│ ▼ ▼ ▼ ▼ │
│ 主图/属性 分类算法 原图转换 队列下载 │
│ /详情图 │
│ │
└─────────────────────────────────────────────────────────────┘
二、浏览器内核嵌入方案
2.1 技术选型
cpp
// 使用CEF框架嵌入Chromium内核
class TaobaoCollector {
private:
CefRefPtr<CefBrowser> browser_;
CefRefPtr<CefRequestContext> request_context_;
public:
bool Initialize() {
CefSettings settings;
settings.no_sandbox = true;
settings.windowless_rendering_enabled = true;
settings.remote_debugging_port = 0; // 关闭调试端口
// 设置缓存路径
CefString(&settings.cache_path).FromString("./cache");
return CefInitialize(main_args, settings, app, nullptr);
}
void LoadTaobaoProduct(const std::string& url) {
CefWindowInfo window_info;
window_info.SetAsWindowless(0);
CefBrowserSettings browser_settings;
browser_settings.javascript = STATE_ENABLED;
browser_settings.image_loading = STATE_ENABLED;
browser_ = CefBrowserHost::CreateBrowserSync(
window_info, client, url, browser_settings,
request_context_, nullptr);
}
};
2.2 淘宝页面加载
淘宝页面包含大量JS和图片资源,需要合理的加载策略:
cpp
class PageLoadController {
public:
bool WaitForTaobaoPageLoad(CefRefPtr<CefBrowser> browser, int timeout = 15) {
auto start = std::chrono::steady_clock::now();
while (true) {
// 超时检查
if (GetElapsedSeconds(start) > timeout) {
return false;
}
// 1. 检查DOM状态
if (!IsDOMReady(browser)) {
Sleep(200);
continue;
}
// 2. 检查网络空闲(淘宝商品页有大量异步请求)
if (!IsNetworkIdle(browser)) {
Sleep(200);
continue;
}
// 3. 检查jQuery(淘宝依赖jQuery)
if (!IsJQueryLoaded(browser)) {
Sleep(200);
continue;
}
// 4. 额外等待懒加载图片
Sleep(500);
return true;
}
}
private:
bool IsDOMReady(CefRefPtr<CefBrowser> browser) {
std::string script = "document.readyState === 'complete'";
return ExecuteJavaScript(browser, script);
}
bool IsJQueryLoaded(CefRefPtr<CefBrowser> browser) {
std::string script = "typeof jQuery !== 'undefined'";
return ExecuteJavaScript(browser, script);
}
};
三、淘宝页面DOM提取
3.1 主图提取
javascript
// 提取淘宝主图
function extractTaobaoMainImages() {
const images = [];
// 淘宝主图容器选择器
const selectors = [
'.J_UlThumb', // 淘宝主图缩略图区
'.tb-thumb', // 天猫主图区
'.tb-main-pic', // 淘宝主图大图区
'.product-img-box' // 备用选择器
];
for (const selector of selectors) {
const container = document.querySelector(selector);
if (container) {
const imgs = container.querySelectorAll('img');
imgs.forEach(img => {
const src = getHighQualityUrl(img);
if (src) {
images.push({
url: src,
type: 'main'
});
}
});
break;
}
}
return images;
}
// 淘宝原图URL转换
function getHighQualityUrl(img) {
let src = img.src || img.getAttribute('data-src');
if (!src) return null;
// 去除淘宝缩略图尺寸后缀
// 例如: https://img.alicdn.com/xxx_50x50.jpg -> https://img.alicdn.com/xxx.jpg
src = src.replace(/_\d+x\d+\./g, '.');
src = src.replace(/\.sum\./g, '.');
return src;
}
3.2 属性图(SKU图)提取
javascript
// 提取淘宝属性图(颜色/尺码图)
function extractTaobaoSkuImages() {
const skuImages = [];
// 淘宝SKU容器选择器
const skuSelectors = [
'.tb-sku', // 淘宝SKU区
'.J_sku', // 天猫SKU区
'.sku', // 通用SKU区
'.tb-prop' // 属性区
];
for (const selector of skuSelectors) {
const container = document.querySelector(selector);
if (container) {
const imgs = container.querySelectorAll('img');
imgs.forEach(img => {
const src = getHighQualityUrl(img);
if (src) {
// 提取属性名称(颜色/尺码)
const skuName = getSkuName(img, container);
skuImages.push({
url: src,
type: 'sku',
name: skuName
});
}
});
break;
}
}
return skuImages;
}
// 提取SKU属性名称
function getSkuName(img, container) {
// 从alt属性获取
if (img.alt && !img.alt.includes('http')) {
return img.alt;
}
// 从父级元素获取
const parent = img.closest('.sku-item, .tb-sku-item');
if (parent) {
const title = parent.getAttribute('title') ||
parent.querySelector('.sku-name')?.textContent;
if (title) return title.trim();
}
return '属性图';
}
3.3 详情图提取
javascript
// 提取淘宝详情图
function extractTaobaoDetailImages() {
const detailImages = [];
// 淘宝详情容器选择器
const detailSelectors = [
'#description', // 淘宝详情容器
'.detail', // 天猫详情容器
'.desc', // 通用详情容器
'.J_DetailSection' // 淘宝新版详情
];
for (const selector of detailSelectors) {
const container = document.querySelector(selector);
if (container) {
const imgs = container.querySelectorAll('img');
imgs.forEach(img => {
const src = getHighQualityUrl(img);
if (src) {
detailImages.push({
url: src,
type: 'detail'
});
}
});
break;
}
}
return detailImages;
}
四、淘宝主图视频提取
javascript
// 提取淘宝主图视频
function extractTaobaoVideo() {
// 方法1:从video标签获取
const videoElement = document.querySelector('#J_ItemVideo video, .tb-video video');
if (videoElement && videoElement.src) {
return { url: videoElement.src, type: 'mp4' };
}
// 方法2:从页面数据获取
const pageData = document.querySelector('#page-data')?.innerHTML;
if (pageData) {
try {
const data = JSON.parse(pageData);
if (data.videoInfo && data.videoInfo.videoUrl) {
return { url: data.videoInfo.videoUrl, type: 'mp4' };
}
} catch(e) {}
}
// 方法3:从m3u8获取
const source = document.querySelector('video source');
if (source && source.src && source.src.includes('.m3u8')) {
return { url: source.src, type: 'm3u8' };
}
return null;
}
五、完整采集流程集成
python
class TaobaoProductCollector:
def __init__(self):
self.browser = None
def collect(self, url):
"""采集单个淘宝商品"""
result = {
'title': '',
'main_images': [],
'sku_images': [],
'detail_images': [],
'video': None
}
# 1. 加载页面
self.browser.load(url)
# 2. 等待页面完全加载
self.wait_for_load()
# 3. 提取商品标题
result['title'] = self.extract_title()
# 4. 提取主图
result['main_images'] = self.extract_main_images()
# 5. 提取属性图
result['sku_images'] = self.extract_sku_images()
# 6. 提取详情图
result['detail_images'] = self.extract_detail_images()
# 7. 提取视频
result['video'] = self.extract_video()
# 8. 保存到本地
self.save_to_local(result)
return result
def save_to_local(self, result):
"""保存到本地文件夹"""
base_path = f"./downloads/{self.sanitize_filename(result['title'])}"
# 创建目录结构
os.makedirs(f"{base_path}/主图", exist_ok=True)
os.makedirs(f"{base_path}/属性图", exist_ok=True)
os.makedirs(f"{base_path}/详情图", exist_ok=True)
# 保存主图
for i, img_url in enumerate(result['main_images'], 1):
self.download_image(img_url, f"{base_path}/主图/主图_{i}.jpg")
# 保存属性图
for img in result['sku_images']:
name = img.get('name', '属性图')
self.download_image(img['url'], f"{base_path}/属性图/{name}.jpg")
# 保存详情图
for i, img_url in enumerate(result['detail_images'], 1):
self.download_image(img_url, f"{base_path}/详情图/详情图_{i}.jpg")
# 保存视频
if result['video']:
self.download_video(result['video']['url'], f"{base_path}/视频.mp4")
六、实测数据
以一键存图为例,淘宝商品采集实测数据:
| 指标 | 数据 |
|---|---|
| 淘宝商品加载时间 | 2-4秒 |
| 主图提取成功率 | 100% |
| 属性图提取成功率 | 95%+ |
| 详情图提取成功率 | 100% |
| 视频提取成功率 | 95%+ |
| 单商品总耗时 | 3-5秒 |
七、总结
淘宝商品数据采集的核心技术要点:
| 环节 | 关键技术 |
|---|---|
| 内核嵌入 | CEF + Chromium |
| 页面等待 | 三重等待策略 |
| 主图提取 | 多选择器匹配 |
| 属性图提取 | SKU容器定位 + 属性名称关联 |
| 详情图提取 | 详情容器识别 |
| 视频提取 | video标签 + m3u8处理 |
百度搜索"一键存图"即可找到完整实现。