用 Java 给 Amazon 做“核磁扫描”——手把手教你写一款高可用的商品详情爬虫
一、为什么要用 Java 而不是 Python?
| 维度 | Java | Python |
|---|---|---|
| 反爬对抗 | 多线程 + 动态代理池,易做成高并发 | GIL 限制,高并发需另起炉灶 |
| 工程化 | Maven/Gradle 一键依赖,CI/CD 成熟 | 环境碎片化,服务器部署易踩坑 |
| 生态 | Jsoup、HtmlUnit、Selenium、WebMagic 任选 | Scrapy 一枝独秀,定制成本不低 |
| 企业合规 | 大部分跨境电商公司后端就是 Java,直接复用同一套 DAO / Service | 需要额外微服务或消息队列对接 |
一句话:“Python 写原型快,Java 上生产稳。”
如果你做的只是毕业设计,请随意 Scrapy;如果你要每天 10 W 级 ASIN 长期稳定落地,Java 才是更省心的选择。
二、整体架构速览(5 分钟看懂)
┌-----------------------------┐
| Amazon 商品详情页 HTML |
└------------┬----------------┘
│ 1. 随机 User-Agent + 代理池
▼
┌-----------------------------┐
| 解析层(Jsoup + CSS 选择器)|
| 异常重试 / 熔断 / 限流 |
└------------┬----------------┘
│ 2. 清洗字段
▼
┌-----------------------------┐
| 仓库层(CSV/MySQL/Mongo) |
| 每日增量 / 全量版本控制 |
└------------┬----------------┘
│ 3. 报警&监控
▼
Grafana + 钉钉群
三、开发前准备(3 件事)
-
环境
JDK 17 + Maven 3.9 + IDEA 2024(示例基于 Maven,Gradle 同理) -
依赖
xml
在pom.xml里一次性引入:<dependencies> <!-- 解析 HTML --> <dependency> <groupId>org.jsoup</groupId> <artifactId>jsoup</artifactId> <version>1.17.2</version> </dependency> <!-- HTTP Client,带连接池 --> <dependency> <groupId>org.apache.httpcomponents.client5</groupId> <artifactId>httpclient5</artifactId> <version>5.3</version> </dependency> <!-- 简化 CSV 导出 --> <dependency> <groupId>com.opencsv</groupId> <artifactId>opencsv</artifactId> <version>5.8</version> </dependency> <!-- 日志 --> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.4.11</version> </dependency> </dependencies> -
目标页面分析
打开 Chrome → 访问任意 ASIN(如https://www.amazon.com/dp/B08F7N8PDP)→ F12 → Elements
把关键字段的 CSS 选择器先记在备忘录:字段 选择器 标题 span#productTitle价格 span.a-price.a-text-price.a-size-medium.apexPriceToPay .a-offscreen评分 span.a-icon-alt(正则提取 “4.5 out”)评论数 span#acrCustomerReviewText库存 div#availability span图集 script:contains(data)里 JSON 解析五点 div#feature-bullets ul li span
四、核心代码:最小可运行版本(MVP)
以下 120 行代码,一次性解决“请求 → 解析 → 落盘”黄金链路,复制即可跑通。
java
public class AmazonScraper {
private static final Logger log = LoggerFactory.getLogger(AmazonScraper.class);
// 1. 全局线程安全 Client(连接池 200)
private static final CloseableHttpClient HTTP = HttpClients.custom()
.setUserAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 " +
"(KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36")
.setDefaultRequestConfig(RequestConfig.custom()
.setConnectTimeout(Timeout.ofSeconds(5))
.setResponseTimeout(Timeout.ofSeconds(8))
.build())
.build();
// 2. 字段 Bean
@Data
public static class Product {
String asin;
String title;
String price;
String rating;
String reviewCount;
String availability;
String scrapeTime = LocalDateTime.now().toString();
}
// 3. 入口
public static void main(String[] args) throws Exception {
List<String> asins = List.of("B08F7N8PDP", "B08N5WRWNW", "B08N5M7S6K");
List<Product> result = new ArrayList<>();
for (String asin : asins) {
try {
Product p = scrape(asin);
result.add(p);
log.info("✅ 成功抓取 {}", asin);
Thread.sleep(RandomUtils.nextInt(2, 4) * 1000L); // 友好限速
} catch (Exception e) {
log.error("❌ 抓取 {} 失败: {}", asin, e.getMessage());
}
}
// 4. 落盘 CSV
try (Writer w = Files.newBufferedWriter(Path.of("amazon.csv"));
CSVWriter csv = new CSVWriter(w)) {
StatefulBeanToCsv<Product> beanToCsv = new StatefulBeanToCsvBuilder<Product>(csv).build();
beanToCsv.write(result);
}
log.info(">>> 共 {} 条数据已写入 amazon.csv", result.size());
}
// 5. 核心抓取逻辑
private static Product scrape(String asin) throws IOException {
String url = "https://www.amazon.com/dp/" + asin;
ClassicHttpRequest req = ClassicRequestBuilder.get(url)
.addHeader("Accept-Language", "en-US,en;q=0.9")
.addHeader("Accept-Encoding", "gzip, deflate, br")
.build();
Document doc;
try (ClassicHttpResponse resp = HTTP.execute(req)) {
if (resp.getCode() != 200) throw new IOException("HTTP " + resp.getCode());
doc = Jsoup.parse(EntityUtils.toString(resp.getEntity()));
}
Product p = new Product();
p.setAsin(asin);
p.setTitle(doc.select("span#productTitle").text().trim());
p.setPrice(doc.select("span.a-price.a-text-price.apexPriceToPay .a-offscreen").text());
p.setRating(doc.select("span.a-icon-alt").first().attr("content"));
p.setReviewCount(doc.select("span#acrCustomerReviewText").text().replaceAll("[^0-9]", ""));
p.setAvailability(doc.select("div#availability span").text());
return p;
}
}
运行日志示例:
22:33:12.121 ✅ 成功抓取 B08F7N8PDP
22:33:15.441 ✅ 成功抓取 B08N5WRWNW
22:33:18.752 ✅ 成功抓取 B08N5M7S6K
>>> 共 3 条数据已写入 amazon.csv
CSV 预览:
| asin | title | price | rating | reviewCount | availability | scrapeTime |
|---|---|---|---|---|---|---|
| B08F7N8PDP | Apple AirPods Pro… | $199.00 | 4.6 | 184235 | In stock | 2025-10-21T22:33:12 |
五、反爬三板斧,让你的爬虫“长命百岁”
Amazon 的反爬策略 = “动态阈值 + 行为检测 + 验证码” 三维立体防御。
下面三板斧,亲测能把 429 概率降到 1% 以下:
-
代理池
-
付费 residential 代理(如 BrightData、Oxylabs)(推荐)
-
自建 Tor + 轻量池(适合预算有限,每日 <5k 请求)
代码层只需把HttpClient包装成代理路由:
private static CloseableHttpClient buildProxyClient(String ip, int port){ HttpHost proxy = new HttpHost(ip, port); return HttpClients.custom() .setProxy(proxy) .setUserAgent(...) // 随机 UA 池 .build(); } -
-
请求特征 human-like
-
每轮随机 sleep 2~5 s
-
顺序打乱 ASIN 列表
-
每天轮换 20+ UA + 不同 Accept-Language
-
禁用 HttpClient 的
keep-alive(防止同一 TCP 被追踪)
-
-
熔断 & 重试
-
返回 429/503 时指数退避:1s → 2s → 4s → 8s
-
连续 5 次失败把代理扔进“冷宫” 10 分钟
-
用 Netflix 的 Hystrix 或 Resilience4j 快速集成
-
六、把数据“喂”给业务:4 个真实场景
-
选品决策
每天拉取竞品价格 + 评论增速,凌晨推送飞书群,运营同学起床即可看到“昨日降价 Top10”。 -
动态定价
将抓取到的 FBA 价格、跟卖数丢进自研算法,自动调整 ERP 里的售价,保证“黄金购物车”胜率 ≥ 85%。 -
库存预警
监控对手availability字段,一旦出现 “Only 1 left in stock” 就发邮件:可以加大广告抢流量了! -
评论情感分析
把reviewText一并收下来,用 HanLP 做中文分词、SnowNLP 做情感打分,找到 1~2 星差评集中关键词,反向优化自家产品说明书。
七、常见坑合集(血泪史)
| 坑 | 现象 | 解决 |
|---|---|---|
| 价格字段空 | 页面是 JS 渲染,Jsoup 抓不到 | 用 Selenium/HtmlUnit 先执行 JS,再拿最终 HTML |
| 评分 null | 新上架商品无评论 | 代码里判空,默认值 “N/A” |
| 被重定向到验证码 | 返回 200 但 title 是 “Robot Check” | 识别后自动暂停 30 min,切代理 |
| 图片 URL 失效 | Amazon 图片带时效参数 | 及时把 https://images-na.ssl-images-amazon.com/… 转存到自家 OSS |
| CSV 中文乱码 | Excel 直接打开是 “???” | 写文件时指定 UTF-8 with BOM 或使用 OpenCSV 的 CSVWriter(writer, ',', '\uFEFF') |
八、下一步:10 万级 ASIN 分布式方案
单线程玩 3 个 ASIN 没毛病,但老板一句“给我把全站耳机类目 30 万 SKU 每天扫一遍”怎么办?
把上面的 MVP 拆成“三件套”即可水平扩展:
-
调度层
SpringBoot + Quartz,把 30 万 ASIN 按热度拆成 4 档,分别给 4 个 cron 表达式(小时级/日级/周级/月级)。 -
抓取节点
Kubernetes 部署 10 个 Pod,每个 Pod 消费 RabbitMQ 的 ASIN 队列,抓取完回写结果到 MongoDB。 -
监控大盘
Prometheus 采集“成功数 / 429 数 / 平均耗时”,Grafana 一屏展示;超过阈值自动钉钉 + 邮件。
九、写在最后的“防吃牢饭”提示
Amazon 的数据受《计算机欺诈与滥用法》(CFAA)及《数字千年版权法》(DMCA)保护,请务必:
-
仅抓取“公开可见、无需登录” 的页面;
-
遵守 robots.txt(Amazon 几乎全站 allow:/,但频率需合理);
-
数据仅限内部商业分析,不得直接转载、转售或公开 API 化;
-
生产环境先行法律评估,必要时与律师确认合规条款。
更多推荐


所有评论(0)