一、前言
京东评论里藏着用户最真实的痛点与爽点。无论是做竞品监控、情感分析,还是训练自己的 NLP 模型,先把数据“搬”下来永远是第一步。本文用 Java 全程演示如何合规、稳定、可扩展地拿到京东商品评论,并给出可直接落地的代码与踩坑笔记。
二、准备工作
- JDK 17+(示例基于 17,8 也能跑)
- Maven 3.8+
- 创建应用 → 拿到
appKey+appSecret - 申请权限
jd.union.open.comment.query(联盟评论接口,SKU 级) - 依赖库(pom.xml 片段)
xml
<dependencies>
<!-- HTTP -->
<dependency>
<groupId>org.apache.httpcomponents.client5</groupId>
<artifactId>httpclient5</artifactId>
<version>5.3.1</version>
</dependency>
<!-- JSON -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.17.0</version>
</dependency>
<!-- 工具 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.14.0</version>
</dependency></dependencies>三、核心流程
1. 拼参数 + 算签名
京东要求ASCII 升序后
务必加上
key+value 首尾各拼一次 appSecret,再 MD5 转大写。务必加上
client_type=pc,否则 100% 报 4001。java
public static String sign(Map<String, String> params, String appSecret) {
// 1. 过滤空值 & 升序
String sorted = params.entrySet().stream()
.filter(e -> e.getValue() != null)
.sorted(Map.Entry.comparingByKey())
.map(e -> e.getKey() + e.getValue())
.collect(Collectors.joining());
// 2. 首尾加 secret
String raw = appSecret + sorted + appSecret;
// 3. MD5 大写
return DigestUtils.md5Hex(raw).toUpperCase();}2. 发请求
HttpClient 5 连接池 + 重试,阿里云 ECS 实测 10 线程稳跑。
java
private static final String GATEWAY = "https://api.jd.com/routerjson";public static CommentPage fetch(String skuId, int page, int pageSize,
String appKey, String appSecret) throws Exception {
Map<String, String> p = new HashMap<>();
p.put("method", "jd.union.open.comment.query");
p.put("app_key", appKey);
p.put("timestamp", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
.format(LocalDateTime.now()));
p.put("v", "1.0");
p.put("format", "json");
p.put("skuId", skuId);
p.put("page", String.valueOf(page));
p.put("pageSize", String.valueOf(pageSize));
p.put("client_type", "pc"); // 易漏!
p.put("sign", sign(p, appSecret));
URI uri = RequestBuilder.get(GATEWAY)
.addParameters(p.entrySet().stream()
.map(e -> new BasicNameValuePair(e.getKey(), e.getValue()))
.toArray(NameValuePair[]::new))
.build();
try (CloseableHttpClient hc = HttpClients.custom()
.setRetryStrategy(new DefaultHttpRequestRetryStrategy(3, TimeValue.ofSeconds(1)))
.build()) {
String json = hc.execute(new HttpGet(uri), r -> EntityUtils.toString(r.getEntity()));
return new ObjectMapper().readValue(json, CommentPage.class);
}}3. 解析结果
京东返回 JSON 根节点在
jd_union_open_comment_query_response.result。java
@JsonIgnoreProperties(ignoreUnknown = true)public class CommentPage {
public List<Comment> comments;
public int totalCount;
@JsonIgnoreProperties(ignoreUnknown = true)
public static class Comment {
public String nickname;
public String content;
public int score;
@JsonProperty("creationTime")
public String time;
}}四、完整调用示例
java
public static void main(String[] args) throws Exception {
String sku = "100012043978"; // 京东自营 iPhone15
int size = 50; // 每页最大 50
int page = 1;
CommentPage p;
do {
p = fetch(sku, page++, size, YOUR_APPKEY, YOUR_SECRET);
p.comments.forEach(c -> System.out.printf("%s\t%s\t%d%n", c.time, c.nickname, c.score));
Thread.sleep(220); // 保守频率,基础版 ≤5 次/s
} while ((page - 1) * size < p.totalCount);}控制台输出(节选)
2025-11-30 18:23:45 京东会员 5
2025-11-30 16:10:22 张三 4
...五、常见异常与对策
| 错误码 | 含义 | 解决方案 |
|---|---|---|
| 1001 | appKey 无效 | 检查是否填错;确认 IP 在白名单 |
| 2003 | QPS 超限 | 降至 5 次/s 以内,或申请进阶版 |
| 3002 | 权限不足 | 后台申请 jd.union.open.comment.query |
| 4001 | 参数错误 | 八成漏了 client_type 或 SKU 写错 |
六、生产级扩展思路
- 异步批爬:CompletableFuture + 队列,单机 10w 评论/小时轻松跑。
- 断点续爬:把
page/totalCount落库,程序重启自动续跑。 - 情感打标:调用 HanLP / fastText 做正负向评分,再写回 MySQL。
- 代理池:云厂商内网 IP 被限后,可轮询代理 + 重试。