Javaでcaptcha解決:HttpClientを使った動作例(2026年)
Javaでcaptchaソルバーを作るのは、HTTP呼び出しが1回です。Java 11以降にはjava.net.http.HttpClientが標準で含まれているので、このガイドで必要な依存関係はJSON用のGsonだけです。Scarnox APIにサイトキーとページURLをPOSTし、solution.tokenを読み取り、トークンをサイトのフォームに送信します。このガイドでは、Scarnoxが現在プロキシなしで解決しているPopularCaptchaを扱います。
無料のトライアルクレジットで始める(カード登録は不要)と、他で使う前に、自分が管理しているページでコードを試せます。
必要なもの
- Java 17以降(
java -versionで確認)。 - Gson 2.10以降。Mavenでは
com.google.code.gson:gson:2.10.1です。 - ScarnoxのAPIキー。環境変数から読み込みます。
- ページのPopularCaptchaウィジェットのサイトキー。見つけ方はcaptchaのサイトキーの見つけ方を参照してください。
ソルバークラス
このクラスは、Cookieを扱う1つのHttpClientをラップしています。そのため、フォーム送信までセッションを維持できます。解決メソッドはBearerヘッダーを設定し、JSONボディを送信し、errorIdを確認します。
// ScarnoxSolver.java
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import java.net.CookieManager;
import java.net.CookiePolicy;
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.LinkedHashMap;
import java.util.Map;
public class ScarnoxSolver {
private static final String API_URL = "https://api.scarnox.com/api/tasks/create";
private static final Gson GSON = new Gson();
private final String apiKey;
private final HttpClient http = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(10))
.cookieHandler(new CookieManager(null, CookiePolicy.ACCEPT_ALL))
.build();
public ScarnoxSolver(String apiKey) {
this.apiKey = apiKey;
}
/** トークンを返す。トークンの準備ができるまで呼び出しはブロックされる。 */
public String solve(String sitekey, String pageUrl, boolean invisible) throws Exception {
Map<String, Object> body = new LinkedHashMap<>();
body.put("captcha_type", "PopularCaptchaTaskProxyless");
body.put("site_key", sitekey);
body.put("site_url", pageUrl);
if (invisible) {
body.put("is_invisible", true);
}
HttpRequest request = HttpRequest.newBuilder(URI.create(API_URL))
.timeout(Duration.ofSeconds(185))
.header("Authorization", "Bearer " + apiKey)
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(GSON.toJson(body)))
.build();
HttpResponse<String> response = http.send(request, HttpResponse.BodyHandlers.ofString());
JsonObject data;
try {
data = JsonParser.parseString(response.body()).getAsJsonObject();
} catch (RuntimeException e) {
throw new IllegalStateException("HTTP " + response.statusCode() + ": not JSON");
}
if (data.get("errorId").getAsInt() != 0) {
throw new IllegalStateException(
data.get("errorCode").getAsString() + ": "
+ data.get("errorDescription").getAsString());
}
return data.getAsJsonObject("solution").get("token").getAsString();
}
/** トークンをサイトのフォームに送信し、HTTPステータスを返す。 */
public int submit(String pageUrl, String email, String token) throws Exception {
// 先にページを読み込み、セッションCookieを設定させる。
http.send(HttpRequest.newBuilder(URI.create(pageUrl)).GET().build(),
HttpResponse.BodyHandlers.discarding());
String form = "email=" + enc(email)
+ "&h-captcha-response=" + enc(token)
+ "&g-recaptcha-response=" + enc(token);
HttpRequest request = HttpRequest.newBuilder(URI.create(pageUrl))
.header("Content-Type", "application/x-www-form-urlencoded")
.POST(HttpRequest.BodyPublishers.ofString(form))
.build();
return http.send(request, HttpResponse.BodyHandlers.discarding()).statusCode();
}
private static String enc(String value) {
return URLEncoder.encode(value, StandardCharsets.UTF_8);
}
}
AuthorizationヘッダーにはBearerの接頭辞を付けます。APIはトークンの準備ができるまでブロックするため、リクエストのタイムアウトは185秒です。ポーリングの手順はありません。
メインプログラム
メインメソッドは、数回のリトライで解決を試み、その後トークンを送信します。
// Main.java
public class Main {
public static void main(String[] args) throws Exception {
String apiKey = System.getenv("SCARNOX_API_KEY");
if (apiKey == null || apiKey.isBlank()) {
throw new IllegalStateException("Set SCARNOX_API_KEY");
}
String pageUrl = "https://example.com/signup";
String sitekey = "YOUR-SITEKEY-UUID";
ScarnoxSolver solver = new ScarnoxSolver(apiKey);
String token = null;
for (int attempt = 1; attempt <= 3 && token == null; attempt++) {
try {
token = solver.solve(sitekey, pageUrl, false);
} catch (IllegalStateException e) {
System.err.println("Attempt " + attempt + " failed: " + e.getMessage());
Thread.sleep(attempt * 1000L);
}
}
if (token == null) {
throw new IllegalStateException("No token after 3 attempts");
}
int status = solver.submit(pageUrl, "you@example.com", token);
System.out.println("Status: " + status);
}
}
実行します。
export SCARNOX_API_KEY=scarnox_YOURAPIKEY
javac -cp gson-2.10.1.jar *.java
java -cp .:gson-2.10.1.jar Main
この構成にしている理由
クライアントはソルバーごとに1つ。 HttpClientはスレッドセーフで、再利用するためのものです。リクエストごとに作ると、接続が無駄になります。
Cookieを有効にする。 クライアントのCookieManagerが、フォームを読み込んだセッションを維持するので、トークンは、サイトが期待するCookieと一緒に送られます。
明示的なエラーチェック。 APIは成功時にerrorIdを0で返します。それ以外の値の場合はerrorCodeとerrorDescriptionが含まれ、ソルバーはその文言を付けて例外を投げます。トークンはsolution.tokenにあります。
両方のcaptchaフィールド。 フォームにはh-captcha-responseとg-recaptcha-responseの両方を入れます。サイトはどちらか一方を読むことが多いためです。
タイミング
トークンは約2分で期限切れになります。長い処理の開始時ではなく、送信の直前に解決してください。猶予の取り方と、期限切れのトークンと拒否されたトークンの見分け方は、captchaトークンの期限切れで説明しています。
不可視のウィジェット
ウィジェットが不可視の場合は、solveの第3引数にtrueを渡してください。リクエストにはis_invisible: trueが含まれます。ページがトークンを受け取るJavaScriptのコールバックを必要とする場合は、サーバー側のクライアントではトリガーできないので、ブラウザの自動化ツールのほうが適しています。パターンは不可視captchaのガイドにまとめています。
エラー処理
リトライは一時的な失敗を扱います。サイトキーの誤りや、ウィジェットが動いていないページURLなど、変わらないエラーではリトライを止め、レビューのためにログに残してください。停止すべきコードを含む完全なポリシーは、captcha解決のエラー処理にあります。
スクレイピングの際の注意
プログラムが公開ページをスクレイピングするなら、まずrobots.txtとサイトの利用規約を確認してください。リクエストの間には遅延を入れ、必要な個人データだけを保持してください。法的な背景はWebスクレイピングは合法かを参照してください。
料金
成功した解決ごとに料金がかかり、失敗した解決は無料です。料金の詳細は料金ページを参照してください。始めるには、無料のトライアルクレジットで始める(カード登録は不要)ことができます。