在电商应用市场(如应用宝、App Store)中,选品比价类APP是用户量最大的工具型应用之一。这类应用的核心能力在于实时、准确地获取淘宝、京东、1688等多平台的商品数据,为用户提供"同款比价""历史价格追踪""促销提醒"等核心功能。本文将深入讲解如何通过淘宝商品详情API接口(taobao.item.get)获取商品数据,并构建一套完整的选品比价系统。
一、淘宝商品详情API接口概述
1.1 核心接口介绍
淘宝开放平台(TOP)提供的商品详情API是获取淘宝/天猫商品结构化数据的核心通道,主要接口包括:
表格
| 接口名称 | 功能描述 | 适用场景 |
taobao.item.get |
获取单个商品全量详情 | 商品详情页展示、比价分析 |
taobao.items.list.get |
批量获取商品列表 | 商品库同步、批量监控 |
taobao.tbk.item.get |
淘宝客商品详情 | CPS推广、返利应用 |
taobao.item.search |
关键词搜索商品 | 选品发现、竞品监控 |
taobao.item.reviews.get |
获取商品评价 | 口碑分析、差评预警 |
本文以 taobao.item.get 为核心,详细介绍其调用方式、签名算法和数据解析。该接口支持获取商品标题、价格、库存、销量、SKU规格、图片、评价等全量信息,是选品比价应用的数据基石。
1.2 接口核心能力
plain
┌─────────────────────────────────────────┐ │ taobao.item.get 数据能力 │ ├─────────────────────────────────────────┤ │ 基础信息:标题、副标题、类目、品牌 │ │ 价格信息:售价、原价、促销价、优惠券 │ │ 库存信息:总库存、SKU库存、预售状态 │ │ 销量信息:月销量、总销量、评价数 │ │ 图片信息:主图、详情图、SKU图、视频 │ │ SKU信息:规格组合、价格差异、库存分布 │ │ 店铺信息:店铺名、评分、DSR │ │ 物流信息:发货地、运费模板 │ │ 促销信息:满减、优惠券、限时折扣 │ └─────────────────────────────────────────┘
二、准备工作
2.1 注册淘宝开放平台开发者账号
- 访问 淘宝开放平台 注册开发者账号
- 完成实名认证(个人或企业)
- 创建应用,选择"网站应用"或"自用型应用"
- 获取以下凭证:
表格
| 凭证 | 说明 |
App Key |
应用唯一标识 |
App Secret |
应用密钥,用于签名 |
Session Key |
用户授权令牌(部分接口需要) |
2.2 申请接口权限
不同接口需要不同的权限级别:
表格
| 接口 | 所需权限 | 申请条件 |
taobao.item.get |
商品详情API | 基础权限,较易申请 |
taobao.tbk.item.get |
淘宝客商品API | 需加入淘宝客联盟 |
taobao.item.search |
商品搜索API | 需企业认证 |
2.3 Maven依赖配置
xml
<dependencies> <!-- HTTP请求 --> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.14</version> </dependency> <!-- JSON解析 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>2.0.43</version> </dependency> <!-- MD5加密 --> <dependency> <groupId>commons-codec</groupId> <artifactId>commons-codec</artifactId> <version>1.16.0</version> </dependency> </dependencies>
三、核心代码实现
3.1 TOP签名工具类
淘宝开放平台(TOP)签名规则:所有业务参数+公共参数(不含sign、file字段,空值剔除),按key ASCII升序排列,拼接为 app_secret+key1value1key2value2...+app_secret,最后进行MD5加密并转大写。
java
import org.apache.commons.codec.digest.DigestUtils; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.util.Map; import java.util.TreeMap; /** * 淘宝开放平台(TOP)签名工具类 * 签名规则:app_secret + 按key升序拼接(key+value) + app_secret → MD5 → 大写 */ public class TopSignUtil { /** * 生成TOP API签名 * @param params 请求参数(不含sign) * @param appSecret 应用密钥 * @return 大写MD5签名 */ public static String generateSign(Map<String, String> params, String appSecret) { // 1. 剔除sign和空值参数,按key ASCII升序排列 TreeMap<String, String> sortedParams = new TreeMap<>(); for (Map.Entry<String, String> entry : params.entrySet()) { String key = entry.getKey(); String value = entry.getValue(); if (!"sign".equals(key) && !"file".equals(key) && value != null && !value.trim().isEmpty()) { sortedParams.put(key, value); } } // 2. 按key升序拼接 key+value StringBuilder sb = new StringBuilder(); for (Map.Entry<String, String> entry : sortedParams.entrySet()) { sb.append(entry.getKey()).append(entry.getValue()); } // 3. 首尾拼接app_secret String toSign = appSecret + sb.toString() + appSecret; // 4. MD5加密,转大写 return DigestUtils.md5Hex(toSign).toUpperCase(); } /** * URL编码参数值 */ public static String urlEncode(String value) { try { return URLEncoder.encode(value, "UTF-8"); } catch (UnsupportedEncodingException e) { throw new RuntimeException("URL编码失败", e); } } }
3.2 淘宝API基础客户端
java
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.util.EntityUtils; import java.util.HashMap; import java.util.Map; /** * 淘宝API基础客户端 */ public class TopApiClient { // 生产环境网关 private static final String PROD_GATEWAY = "https://gw.api.taobao.com/router/rest"; // 沙箱环境网关(测试用) private static final String SANDBOX_GATEWAY = "https://gw.api.tbsandbox.com/router/rest"; private final String appKey; private final String appSecret; private final String sessionKey; private final boolean useSandbox; public TopApiClient(String appKey, String appSecret, String sessionKey) { this(appKey, appSecret, sessionKey, false); } public TopApiClient(String appKey, String appSecret, String sessionKey, boolean useSandbox) { this.appKey = appKey; this.appSecret = appSecret; this.sessionKey = sessionKey; this.useSandbox = useSandbox; } /** * 执行API请求 * @param method 接口方法名 * @param bizParams 业务参数 * @return JSON响应 */ public JSONObject execute(String method, Map<String, String> bizParams) { try { // 构建完整参数 Map<String, String> params = new HashMap<>(); params.put("method", method); params.put("app_key", appKey); params.put("timestamp", getCurrentTimestamp()); params.put("format", "json"); params.put("v", "2.0"); params.put("sign_method", "md5"); // 添加session(需要授权的接口) if (sessionKey != null && !sessionKey.isEmpty()) { params.put("session", sessionKey); } // 添加业务参数 for (Map.Entry<String, String> entry : bizParams.entrySet()) { params.put(entry.getKey(), entry.getValue()); } // 生成签名 String sign = TopSignUtil.generateSign(params, appSecret); params.put("sign", sign); // 构建请求URL String gateway = useSandbox ? SANDBOX_GATEWAY : PROD_GATEWAY; String url = buildUrl(gateway, params); // 发送POST请求 try (CloseableHttpClient client = HttpClients.createDefault()) { HttpPost httpPost = new HttpPost(url); httpPost.setHeader("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8"); String response = EntityUtils.toString( client.execute(httpPost).getEntity(), "UTF-8"); return JSON.parseObject(response); } } catch (Exception e) { throw new RuntimeException("API请求失败: " + e.getMessage(), e); } } /** * 获取当前时间戳(格式:yyyy-MM-dd HH:mm:ss) */ private String getCurrentTimestamp() { return new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss") .format(new java.util.Date()); } /** * 构建带参数的URL */ private String buildUrl(String gateway, Map<String, String> params) { StringBuilder url = new StringBuilder(gateway); url.append("?"); for (Map.Entry<String, String> entry : params.entrySet()) { url.append(entry.getKey()) .append("=") .append(TopSignUtil.urlEncode(entry.getValue())) .append("&"); } // 移除末尾的& return url.substring(0, url.length() - 1); } }
3.3 商品详情服务类
java
import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * 淘宝商品详情服务 */ public class TaobaoItemService { private final TopApiClient apiClient; public TaobaoItemService(TopApiClient apiClient) { this.apiClient = apiClient; } /** * 获取单个商品详情 * @param numIid 商品数字ID * @return 商品详情对象 */ public TaobaoItemDetail getItemDetail(String numIid) { Map<String, String> bizParams = new HashMap<>(); bizParams.put("fields", "num_iid,title,price,original_price,pic_url,detail_url," + "num,post_fee,express_fee,ems_fee,item_weight,item_size," + "seller_id,nick,stuff_status,location,has_discount," + "volume,has_invoice,has_warranty,has_showcase," + "modified,approve_status,product_id,outer_id," + "is_virtual,is_taobao,is_ex,is_timing," + "auction_point,property_alias,props_name,props," + "sku,desc,item_imgs,prop_imgs,video," + "sell_point,coupon_info,shopinfo"); bizParams.put("num_iid", numIid); JSONObject response = apiClient.execute("taobao.item.get", bizParams); // 解析响应 if (response.containsKey("item_get_response")) { JSONObject itemJson = response.getJSONObject("item_get_response") .getJSONObject("item"); return parseItemDetail(itemJson); } else if (response.containsKey("error_response")) { JSONObject error = response.getJSONObject("error_response"); throw new RuntimeException("接口错误: " + error.getString("msg")); } throw new RuntimeException("未知响应格式"); } /** * 批量获取商品详情(最多20个) */ public List<TaobaoItemDetail> getItemDetailBatch(List<String> numIids) { if (numIids.size() > 20) { throw new IllegalArgumentException("单次最多查询20个商品"); } Map<String, String> bizParams = new HashMap<>(); bizParams.put("fields", "num_iid,title,price,pic_url,volume,nick"); bizParams.put("num_iids", String.join(",", numIids)); JSONObject response = apiClient.execute("taobao.items.list.get", bizParams); List<TaobaoItemDetail> items = new ArrayList<>(); if (response.containsKey("items_list_get_response")) { JSONArray itemsArray = response.getJSONObject("items_list_get_response") .getJSONObject("items").getJSONArray("item"); for (int i = 0; i < itemsArray.size(); i++) { items.add(parseItemDetail(itemsArray.getJSONObject(i))); } } return items; } /** * 关键词搜索商品 * @param keyword 搜索关键词 * @param page 页码 * @param pageSize 每页数量 */ public List<TaobaoItemDetail> searchItems(String keyword, int page, int pageSize) { Map<String, String> bizParams = new HashMap<>(); bizParams.put("fields", "num_iid,title,pict_url,small_images,reserve_price," + "zk_final_price,user_type,provcity,item_url," + "seller_id,volume,nick"); bizParams.put("q", keyword); bizParams.put("page_no", String.valueOf(page)); bizParams.put("page_size", String.valueOf(pageSize)); JSONObject response = apiClient.execute("taobao.tbk.item.get", bizParams); List<TaobaoItemDetail> items = new ArrayList<>(); if (response.containsKey("tbk_item_get_response")) { JSONArray results = response.getJSONObject("tbk_item_get_response") .getJSONObject("results").getJSONArray("n_tbk_item"); for (int i = 0; i < results.size(); i++) { items.add(parseTbkItem(results.getJSONObject(i))); } } return items; } /** * 解析商品详情JSON */ private TaobaoItemDetail parseItemDetail(JSONObject json) { TaobaoItemDetail item = new TaobaoItemDetail(); item.setNumIid(json.getString("num_iid")); item.setTitle(json.getString("title")); item.setPrice(json.getDouble("price")); item.setOriginalPrice(json.getDouble("original_price")); item.setPicUrl(json.getString("pic_url")); item.setDetailUrl(json.getString("detail_url")); item.setNum(json.getIntValue("num")); // 库存 item.setVolume(json.getIntValue("volume")); // 销量 item.setNick(json.getString("nick")); // 卖家昵称 item.setLocation(json.getString("location")); item.setPostFee(json.getString("post_fee")); item.setExpressFee(json.getString("express_fee")); item.setEmsFee(json.getString("ems_fee")); item.setItemWeight(json.getString("item_weight")); item.setItemSize(json.getString("item_size")); item.setSellerId(json.getLong("seller_id")); item.setStuffStatus(json.getString("stuff_status")); item.setHasDiscount(json.getBooleanValue("has_discount")); item.setHasInvoice(json.getBooleanValue("has_invoice")); item.setHasWarranty(json.getBooleanValue("has_warranty")); item.setApproveStatus(json.getString("approve_status")); item.setProductId(json.getLong("product_id")); item.setOuterId(json.getString("outer_id")); item.setSellPoint(json.getString("sell_point")); item.setDesc(json.getString("desc")); // 解析图片列表 if (json.containsKey("item_imgs")) { JSONArray imgs = json.getJSONObject("item_imgs").getJSONArray("item_img"); List<String> imageList = new ArrayList<>(); for (int i = 0; i < imgs.size(); i++) { imageList.add(imgs.getJSONObject(i).getString("url")); } item.setItemImages(imageList); } // 解析SKU信息 if (json.containsKey("skus")) { JSONArray skus = json.getJSONObject("skus").getJSONArray("sku"); List<SkuInfo> skuList = new ArrayList<>(); for (int i = 0; i < skus.size(); i++) { JSONObject skuJson = skus.getJSONObject(i); SkuInfo sku = new SkuInfo(); sku.setSkuId(skuJson.getString("sku_id")); sku.setProperties(skuJson.getString("properties_name")); sku.setPrice(skuJson.getDouble("price")); sku.setQuantity(skuJson.getIntValue("quantity")); sku.setOuterId(skuJson.getString("outer_id")); skuList.add(sku); } item.setSkus(skuList); } // 解析店铺信息 if (json.containsKey("shopinfo")) { JSONObject shop = json.getJSONObject("shopinfo"); ShopInfo shopInfo = new ShopInfo(); shopInfo.setShopName(shop.getString("shop_name")); shopInfo.setShopUrl(shop.getString("shop_url")); shopInfo.setCreditLevel(shop.getIntValue("credit_level")); item.setShopInfo(shopInfo); } return item; } private TaobaoItemDetail parseTbkItem(JSONObject json) { TaobaoItemDetail item = new TaobaoItemDetail(); item.setNumIid(json.getString("num_iid")); item.setTitle(json.getString("title")); item.setPicUrl(json.getString("pict_url")); item.setPrice(json.getDouble("zk_final_price")); item.setOriginalPrice(json.getDouble("reserve_price")); item.setVolume(json.getIntValue("volume")); item.setNick(json.getString("nick")); item.setLocation(json.getString("provcity")); item.setUserType(json.getIntValue("user_type")); // 0=C店, 1=天猫 return item; } }
3.4 商品详情实体类
java
import java.util.List; /** * 淘宝商品详情实体类 */ public class TaobaoItemDetail { // 基础信息 private String numIid; // 商品数字ID private String title; // 商品标题 private String subTitle; // 副标题 private Double price; // 当前售价 private Double originalPrice; // 原价 private String picUrl; // 主图URL private String detailUrl; // 详情页URL // 库存与销量 private Integer num; // 库存数量 private Integer volume; // 销量 private String stuffStatus; // 商品新旧:new=全新,unused=闲置 // 物流信息 private String postFee; // 平邮费用 private String expressFee; // 快递费用 private String emsFee; // EMS费用 private String location; // 发货地 private String itemWeight; // 商品重量 private String itemSize; // 商品尺寸 // 卖家信息 private Long sellerId; // 卖家ID private String nick; // 卖家昵称 private ShopInfo shopInfo; // 店铺信息 private Integer userType; // 0=淘宝C店, 1=天猫 // 商品属性 private String props; // 商品属性 private String propsName; // 属性名称 private String propertyAlias; // 属性别名 private String sellPoint; // 卖点 private String desc; // 商品描述(HTML) // 状态与标识 private String approveStatus; // 商品状态:onsale=出售中,instock=库中 private Boolean hasDiscount; // 是否支持会员折扣 private Boolean hasInvoice; // 是否支持发票 private Boolean hasWarranty; // 是否支持保修 private Boolean hasShowcase; // 是否橱窗推荐 private Long productId; // 产品ID private String outerId; // 商家编码 // 媒体信息 private List<String> itemImages; // 商品图片列表 private List<SkuInfo> skus; // SKU列表 // Getters and Setters public String getNumIid() { return numIid; } public void setNumIid(String numIid) { this.numIid = numIid; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getSubTitle() { return subTitle; } public void setSubTitle(String subTitle) { this.subTitle = subTitle; } public Double getPrice() { return price; } public void setPrice(Double price) { this.price = price; } public Double getOriginalPrice() { return originalPrice; } public void setOriginalPrice(Double originalPrice) { this.originalPrice = originalPrice; } public String getPicUrl() { return picUrl; } public void setPicUrl(String picUrl) { this.picUrl = picUrl; } public String getDetailUrl() { return detailUrl; } public void setDetailUrl(String detailUrl) { this.detailUrl = detailUrl; } public Integer getNum() { return num; } public void setNum(Integer num) { this.num = num; } public Integer getVolume() { return volume; } public void setVolume(Integer volume) { this.volume = volume; } public String getStuffStatus() { return stuffStatus; } public void setStuffStatus(String stuffStatus) { this.stuffStatus = stuffStatus; } public String getPostFee() { return postFee; } public void setPostFee(String postFee) { this.postFee = postFee; } public String getExpressFee() { return expressFee; } public void setExpressFee(String expressFee) { this.expressFee = expressFee; } public String getEmsFee() { return emsFee; } public void setEmsFee(String emsFee) { this.emsFee = emsFee; } public String getLocation() { return location; } public void setLocation(String location) { this.location = location; } public String getItemWeight() { return itemWeight; } public void setItemWeight(String itemWeight) { this.itemWeight = itemWeight; } public String getItemSize() { return itemSize; } public void setItemSize(String itemSize) { this.itemSize = itemSize; } public Long getSellerId() { return sellerId; } public void setSellerId(Long sellerId) { this.sellerId = sellerId; } public String getNick() { return nick; } public void setNick(String nick) { this.nick = nick; } public ShopInfo getShopInfo() { return shopInfo; } public void setShopInfo(ShopInfo shopInfo) { this.shopInfo = shopInfo; } public Integer getUserType() { return userType; } public void setUserType(Integer userType) { this.userType = userType; } public String getProps() { return props; } public void setProps(String props) { this.props = props; } public String getPropsName() { return propsName; } public void setPropsName(String propsName) { this.propsName = propsName; } public String getPropertyAlias() { return propertyAlias; } public void setPropertyAlias(String propertyAlias) { this.propertyAlias = propertyAlias; } public String getSellPoint() { return sellPoint; } public void setSellPoint(String sellPoint) { this.sellPoint = sellPoint; } public String getDesc() { return desc; } public void setDesc(String desc) { this.desc = desc; } public String getApproveStatus() { return approveStatus; } public void setApproveStatus(String approveStatus) { this.approveStatus = approveStatus; } public Boolean getHasDiscount() { return hasDiscount; } public void setHasDiscount(Boolean hasDiscount) { this.hasDiscount = hasDiscount; } public Boolean getHasInvoice() { return hasInvoice; } public void setHasInvoice(Boolean hasInvoice) { this.hasInvoice = hasInvoice; } public Boolean getHasWarranty() { return hasWarranty; } public void setHasWarranty(Boolean hasWarranty) { this.hasWarranty = hasWarranty; } public Boolean getHasShowcase() { return hasShowcase; } public void setHasShowcase(Boolean hasShowcase) { this.hasShowcase = hasShowcase; } public Long getProductId() { return productId; } public void setProductId(Long productId) { this.productId = productId; } public String getOuterId() { return outerId; } public void setOuterId(String outerId) { this.outerId = outerId; } public List<String> getItemImages() { return itemImages; } public void setItemImages(List<String> itemImages) { this.itemImages = itemImages; } public List<SkuInfo> getSkus() { return skus; } public void setSkus(List<SkuInfo> skus) { this.skus = skus; } @Override public String toString() { return "TaobaoItemDetail{" + "numIid='" + numIid + '\'' + ", title='" + title + '\'' + ", price=" + price + ", originalPrice=" + originalPrice + ", num=" + num + ", volume=" + volume + ", nick='" + nick + '\'' + ", location='" + location + '\'' + '}'; } // SKU信息内部类 public static class SkuInfo { private String skuId; private String properties; private Double price; private Integer quantity; private String outerId; public String getSkuId() { return skuId; } public void setSkuId(String skuId) { this.skuId = skuId; } public String getProperties() { return properties; } public void setProperties(String properties) { this.properties = properties; } public Double getPrice() { return price; } public void setPrice(Double price) { this.price = price; } public Integer getQuantity() { return quantity; } public void setQuantity(Integer quantity) { this.quantity = quantity; } public String getOuterId() { return outerId; } public void setOuterId(String outerId) { this.outerId = outerId; } } // 店铺信息内部类 public static class ShopInfo { private String shopName; private String shopUrl; private Integer creditLevel; public String getShopName() { return shopName; } public void setShopName(String shopName) { this.shopName = shopName; } public String getShopUrl() { return shopUrl; } public void setShopUrl(String shopUrl) { this.shopUrl = shopUrl; } public Integer getCreditLevel() { return creditLevel; } public void setCreditLevel(Integer creditLevel) { this.creditLevel = creditLevel; } } }
3.5 主程序入口
java
public class TaobaoItemDemo { public static void main(String[] args) { // 配置信息(请替换为实际值) String appKey = "your_app_key"; String appSecret = "your_app_secret"; String sessionKey = "your_session_key"; // 部分接口需要 String numIid = "12345678901"; // 淘宝商品数字ID try { // 初始化客户端(沙箱环境测试) TopApiClient apiClient = new TopApiClient(appKey, appSecret, sessionKey, true); TaobaoItemService itemService = new TaobaoItemService(apiClient); System.out.println("正在查询商品详情,num_iid: " + numIid); System.out.println("=".repeat(60)); // 调用接口获取商品详情 TaobaoItemDetail item = itemService.getItemDetail(numIid); // 输出商品信息 System.out.println("【商品基础信息】"); System.out.println("商品ID: " + item.getNumIid()); System.out.println("商品标题: " + item.getTitle()); System.out.println("当前售价: ¥" + item.getPrice()); System.out.println("原价: ¥" + item.getOriginalPrice()); System.out.println("库存: " + item.getNum() + "件"); System.out.println("销量: " + item.getVolume() + "件"); System.out.println("主图: " + item.getPicUrl()); System.out.println("详情页: " + item.getDetailUrl()); System.out.println("\n【物流信息】"); System.out.println("发货地: " + item.getLocation()); System.out.println("平邮: ¥" + item.getPostFee()); System.out.println("快递: ¥" + item.getExpressFee()); System.out.println("EMS: ¥" + item.getEmsFee()); System.out.println("重量: " + item.getItemWeight()); System.out.println("\n【卖家信息】"); System.out.println("卖家昵称: " + item.getNick()); System.out.println("卖家ID: " + item.getSellerId()); if (item.getShopInfo() != null) { System.out.println("店铺名: " + item.getShopInfo().getShopName()); System.out.println("店铺链接: " + item.getShopInfo().getShopUrl()); } System.out.println("\n【商品属性】"); System.out.println("是否全新: " + ("new".equals(item.getStuffStatus()) ? "是" : "否")); System.out.println("是否支持发票: " + (item.getHasInvoice() != null && item.getHasInvoice() ? "是" : "否")); System.out.println("是否支持保修: " + (item.getHasWarranty() != null && item.getHasWarranty() ? "是" : "否")); System.out.println("卖点: " + item.getSellPoint()); System.out.println("\n【SKU规格】"); if (item.getSkus() != null) { for (TaobaoItemDetail.SkuInfo sku : item.getSkus()) { System.out.println(" - " + sku.getProperties() + " | 价格: ¥" + sku.getPrice() + " | 库存: " + sku.getQuantity() + "件"); } } System.out.println("\n【图片列表】"); if (item.getItemImages() != null) { System.out.println("共" + item.getItemImages().size() + "张图片"); for (int i = 0; i < Math.min(3, item.getItemImages().size()); i++) { System.out.println(" " + (i+1) + ". " + item.getItemImages().get(i)); } } // 关键词搜索示例 System.out.println("\n" + "=".repeat(60)); System.out.println("【关键词搜索示例】搜索: iPhone 16"); List<TaobaoItemDetail> searchResults = itemService.searchItems("iPhone 16", 1, 5); System.out.println("找到" + searchResults.size() + "个商品:"); for (TaobaoItemDetail result : searchResults) { System.out.println(" - " + result.getTitle() + " | ¥" + result.getPrice() + " | 销量:" + result.getVolume() + " | " + (result.getUserType() != null && result.getUserType() == 1 ? "天猫" : "淘宝")); } } catch (Exception e) { System.err.println("程序执行异常: " + e.getMessage()); e.printStackTrace(); } } }
四、接口返回数据结构
4.1 成功响应示例
JSON
{ "item_get_response": { "item": { "num_iid": "12345678901", "title": "Apple/苹果 iPhone 16 Pro Max 256GB 钛金属", "price": "9999.00", "original_price": "10999.00", "pic_url": "https://img.alicdn.com/xxx.jpg", "detail_url": "https://item.taobao.com/item.htm?id=12345678901", "num": 5000, "volume": 12500, "post_fee": "0.00", "express_fee": "0.00", "ems_fee": "20.00", "location": "上海", "item_weight": "0.227", "item_size": "159.9x76.7x8.25", "seller_id": 123456789, "nick": "苹果官方旗舰店", "stuff_status": "new", "has_discount": true, "has_invoice": true, "has_warranty": true, "approve_status": "onsale", "product_id": 1234567890, "outer_id": "IP16PM-256-TITAN", "sell_point": "A18芯片 4800万像素 超长续航", "desc": "<div>商品详细描述HTML...</div>", "item_imgs": { "item_img": [ {"url": "https://img.alicdn.com/1.jpg"}, {"url": "https://img.alicdn.com/2.jpg"}, {"url": "https://img.alicdn.com/3.jpg"} ] }, "skus": { "sku": [ { "sku_id": "1234567890123", "properties_name": "1627207:3232483:颜色:黑色;20509:28315:内存:256GB", "price": "9999.00", "quantity": 2000, "outer_id": "IP16PM-256-BLACK" }, { "sku_id": "1234567890124", "properties_name": "1627207:3232484:颜色:白色;20509:28315:内存:256GB", "price": "9999.00", "quantity": 3000, "outer_id": "IP16PM-256-WHITE" } ] }, "shopinfo": { "shop_name": "苹果官方旗舰店", "shop_url": "https://apple.tmall.com", "credit_level": 18 } } } }
4.2 错误响应示例
JSON
{ "error_response": { "code": 27, "msg": "Invalid session", "sub_code": "isv.invalid-session", "sub_msg": "Session无效或已过期" } }
五、核心返回字段说明
表格
| 字段分类 | 字段名 | 类型 | 说明 |
| 基础信息 | num_iid |
String | 商品数字ID(淘宝唯一标识) |
title |
String | 商品标题 | |
price |
String | 当前售价 | |
original_price |
String | 原价/市场价 | |
pic_url |
String | 商品主图URL | |
detail_url |
String | 商品详情页链接 | |
| 库存销量 | num |
Integer | 库存数量 |
volume |
Integer | 30天销量 | |
stuff_status |
String | 新旧程度:new=全新 | |
| 物流信息 | post_fee |
String | 平邮费用 |
express_fee |
String | 快递费用 | |
ems_fee |
String | EMS费用 | |
location |
String | 发货地 | |
item_weight |
String | 商品重量(kg) | |
item_size |
String | 商品尺寸 | |
| 卖家信息 | seller_id |
Long | 卖家ID |
nick |
String | 卖家昵称 | |
user_type |
Integer | 0=淘宝C店, 1=天猫 | |
| 商品属性 | props |
String | 商品属性串 |
props_name |
String | 属性名称串 | |
sell_point |
String | 商品卖点 | |
desc |
String | 商品描述(HTML) | |
| 状态标识 | approve_status |
String | onsale=出售中, instock=库中 |
has_discount |
Boolean | 是否支持会员折扣 | |
has_invoice |
Boolean | 是否支持发票 | |
has_warranty |
Boolean | 是否支持保修 | |
product_id |
Long | 产品ID | |
outer_id |
String | 商家编码 | |
| 媒体信息 | item_imgs |
Array | 商品图片列表 |
| SKU信息 | skus |
Array | SKU规格列表 |
| 店铺信息 | shopinfo |
Object | 店铺信息 |
六、选品比价应用场景
6.1 同款比价系统架构
plain
┌─────────────────────────────────────────┐ │ 用户端(APP/小程序) │ │ 搜索商品 → 展示比价结果 → 跳转购买 │ ├─────────────────────────────────────────┤ │ 比价服务端 │ │ 商品搜索 → 多平台采集 → 同款匹配 → 价格对比 │ ├─────────────────────────────────────────┤ │ 数据采集层 │ │ 淘宝API │ 京东API │ 1688API │ 拼多多API │ ├─────────────────────────────────────────┤ │ 数据存储层 │ │ MySQL │ Redis │ Elasticsearch │ └─────────────────────────────────────────┘
6.2 同款匹配算法
java
/** * 基于商品标题和属性的同款匹配 */ public class ItemMatcher { /** * 计算两个商品的相似度 * @return 相似度分数 (0-1) */ public double calculateSimilarity(TaobaoItemDetail item1, TaobaoItemDetail item2) { double score = 0.0; // 1. 标题相似度(Jaccard系数) double titleSim = jaccardSimilarity(item1.getTitle(), item2.getTitle()); score += titleSim * 0.4; // 2. 品牌匹配 if (extractBrand(item1.getTitle()).equals(extractBrand(item2.getTitle()))) { score += 0.2; } // 3. 规格属性匹配 double propSim = compareProperties(item1.getProps(), item2.getProps()); score += propSim * 0.3; // 4. 价格接近度 double priceRatio = Math.min(item1.getPrice(), item2.getPrice()) / Math.max(item1.getPrice(), item2.getPrice()); score += priceRatio * 0.1; return score; } private double jaccardSimilarity(String s1, String s2) { Set<String> set1 = new HashSet<>(Arrays.asList(s1.split(""))); Set<String> set2 = new HashSet<>(Arrays.asList(s2.split(""))); Set<String> intersection = new HashSet<>(set1); intersection.retainAll(set2); Set<String> union = new HashSet<>(set1); union.addAll(set2); return (double) intersection.size() / union.size(); } private String extractBrand(String title) { // 从标题提取品牌名(需维护品牌词库) String[] brands = {"Apple", "华为", "小米", "耐克", "阿迪达斯"}; for (String brand : brands) { if (title.contains(brand)) return brand; } return ""; } private double compareProperties(String props1, String props2) { // 比较关键属性(颜色、尺码、内存等) // 实现略... return 0.5; } }
6.3 价格监控与预警
java
/** * 价格监控服务 */ public class PriceMonitorService { /** * 监控商品价格变动 */ public void monitorPrice(String numIid, double threshold) { // 获取当前价格 TaobaoItemDetail current = itemService.getItemDetail(numIid); double currentPrice = current.getPrice(); // 获取历史价格 Double historicalPrice = priceHistoryDao.getLatestPrice(numIid); if (historicalPrice != null) { double changeRate = (currentPrice - historicalPrice) / historicalPrice; if (Math.abs(changeRate) >= threshold) { // 价格变动超过阈值,发送通知 sendPriceAlert(numIid, current.getTitle(), historicalPrice, currentPrice, changeRate); } } // 保存当前价格到历史记录 priceHistoryDao.savePrice(numIid, currentPrice); } private void sendPriceAlert(String numIid, String title, double oldPrice, double newPrice, double changeRate) { String direction = changeRate > 0 ? "上涨" : "下降"; String message = String.format( "【价格%s提醒】\n商品: %s\n原价: ¥%.2f\n现价: ¥%.2f\n变动: %.1f%%", direction, title, oldPrice, newPrice, Math.abs(changeRate) * 100 ); // 发送推送通知... } }
七、常见问题与解决方案
表格
| 问题 | 原因 | 解决方案 |
Invalid signature |
签名算法错误或参数排序不对 | 确认按key ASCII升序排列,首尾拼接app_secret |
Invalid session |
Session Key过期或未授权 | 重新获取Session Key,检查授权范围 |
Insufficient permissions |
未申请接口权限 | 在开放平台申请对应API权限 |
Item not found |
商品ID错误或商品已下架 | 核对num_iid,确认商品状态 |
Rate limit exceeded |
超出调用频率限制 | 控制QPS,申请提升配额 |
Fields invalid |
返回字段参数错误 | 检查fields字段名是否正确 |
八、进阶优化建议
8.1 数据缓存策略
java
public class ItemCache { // 商品详情缓存5分钟 private static final LoadingCache<String, TaobaoItemDetail> itemCache = Caffeine.newBuilder() .maximumSize(10000) .expireAfterWrite(5, TimeUnit.MINUTES) .build(key -> itemService.getItemDetail(key)); // 价格缓存1分钟(价格变动频繁) private static final LoadingCache<String, Double> priceCache = Caffeine.newBuilder() .maximumSize(50000) .expireAfterWrite(1, TimeUnit.MINUTES) .build(key -> itemService.getItemDetail(key).getPrice()); }
8.2 批量查询优化
java
public List<TaobaoItemDetail> batchGetWithLimit(List<String> numIids, int batchSize) { List<TaobaoItemDetail> results = new ArrayList<>(); // 分批查询,每批不超过20个 for (int i = 0; i < numIids.size(); i += batchSize) { List<String> batch = numIids.subList(i, Math.min(i + batchSize, numIids.size())); results.addAll(itemService.getItemDetailBatch(batch)); // 控制频率,避免触发限流 if (i + batchSize < numIids.size()) { Thread.sleep(1000); } } return results; }
如遇任何疑问或有进一步的需求,请随时与我私信或者评论联系。