关于 scraperapi-java-sdk
面向使用 ScraperAPI 的开发者的 Java SDK 指南,涵盖同步抓取与批量任务。内容说明请求选项、升级策略、结构化响应和错误处理。
主要功能
- 01GET 请求的链式构建器
- 02JavaScript 渲染与代理选项
- 03粘性会话与重试
- 04支持站点的结构化 JSON
- 05批量任务异步处理
适用场景
- 01获取单个 URL 的 HTML
- 02抓取 JavaScript 渲染页面
- 03分页请求间保持代理会话
- 04异步处理大量 URL
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ scraperapi/ scraperapi-skills --skill scraperapi-java-sdk安装后在 Claude Code 中输入/scraperapi-java-sdk确认已加载。
详细介绍
ScraperAPI — Java SDK 最佳实践
要求: Java 8+、Maven 或 Gradle、SCRAPERAPI_API_KEY 环境变量。
设置
Maven
<dependency>
<groupId>com.scraperapi</groupId>
<artifactId>sdk</artifactId>
<version>1.2</version>
</dependency>
Gradle
implementation 'com.scraperapi:sdk:1.2'
实例化客户端
import com.scraperapi.ScraperApiClient;
ScraperApiClient client = new ScraperApiClient(System.getenv("SCRAPERAPI_API_KEY"));
切勿将 API 密钥硬编码。每次都从环境变量中读取。
基本用法
Java SDK 使用流式构建器模式。在 .get() 的结果上链式调用参数方法,然后调用 .result() 阻塞并获取 HTML。
// Simple GET — returns HTML as a String
String html = client.get("https://example.com/").result();
// With parameters — chain before .result()
String html = client.get("https://example.com/")
.render(true)
.result();
// Multiple parameters
String html = client.get("https://example.com/")
.render(true)
.countryCode("us")
.result();
选择指南
| 情况 | 方案 |
|---|---|
| 单个 URL,同步处理 | .get(url).<params>.result() |
| 页面通过 JavaScript 加载内容 | 链式调用 .render(true) |
| 网站屏蔽数据中心代理 | 链式调用 .premium(true) |
| 最强反机器人保护 | 链式调用 .ultraPremium(true) |
| 同一域名上的多步骤/分页流程 | 链式调用 .sessionNumber(n) |
| 可能发生暂时性故障 | 链式调用 .retry(n) |
| 20 个以上 URL 或批处理任务 | 使用 HttpClient 调用异步端点 |
| 受支持的平台(Amazon、Google 等) | 结构化数据端点 |
参数参考
渲染
// Render JavaScript before returning HTML
// Use when: page is a React/Vue/Angular SPA, or initial scrape returns empty content
// Cost: +10 credits
String html = client.get("https://spa-site.com/").render(true).result();
// Wait for a DOM element before capturing (requires render)
String html = client.get("https://spa-site.com/")
.render(true)
.waitForSelector(".product-list")
.result();
不要默认调用 .render(true)——先尝试不启用它。这会增加费用和延迟。
代理与地理定位
// Route through a country-specific proxy — no extra credit cost
String html = client.get("https://example.com/").countryCode("gb").result();
// Premium residential/mobile IPs — for sites that block datacenter proxies
// Cost: 10 credits (25 with render)
String html = client.get("https://hard-site.com/").premium(true).result();
// Ultra-premium — for the toughest anti-bot protections
// Cost: 30 credits (75 with render)
// Note: incompatible with custom headers
String html = client.get("https://hardest-site.com/").ultraPremium(true).result();
premium 和 ultraPremium 互斥——切勿同时链式调用。
升级顺序:标准(1 cr)→ 渲染(10 cr)→ premium(10 cr)→ ultraPremium(30 cr)。
会话(粘性代理)
// Reuse the same proxy IP across requests — useful for pagination and multi-step flows
// Sessions expire 15 minutes after last use; any integer is a valid ID
String page1 = client.get("https://example.com/page1").sessionNumber(42).result();
String page2 = client.get("https://example.com/page2").sessionNumber(42).result();
设备类型与自动解析
// Emulate a mobile browser user-agent
String html = client.get("https://example.com/").deviceType("mobile").result();
// Return structured JSON for supported sites (Amazon, Google, etc.)
String json = client.get("https://amazon.com/dp/B09V3KXJPB").autoparse(true).result();
重试
// Override the default retry count (default: 3)
// ScraperAPI retries failed requests for up to 70 seconds internally;
// .retry() controls how many times the SDK retries after a non-200 response
String html = client.get("https://flaky-site.com/").retry(5).result();
不要设置过短的超时时间——SDK 默认值经过校准,可留出 ScraperAPI 内部重试窗口(最长 70 秒)。将客户端超时设为 5 秒会导致误报失败。
升级阶梯
始终从最便宜的方案开始。只有在网站屏蔽了上一层级时才升级。
public static String scrapeWithEscalation(ScraperApiClient client, String url) throws Exception {
// Try each tier in order — stop at the first success
String[][] tiers = {
{}, // 1 credit — standard
{"render:true"}, // 10 credits
{"premium:true"}, // 10 credits
{"premium:true", "render:true"}, // 25 credits
{"ultraPremium:true"}, // 30 credits
};
// Practical implementation — explicit tier cascade
String[] attempts = { "standard", "render", "premium", "premiumRender", "ultraPremium" };
for (String tier : attempts) {
try {
var req = client.get(url);
switch (tier) {
case "render": req = req.render(true); break;
case "premium": req = req.premium(true); break;
case "premiumRender": req = req.premium(true).render(true); break;
case "ultraPremium": req = req.ultraPremium(true); break;
}
String html = req.result();
if (html != null && html.toLowerCase().contains("<html")) return html;
} catch (Exception e) {
// Log and try next tier
}
}
return null;
}
异步任务(用于批处理)
.result() 会阻塞调用线程。对于 20 个以上的 URL,请通过 REST 端点提交异步任务,并发收集结果。
import java.net.http.*;
import java.net.URI;
import com.fasterxml.jackson.databind.ObjectMapper;
private static final String API_KEY = System.getenv("SCRAPERAPI_API_KEY");
private static final HttpClient HTTP = HttpClient.newHttpClient();
private static final ObjectMapper JSON = new ObjectMapper();
public static Map<String, Object> submitJob(String url) throws Exception {
String body = JSON.writeValueAsString(Map.of("apiKey", API_KEY, "url", url));
HttpRequest req = HttpRequest.newBuilder()
.uri(URI.create("https://async.scraperapi.com/jobs"))
.POST(HttpRequest.BodyPublishers.ofString(body))
.header("Content-Type", "application/json")
.build();
HttpResponse<String> resp = HTTP.send(req, HttpResponse.BodyHandlers.ofString());
return JSON.readValue(resp.body(), Map.class); // {id, statusUrl}
}
public static String pollJob(Map<String, Object> job, int maxWaitSec) throws Exception {
long deadline = System.currentTimeMillis() + maxWaitSec * 1000L;
while (System.currentTimeMillis() < deadline) {
HttpRequest req = HttpRequest.newBuilder()
.uri(URI.create((String) job.get("statusUrl")))
.GET().build();
Map<String, Object> data = JSON.readValue(
HTTP.send(req, HttpResponse.BodyHandlers.ofString()).body(), Map.class);
if ("finished".equals(data.get("status")))
return ((Map<?, ?>) data.get("response")).get("body").toString();
if ("failed".equals(data.get("status")))
throw new RuntimeException("Job " + job.get("id") + " failed");
Thread.sleep(5_000);
}
throw new RuntimeException("Job " + job.get("id") + " timed out");
}
结构化数据端点
对于受支持的平台,请使用结构化端点,而非抓取原始 HTML。
public static String structuredGet(String vertical, Map<String, String> params) throws Exception {
StringBuilder query = new StringBuilder("api_key=" + API_KEY);
params.forEach((k, v) -> query.append("&").append(k).append("=").append(v));
URI uri = URI.create("https://api.scraperapi.com/structured/" + vertical + "?" + query);
HttpRequest req = HttpRequest.newBuilder().uri(uri).GET().build();
HttpResponse<String> resp = HTTP.send(req, HttpResponse.BodyHandlers.ofString());
if (resp.statusCode() != 200)
throw new RuntimeException("Error " + resp.statusCode());
return resp.body();
}
// Google SERP
String serp = structuredGet("google/search", Map.of("query", "java web scraping"));
// Amazon product
String product = structuredGet("amazon/product", Map.of("asin", "B09V3KXJPB"));
// Walmart search
String items = structuredGet("walmart/search", Map.of("query", "standing desk", "tld", "com"));
错误处理
public static String safeScrape(ScraperApiClient client, String url) {
try {
return client.get(url).retry(3).result();
} catch (Exception e) {
String msg = e.getMessage() != null ? e.getMessage() : "";
if (msg.contains("401")) throw new RuntimeException("Invalid API key — check SCRAPERAPI_API_KEY", e);
if (msg.contains("403")) throw new RuntimeException("Blocked or out of credits — try premium/ultraPremium", e);
if (msg.contains("429")) throw new RuntimeException("Rate limit — reduce concurrency or use async", e);
if (msg.contains("500") || msg.contains("503"))
throw new RuntimeException("Transient error — retry with backoff", e);
throw new RuntimeException("Scrape failed: " + url, e);
}
}
状态代码:200 表示成功,401 表示密钥无效,403 表示被屏蔽/无可用额度,404 表示未找到目标,429 表示触发速率限制,500/503 表示暂时性故障(不扣费——可安全地采用退避策略重试)。
额度费用参考
| 请求类型 | 额度 |
|---|---|
标准 .result() | 1 |
.render(true) | 10 |
.premium(true) | 10 |
.premium(true).render(true) | 25 |
.ultraPremium(true) | 30 |
.ultraPremium(true).render(true) | 75 |
文档
评论
支持 Markdown