淘宝 API 接口获取商品到手价的 Java 实现
淘宝开放平台提供了丰富的 API 接口,获取商品到手价(含优惠券、满减等优惠后价格)需调用淘宝联盟相关接口。以下是基于 Java 语言的实现方案,包含签名生成、HTTP 请求及响应解析逻辑。
一、核心依赖与准备工作
依赖引入
需添加 HTTP 客户端和 JSON 解析依赖(以 Maven 为例):
xml
org.apache.httpcomponents.client5
httpclient5
5.3
com.alibaba
fastjson
2.0.32
认证信息
从淘宝开放平台获取appKey和appSecret
部分接口需session(用户会话密钥)
二、Java 实现代码
以下代码实现了淘宝联盟 API 调用框架,重点实现taobao.get接口获取商品到手价:
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.apache.hc.client5.http.classic.methods.HttpGet;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.core5.http.HttpEntity;
import org.apache.hc.core5.http.ParseException;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.*;
public class TaobaoPriceClient {
// API网关地址
private static final String API_URL = "https://eco.taobao.com/router/rest";
// 编码格式
private static final String CHARSET = "UTF-8";
// 时间格式
private static final SimpleDateFormat TIME_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
private final String appKey; private final String appSecret; private final String session; /** * 初始化客户端 * @param appKey 应用appKey * @param appSecret 应用appSecret * @param session 用户会话密钥(可选) */ public TaobaoPriceClient(String appKey, String appSecret, String session) { this.appKey = appKey; this.appSecret = appSecret; this.session = session; } /** * 生成签名 * @param params 所有请求参数 * @return 签名字符串 */ private String generateSign(Map<String, String> params) { try { // 1. 按参数名ASCII升序排序 List<Map.Entry<String, String>> entryList = new ArrayList<>(params.entrySet()); entryList.sort(Comparator.comparing(Map.Entry::getKey)); // 2. 拼接为keyvaluekeyvalue格式 StringBuilder signSb = new StringBuilder(); for (Map.Entry<String, String> entry : entryList) { signSb.append(entry.getKey()).append(entry.getValue()); } // 3. 首尾拼接appSecret String signStr = appSecret + signSb.toString() + appSecret; // 4. MD5加密并转为大写 MessageDigest md5 = MessageDigest.getInstance("MD5"); byte[] bytes = md5.digest(signStr.getBytes(CHARSET)); // 字节数组转十六进制字符串 StringBuilder hexSb = new StringBuilder(); for (byte b : bytes) { String hex = Integer.toHexString(b & 0xFF); if (hex.length() == 1) { hexSb.append("0"); } hexSb.append(hex); } return hexSb.toString().toUpperCase(); } catch (NoSuchAlgorithmException | UnsupportedEncodingException e) { throw new RuntimeException("签名生成失败", e); } } /** * 执行API调用 * @param method 接口方法名 * @param bizParams 业务参数 * @return 接口返回的业务数据 */ private JSONObject callApi(String method, Map<String, String> bizParams) { try (CloseableHttpClient httpClient = HttpClients.createDefault()) { // 1. 构建公共参数 Map<String, String> params = new HashMap<>(); params.put("app_key", appKey); params.put("method", method); params.put("timestamp", TIME_FORMAT.format(new Date())); params.put("format", "json"); params.put("v", "2.0"); params.put("sign_method", "md5"); if (session != null && !session.isEmpty()) { params.put("session", session); } // 2. 添加业务参数 if (bizParams != null) { params.putAll(bizParams); } // 3. 生成签名 String sign = generateSign(params); params.put("sign", sign); // 4. 构建请求URL StringBuilder urlSb = new StringBuilder(API_URL).append("?"); for (Map.Entry<String, String> entry : params.entrySet()) { urlSb.append(URLEncoder.encode(entry.getKey(), CHARSET)) .append("=") .append(URLEncoder.encode(entry.getValue(), CHARSET)) .append("&"); } String url = urlSb.substring(0, urlSb.length() - 1); // 5. 发送GET请求 HttpGet httpGet = new HttpGet(url); try (CloseableHttpResponse response = httpClient.execute(httpGet)) { HttpEntity entity = response.getEntity(); if (entity == null) { throw new RuntimeException("接口返回空数据"); } // 6. 解析响应 String responseBody = EntityUtils.toString(entity, StandardCharsets.UTF_8); JSONObject result = JSON.parseObject(responseBody); // 7. 处理错误响应 if (result.containsKey("error_response")) { JSONObject error = result.getJSONObject("error_response"); throw new RuntimeException(String.format( "API错误: %s (错误码: %s)", error.getString("msg"), error.getString("code") )); } // 8. 返回业务数据节点 String responseKey = method.replace(".", "_") + "_response"; return result.getJSONObject(responseKey); } } catch (IOException | ParseException e) { throw new RuntimeException("API调用失败", e); } } /** * 获取商品到手价信息 * @param numIid 商品数字ID * @return 价格信息对象 */ public PriceInfo getProductFinalPrice(String numIid) { // 1. 构建业务参数 Map<String, String> bizParams = new HashMap<>(); bizParams.put("num_iids", numIid); bizParams.put("fields", "num_iid,title,pict_url,price,zk_final_price,coupon_info"); // 2. 调用淘宝联盟接口 JSONObject response = callApi("taobao.tbk.item.info.get", bizParams); if (response == null) { return null; } // 3. 解析响应数据 JSONObject results = response.getJSONObject("results"); if (results == null) { return null; } JSONObject item = results.getJSONArray("n_tbk_item").getJSONObject(0); PriceInfo priceInfo = new PriceInfo(); priceInfo.setNumIid(item.getString("num_iid")); priceInfo.setTitle(item.getString("title")); priceInfo.setOriginalPrice(item.getString("price")); priceInfo.setFinalPrice(item.getString("zk_final_price")); priceInfo.setCouponInfo(item.getString("coupon_info")); priceInfo.setPictUrl(item.getString("pict_url")); return priceInfo; } /** * 价格信息实体类 */ public static class PriceInfo { private String numIid; // 商品ID private String title; // 商品标题 private String originalPrice; // 原价 private String finalPrice; // 到手价 private String couponInfo; // 优惠券信息 private String pictUrl; // 商品主图 // Getter和Setter方法 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 getOriginalPrice() { return originalPrice; } public void setOriginalPrice(String originalPrice) { this.originalPrice = originalPrice; } public String getFinalPrice() { return finalPrice; } public void setFinalPrice(String finalPrice) { this.finalPrice = finalPrice; } public String getCouponInfo() { return couponInfo; } public void setCouponInfo(String couponInfo) { this.couponInfo = couponInfo; } public String getPictUrl() { return pictUrl; } public void setPictUrl(String pictUrl) { this.pictUrl = pictUrl; } } // 示例调用 public static void main(String[] args) { // 替换为实际参数 String appKey = "your_app_key"; String appSecret = "your_app_secret"; String session = "your_session"; // 非必需 TaobaoPriceClient client = new TaobaoPriceClient(appKey, appSecret, session); try { // 商品数字ID(从淘宝商品详情页URL获取) String numIid = "6543217890"; PriceInfo priceInfo = client.getProductFinalPrice(numIid); if (priceInfo != null) { System.out.println("商品标题: " + priceInfo.getTitle()); System.out.println("原价: " + priceInfo.getOriginalPrice() + " 元"); System.out.println("到手价: " + priceInfo.getFinalPrice() + " 元"); System.out.println("优惠券: " + (priceInfo.getCouponInfo() != null ? priceInfo.getCouponInfo() : "无")); } } catch (Exception e) { e.printStackTrace(); } } }
关键技术说明
1. 签名生成逻辑
淘宝 API 签名是核心安全机制,实现步骤:
- 将所有参数按参数名 ASCII 码升序排序
- 拼接为
keyvaluekeyvalue格式(无分隔符) - 首尾拼接
appSecret后进行 MD5 加密 - 加密结果转为大写即为签名值
2. 核心接口解析
使用
taobao.tbk.item.info.get接口(淘宝联盟接口),需申请联盟权限关键参数:
num_iids:商品数字 ID(从商品 URL 提取,如中的123456)fields:指定返回字段,zk_final_price即为优惠后到手价3. 响应处理
- 接口返回 JSON 格式数据,错误信息在
error_response节点 - 成功响应的业务数据在``节点
zk_final_price字段已包含所有优惠(店铺券、满减等),直接作为到手价使用使用注意事项
- 权限申请:淘宝联盟接口需在阿里妈妈联盟单独申请,个人开发者需完成实名认证
- 限流控制:默认 QPS 为 10,超限会返回 429 错误,建议添加请求间隔
- 异常处理:生产环境需完善重试机制(如使用 guava-retrying),处理网络波动和临时错误
- 参数编码:所有参数需进行 URL 编码,避免特殊字符导致签名错误
该实现可直接集成到 Java 项目中,适用于电商比价系统、导购平台等场景,通过调整fields参数可获取更多商品信息(如销量、佣金比例等)。