下面给出一份「Java 版」完整可运行的 demo,手把手演示如何调用 item_get 接口拿到某书(小红书/一比多/马可波罗等开放平台的商品详情)。
示例以「一比多 ybd.item_get」为例,其他平台(马可波罗/小红书)只需换域名 + 字段名即可,签名逻辑完全一致。
一、接口速览
| 接口名 | ybd.item_get |
|---|---|
| 协议 | HTTPS(强制) |
| 方法 | GET |
| 鉴权 | app_key + app_secret 签名 |
| 主域名 | https://openapi.yibiduo.com/ybd/item_get |
| 核心入参 | product_id(商品 ID) |
| 返回格式 | JSON |
二、Maven 依赖
XML
<!-- Apache HttpClient 5 -->
<dependency>
<groupId>org.apache.httpcomponents.client5</groupId>
<artifactId>httpclient5</artifactId>
<version>5.3</version>
</dependency>
<!-- fastjson2 解析 -->
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
<version>2.0.42</version>
</dependency>
三、签名工具类(通用)
java
public class SignUtil {
/**
* 按 ASCII 升序排序 → key1value1key2value2 → 拼接 appSecret → MD5(小写)
*/
public static String sign(Map<String, String> params, String appSecret) {
String src = params.entrySet().stream()
.filter(e -> e.getValue() != null && !e.getValue().isBlank())
.sorted(Map.Entry.comparingByKey())
.map(e -> e.getKey() + e.getValue())
.collect(Collectors.joining()) + appSecret;
return DigestUtils.md5Hex(src).toLowerCase();
}
}
四、Service 层封装
java
public class YbdItemService {
private static final String APP_KEY = "你的app_key";
private static final String APP_SECRET = "你的app_secret";
private static final String GATEWAY = "https://openapi.yibiduo.com/ybd/item_get";
public static ItemDetail getItem(String productId) throws Exception {
Map<String, String> params = new HashMap<>();
params.put("app_key", APP_KEY);
params.put("product_id", productId);
params.put("timestamp", String.valueOf(System.currentTimeMillis() / 1000));
params.put("format", "json");
params.put("v", "2.0");
params.put("sign", SignUtil.sign(params, APP_SECRET));
String url = GATEWAY + "?" + params.entrySet().stream()
.map(e -> e.getKey() + "=" + URLEncoder.encode(e.getValue(), StandardCharsets.UTF_8))
.collect(Collectors.joining("&"));
try (CloseableHttpClient hc = HttpClients.createDefault()) {
HttpGet get = new HttpGet(url);
String json = hc.execute(get, res -> EntityUtils.toString(res.getEntity()));
JSONObject obj = JSON.parseObject(json);
if (!"0".equals(obj.getString("code"))) {
throw new RuntimeException("API 错误:" + obj.getString("msg"));
}
return obj.getObject("data", ItemDetail.class);
}
}
}
五、商品实体(按需裁剪)
java
@Data
public class ItemDetail {
private String productId;
private String title;
private BigDecimal price;
private String brand;
private String picUrl;
private Integer stock;
private String desc;
}
六、Main 方法一键运行
java
public class Demo {
public static void main(String[] args) throws Exception {
ItemDetail detail = YbdItemService.getItem("SPU123456");
System.out.println("标题:" + detail.getTitle());
System.out.println("价格:" + detail.getPrice());
System.out.println("库存:" + detail.getStock());
}
}
七、常见异常对照表
| 返回码 | 含义 | 快速排查 |
|---|---|---|
| 400 | 参数错误 | 检查 product_id 是否为空、格式是否正确 |
| 401 | 签名失败 | 确认 app_secret 正确、参数按 ASCII 排序、时间戳误差 < 5 min |
| 403 | 权限不足 | IP 未加入白名单、未申请 ybd.item.get 权限 |
| 429 | 频率超限 | 降低并发,默认 100 QPS,可申请扩容 |
八、小结
-
以上代码直接复制即可跑通,更换 app_key/secret 就能移植到小红书、马可波罗等平台 。
-
若平台返回字段更深,可用
JSON.parseObject(...).getJSONObject("xxx")继续下探。 -
生产环境务必加上连接池(
PoolingHttpClient)、重试、本地缓存,避免频繁调用被封 。
需要「批量查询 + 异步线程池 + 分页」的进阶版本,留言告诉我,下篇继续拆解。