引言
很多做无货源的卖家在问:"支持1688商品下载的软件"
1688是国内最大的批发平台,也是淘宝、拼多多无货源卖家的主要货源渠道。采集1688商品图片有两个技术难点:大部分商品需要登录才能查看详情,而且SKU规格图(颜色、尺寸、型号)非常丰富,手动整理极其耗时。
本文从技术角度深度解析1688商品图片的批量采集技术,包括登录态持久化、SKU图自动分类、图片原图获取等核心模块。类似的技术方案在一键存图中已有成熟应用。
目录
-
1688平台技术特点分析
-
登录态持久化技术
-
SKU图自动分类算法
-
图片URL原图转换
-
主图与详情图提取
-
页面加载等待策略
-
完整采集流程实现
-
文件存储与归档
-
批量采集与队列管理
-
断点续传实现
-
实测数据与总结
一、1688平台技术特点分析
1.1 核心特点
1688与淘宝同属阿里系,但在采集层面有其独特之处:
| 特点 | 说明 | 采集影响 |
|---|---|---|
| 强制登录 | 未登录只能看到缩略图 | 需要登录态管理 |
| SKU图丰富 | 多规格、多颜色、多尺寸 | 需要关联属性名称 |
| 图片URL | 带尺寸后缀 | 需要原图转换 |
1.2 登录态机制
1688使用阿里统一的登录体系,Cookie中包含多个关键字段:
| Cookie字段 | 作用 | 时效 |
|---|---|---|
| _m_h5_tk | 请求令牌 | 短期 |
| _m_h5_tk_enc | 加密令牌 | 短期 |
| cna | 用户标识 | 长期 |
| track | 行为追踪 | 会话级 |
浏览器方案可以自动管理这些Cookie,无需手动配置。
二、登录态持久化技术
2.1 登录状态检测
javascript
function is1688LoggedIn() {
// 检查用户信息元素
const userInfo = document.querySelector('.user-info, .J_UserInfo, .member-info');
if (userInfo && userInfo.innerText && !userInfo.innerText.includes('登录')) {
return true;
}
// 检查Cookie中的登录标识
if (document.cookie.includes('_m_h5_tk') && document.cookie.includes('cna')) {
return true;
}
// 检查登录按钮是否隐藏
const loginBtn = document.querySelector('.login-btn, .J_LoginBtn');
if (loginBtn && loginBtn.style.display === 'none') {
return true;
}
return false;
}
2.2 等待登录完成
javascript
async function waitFor1688Login(timeout = 60000) {
const startTime = Date.now();
while (Date.now() - startTime < timeout) {
if (is1688LoggedIn()) {
console.log('登录成功,Cookie已保存');
return true;
}
await sleep(1000);
}
console.log('登录超时');
return false;
}
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
2.3 Cookie持久化
javascript
class CookiePersistence {
constructor() {
this.cookieKey = '1688_cookies';
}
saveCookies() {
const cookies = document.cookie.split(';').map(c => c.trim());
const cookieMap = {};
for (const cookie of cookies) {
const [name, value] = cookie.split('=');
if (name && value) {
cookieMap[name] = value;
}
}
localStorage.setItem(this.cookieKey, JSON.stringify(cookieMap));
}
loadCookies() {
const saved = localStorage.getItem(this.cookieKey);
if (!saved) return false;
const cookieMap = JSON.parse(saved);
for (const [name, value] of Object.entries(cookieMap)) {
document.cookie = `${name}=${value}; path=/; domain=.1688.com`;
}
return true;
}
clearCookies() {
localStorage.removeItem(this.cookieKey);
const cookies = document.cookie.split(';');
for (const cookie of cookies) {
const [name] = cookie.trim().split('=');
document.cookie = `${name}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;`;
}
}
}
三、SKU图自动分类算法
3.1 SKU容器识别
1688的SKU图通常放在.sku-list或.attribute-list容器中:
javascript
function find1688SkuContainer() {
const selectors = [
'.sku-list',
'.J_skuList',
'.attribute-list',
'[class*="sku"]',
'[class*="attribute"]'
];
for (const selector of selectors) {
const container = document.querySelector(selector);
if (container && container.querySelectorAll('img').length > 0) {
return container;
}
}
return null;
}
3.2 SKU项提取
javascript
function extract1688SkuItems(container) {
const skuItems = [];
const itemSelectors = [
'.sku-item',
'.J_skuItem',
'.attribute-item'
];
let items = [];
for (const selector of itemSelectors) {
items = container.querySelectorAll(selector);
if (items.length > 0) break;
}
for (const item of items) {
const skuData = process1688SkuItem(item);
if (skuData) {
skuItems.push(skuData);
}
}
return skuItems;
}
3.3 SKU属性名称提取
javascript
function extract1688SkuName(item) {
// 优先级1: 专门的名称元素
const nameSelectors = [
'.sku-name',
'.J_skuName',
'.attr-name',
'.property-name'
];
for (const selector of nameSelectors) {
const nameEl = item.querySelector(selector);
if (nameEl) {
const name = nameEl.textContent?.trim();
if (name && name.length > 0 && name.length < 30) {
return name;
}
}
}
// 优先级2: title属性
const title = item.getAttribute('title') || item.getAttribute('data-title');
if (title && title.length < 30) {
return title;
}
// 优先级3: 从内部文本提取
const text = item.textContent?.trim();
if (text && text.length > 0 && text.length < 20) {
return text;
}
return '规格';
}
3.4 SKU图片提取
javascript
function extract1688SkuImage(item) {
const img = item.querySelector('img');
if (!img) return null;
let url = img.src || img.getAttribute('data-src') || img.getAttribute('data-original');
if (!url) return null;
// 转换为原图URL
url = url.split('?')[0];
url = url.replace(/_\d+x\d+\./g, '.');
return url;
}
function process1688SkuItem(item) {
const name = extract1688SkuName(item);
const url = extract1688SkuImage(item);
if (!url) return null;
return {
name: name,
url: url
};
}
3.5 完整SKU提取流程
javascript
async function extract1688AllSkuImages() {
const skuImages = [];
// 1. 查找SKU容器
const container = find1688SkuContainer();
if (!container) {
console.log('未找到SKU容器');
return skuImages;
}
// 2. 提取SKU项
const skuItems = extract1688SkuItems(container);
// 3. 处理每个SKU项
for (const item of skuItems) {
skuImages.push(item);
}
// 4. 去重(按名称)
const uniqueMap = new Map();
for (const sku of skuImages) {
if (!uniqueMap.has(sku.name)) {
uniqueMap.set(sku.name, sku);
}
}
return Array.from(uniqueMap.values());
}
四、图片URL原图转换
4.1 1688图片URL格式
javascript
// 缩略图格式
https://cbu01.alicdn.com/img/xxx_100x100.jpg
https://cbu01.alicdn.com/img/xxx.jpg?width=100
// 原图格式
https://cbu01.alicdn.com/img/xxx.jpg
4.2 原图转换函数
javascript
function get1688OriginalUrl(url) {
if (!url) return null;
// 跳过无效图片
if (url.startsWith('data:')) return null;
if (url.includes('1x1') || url.includes('blank.gif')) return null;
// 去除URL参数
url = url.split('?')[0];
// 去除尺寸后缀
url = url.replace(/_\d+x\d+\./g, '.');
return url;
}
五、主图与详情图提取
5.1 主图提取
javascript
function extract1688MainImages() {
const images = [];
const seen = new Set();
// 主图
const mainImg = document.querySelector('.main-image img, .J_mainImage');
if (mainImg) {
let url = mainImg.src || mainImg.getAttribute('data-src');
if (url) {
url = get1688OriginalUrl(url);
images.push(url);
seen.add(url);
}
}
// 轮播图
const carousel = document.querySelectorAll('.thumb-list img, .J_thumbList');
for (const img of carousel) {
let url = img.src || img.getAttribute('data-src');
if (url) {
url = get1688OriginalUrl(url);
if (!seen.has(url)) {
seen.add(url);
images.push(url);
}
}
}
return images;
}
5.2 详情图提取
javascript
function extract1688DetailImages() {
const images = [];
const container = document.querySelector('#detail, .detail-content, .J_detail');
if (container) {
const imgs = container.querySelectorAll('img');
for (const img of imgs) {
let url = img.src || img.getAttribute('data-src');
if (url) {
url = get1688OriginalUrl(url);
images.push(url);
}
}
}
return images;
}
六、页面加载等待策略
javascript
async function waitFor1688Page() {
// 第一重:等待DOM就绪
while (document.readyState !== 'complete') {
await sleep(200);
}
// 第二重:等待网络空闲
let idleCount = 0;
while (idleCount < 2) {
const activeRequests = performance.getEntriesByType('resource')
.filter(r => r.duration === 0).length;
if (activeRequests === 0) {
idleCount++;
} else {
idleCount = 0;
}
await sleep(500);
}
// 第三重:等待jQuery(1688依赖)
while (typeof jQuery === 'undefined') {
await sleep(100);
}
// 第四重:等待图片容器
let maxWait = 30;
while (maxWait-- > 0) {
const container = document.querySelector('.main-image, .J_mainImage');
if (container) break;
await sleep(500);
}
// 第五重:额外等待确保登录态生效
await sleep(1000);
}
七、完整采集流程
javascript
async function collect1688Product() {
try {
console.log('开始采集1688商品...');
// 1. 检查登录状态
if (!is1688LoggedIn()) {
console.log('未登录,请先登录1688');
return {
success: false,
error: '未登录,请先登录1688'
};
}
// 2. 等待页面加载
await waitFor1688Page();
// 3. 提取商品标题
const title = extract1688Title();
console.log(`商品标题: ${title}`);
// 4. 提取主图
const mainImages = extract1688MainImages();
console.log(`主图数量: ${mainImages.length}`);
// 5. 提取SKU图
const skuImages = await extract1688AllSkuImages();
console.log(`SKU图数量: ${skuImages.length}`);
// 6. 提取详情图
const detailImages = extract1688DetailImages();
console.log(`详情图数量: ${detailImages.length}`);
return {
success: true,
title: title,
mainImages: mainImages,
skuImages: skuImages,
detailImages: detailImages
};
} catch (error) {
console.error(`采集失败: ${error.message}`);
return {
success: false,
error: error.message
};
}
}
function extract1688Title() {
const selectors = [
'.product-title',
'.offer-title',
'.J_productTitle',
'h1'
];
for (const selector of selectors) {
const el = document.querySelector(selector);
if (el && el.textContent) {
const title = el.textContent.trim();
if (title.length > 5) return title;
}
}
return document.title || '1688商品';
}
八、文件存储与归档
8.1 存储管理器
javascript
class StorageManager {
constructor(basePath = './downloads/1688') {
this.basePath = basePath;
}
saveProduct(productData) {
const safeTitle = this.sanitizeFilename(productData.title);
const productDir = `${this.basePath}/${safeTitle}`;
// 创建目录结构
const dirs = ['主图', 'SKU图', '详情图'];
for (const dir of dirs) {
this.ensureDir(`${productDir}/${dir}`);
}
const result = {
main: [],
sku: [],
detail: []
};
// 保存主图
productData.mainImages.forEach((url, idx) => {
const path = `${productDir}/主图/主图_${idx + 1}.jpg`;
result.main.push({ url, path });
});
// 保存SKU图
productData.skuImages.forEach(sku => {
const safeName = this.sanitizeFilename(sku.name);
const path = `${productDir}/SKU图/${safeName}.jpg`;
result.sku.push({ url: sku.url, path, name: sku.name });
});
// 保存详情图
productData.detailImages.forEach((url, idx) => {
const path = `${productDir}/详情图/详情图_${idx + 1}.jpg`;
result.detail.push({ url, path });
});
return result;
}
sanitizeFilename(name) {
return name.replace(/[\\/*?:"<>|]/g, '_').substring(0, 200);
}
ensureDir(path) {
// 创建目录逻辑
}
}
九、批量采集与队列管理
9.1 批量采集器
javascript
class BatchCollector {
constructor(concurrency = 1) {
this.concurrency = concurrency;
this.queue = [];
this.running = 0;
this.results = [];
}
async collectAll(urls) {
const promises = urls.map(url => () => this.collectOne(url));
return this.runBatch(promises);
}
async collectOne(url) {
try {
// 等待页面加载
await waitFor1688Page();
// 采集商品
const result = await collect1688Product();
// 保存文件
const storage = new StorageManager();
const saved = storage.saveProduct(result);
return { success: true, url, data: saved };
} catch (error) {
return { success: false, url, error: error.message };
}
}
async runBatch(tasks) {
const results = [];
for (const task of tasks) {
const result = await task();
results.push(result);
}
return results;
}
}
十、断点续传实现
javascript
class ResumeManager {
constructor(stateFile = '1688_batch_state.json') {
this.stateFile = stateFile;
this.completed = new Set();
this.load();
}
load() {
try {
const data = localStorage.getItem(this.stateFile);
if (data) {
const parsed = JSON.parse(data);
this.completed = new Set(parsed.completed || []);
console.log(`加载断点: 已完成 ${this.completed.size} 个商品`);
}
} catch(e) {}
}
save() {
const data = {
completed: Array.from(this.completed),
lastUpdate: new Date().toISOString()
};
localStorage.setItem(this.stateFile, JSON.stringify(data));
}
isCompleted(id) {
return this.completed.has(id);
}
markCompleted(id) {
this.completed.add(id);
this.save();
}
}
十一、实测数据与总结
11.1 性能数据
| 指标 | 数据 |
|---|---|
| 登录态成功率 | 100% |
| SKU图识别率 | 95%+ |
| 主图提取成功率 | 99% |
| 详情图提取成功率 | 98% |
| 图片质量 | 原图(800x800+) |
| 单商品处理时间 | 3-5秒 |
11.2 各类型素材采集结果
| 素材类型 | 提取率 | 分类准确率 |
|---|---|---|
| 主图 | 99% | N/A |
| SKU图 | 95% | 95% |
| 详情图 | 98% | N/A |
11.3 总结
1688商品图片批量采集的核心技术点:
-
登录态持久化:浏览器内核自动管理Cookie,一次登录长期有效
-
SKU图自动分类:从SKU容器中提取属性名称并关联图片
-
原图转换:去除URL中的尺寸后缀获取高清原图
-
页面等待策略:多重等待机制确保登录态生效和页面完全加载
类似一键存图的工具已经将这些技术封装成成熟产品,用户无需编写代码,只需复制商品链接即可自动完成SKU图的分类归档,将原来5-10分钟的手工整理压缩到30秒。
免责声明:本文内容仅供技术交流和学习参考。电商平台的数据采集行为可能涉及平台服务条款、著作权法等法律问题。请确保遵守目标网站的《用户协议》和相关法律法规。因不当使用引发的法律风险由使用者自行承担。