淘宝拍立淘(item_search_img)接口允许开发者上传一张商品图片,返回与之相似的商品列表,广泛用于比价、选品、竞品监控等场景。下面给出一份"纯 Java 版"爬取思路与示例代码,不依赖任何官方 SDK,也不带外链,方便快速集成到自有爬虫框架中。
一、核心流程
-
注册淘宝开放平台账号,创建应用,拿到 app_key / app_secret,并申请 taobao.item_search_img 权限。
-
把待搜索图片转成淘宝认可的"外部地址":
-
方案 A:先调淘宝 pic/upload 接口拿到 imgId;
-
方案 B:把图片直传 OSS/七牛等可公网访问的地址,复制 URL 即可。
-
-
按淘宝签名规范生成 sign(MD5,ASCII 升序 + app_secret 前后包夹)。
-
用 POST 方式提交到 eco.taobao.com/router/rest,Content-Type 为 application/x-www-form-urlencoded。
-
解析返回 JSON,提取 items→item 数组里的 title、price、pic_url、detail_url、sales 等字段。
二、关键参数
| 参数名 | 必填 | 说明 |
|---|---|---|
| method | 是 | 固定 taobao.item_search_img |
| app_key | 是 | 应用唯一标识 |
| timestamp | 是 | 格式 yyyy-MM-dd HH:mm:ss |
| format | 是 | json |
| v | 是 | 2.0 |
| sign_method | 是 | md5 |
| sign | 是 | 按规范生成的签名 |
| imgid | 是 | 图片 URL(或 upload 后的 id) |
| cat | 否 | 类目 ID,如 50010788 表示女装 |
| page | 否 | 页码,默认 1 |
三、Java 签名与请求示例
java
import java.io.*;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.*;
import org.json.JSONObject;
public class PailitaoCrawler {
private static final String APP_KEY = "your_app_key";
private static final String APP_SECRET = "your_app_secret";
private static final String GATEWAY = "https://eco.taobao.com/router/rest";
public static void main(String[] args) throws Exception {
String imgUrl = "https://your-domain.com/demo.jpg"; // 公网可访问
String json = searchByImage(imgUrl, 1);
System.out.println(json);
}
public static String searchByImage(String imgUrl, int page) throws Exception {
Map<String, String> params = new LinkedHashMap<>();
params.put("method", "taobao.item_search_img");
params.put("app_key", APP_KEY);
params.put("timestamp", new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
params.put("format", "json");
params.put("v", "2.0");
params.put("sign_method", "md5");
params.put("imgid", imgUrl);
params.put("page", String.valueOf(page));
String sign = generateSign(params, APP_SECRET);
params.put("sign", sign);
String body = buildQuery(params);
return httpPost(GATEWAY, body);
}
private static String generateSign(Map<String, String> params, String secret) throws Exception {
List<String> keys = new ArrayList<>(params.keySet());
Collections.sort(keys);
StringBuilder sb = new StringBuilder(secret);
for (String k : keys) sb.append(k).append(params.get(k));
sb.append(secret);
byte[] bytes = MessageDigest.getInstance("MD5").digest(sb.toString().getBytes(StandardCharsets.UTF_8));
StringBuilder hex = new StringBuilder();
for (byte b : bytes) hex.append(String.format("%02X", b));
return hex.toString();
}
private static String buildQuery(Map<String, String> params) throws Exception {
StringBuilder sb = new StringBuilder();
for (Map.Entry<String, String> e : params.entrySet()) {
if (sb.length() > 0) sb.append('&');
sb.append(e.getKey()).append('=')
.append(URLEncoder.encode(e.getValue(), "UTF-8"));
}
return sb.toString();
}
private static String httpPost(String url, String body) throws Exception {
java.net.HttpURLConnection conn = (java.net.HttpURLConnection) new java.net.URL(url).openConnection();
conn.setRequestMethod("POST");
conn.setDoOutput(true);
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
try (OutputStream os = conn.getOutputStream()) {
os.write(body.getBytes(StandardCharsets.UTF_8));
}
try (BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()))) {
StringBuilder res = new StringBuilder();
String line;
while ((line = br.readLine()) != null) res.append(line);
return res.toString();
}
}
}
四、返回数据快速解析
返回片段示例(已格式化):
javascript
{
"item_search_img_response": {
"items": {
"item": [
{
"title": "日系宽松卫衣",
"price": "89.00",
"pic_url": "https://img.alicdn.com/xxx.jpg",
"detail_url": "https://item.taobao.com/id=xxx",
"sales": 1203
}
]
}
}
}
用 org.json 或 fastjson 取出数组即可落地到数据库/ES。
五、常见踩坑
-
sign 错误:确保 ASCII 升序、value 不编码、前后都带 secret。
-
图片地址防盗链:外部地址必须 200 且返回 image/*,否则淘宝会报 5001。
-
频率限制:默认 5000 次/日,超出会 403,需要缓存或队列削峰。
-
类目过滤:cat 传错会返回空列表,可先用 itemcats 接口拉全量映射表。
-
分页最多 100 页,每页 20 条,即 2000 条上限,深度翻页意义不大。