Solver captcha w Javie z HttpClient: działający przykład (2026)
Solver captcha w Javie to jedno wywołanie HTTP. Java 11 i nowsze zawierają java.net.http.HttpClient, więc jedyną zależnością tego przewodnika jest Gson do obsługi JSON. Wysyłasz sitekey i adres strony do API Scarnox, odczytujesz solution.token i przesyłasz token do formularza strony. Ten przewodnik dotyczy PopularCaptcha, które Scarnox dziś rozwiązuje bez proxy.
Zacznij od darmowego kredytu próbnego, bez karty i wypróbuj kod na stronie, którą kontrolujesz, zanim użyjesz go gdziekolwiek indziej.
Wymagania
- Java 17 lub nowsza (
java -version). - Gson 2.10 lub nowszy. W Maven:
com.google.code.gson:gson:2.10.1. - Klucz API Scarnox odczytywany ze zmiennej środowiskowej.
- Sitekey widżetu PopularCaptcha na stronie. Pomoc w jego znalezieniu znajdziesz w artykule jak znaleźć sitekey captcha.
Klasa solvera
Klasa opakowuje jeden HttpClient, który przechowuje ciasteczka, dzięki czemu sesja może przetrwać do wysłania formularza. Metoda solve ustawia nagłówek Bearer, wysyła treść JSON i sprawdza 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;
}
/** Zwraca token. Wywołanie blokuje się, aż token będzie gotowy. */
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();
}
/** Wysyła token do formularza strony i zwraca status HTTP. */
public int submit(String pageUrl, String email, String token) throws Exception {
// Najpierw załaduj stronę, aby ustawić ciasteczko sesji.
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);
}
}
Nagłówek Authorization używa prefiksu Bearer. Timeout żądania wynosi 185 sekund, bo API blokuje się, aż token będzie gotowy. Nie ma etapu odpytywania.
Główny program
Metoda main rozwiązuje captcha z kilkoma próbami, a następnie wysyła token:
// 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);
}
}
Uruchom:
export SCARNOX_API_KEY=scarnox_YOURAPIKEY
javac -cp gson-2.10.1.jar *.java
java -cp .:gson-2.10.1.jar Main
Dlaczego jest zbudowane w ten sposób
Jeden klient na solver. HttpClient jest bezpieczny wątkowo i ma być używany ponownie. Tworzenie go przy każdym żądaniu marnuje połączenia.
Ciasteczka włączone. CookieManager na kliencie zachowuje sesję, która załadowała formularz, dzięki czemu token wysyłany jest z ciasteczkiem, którego oczekuje serwis.
Jawne sprawdzenie błędu. API zwraca errorId równe 0 przy sukcesie. Każda inna wartość zawiera errorCode i errorDescription, a solver rzuca wyjątek z tym tekstem. Token znajduje się w solution.token.
Oba pola captcha. Formularz dostaje h-captcha-response i g-recaptcha-response. Większość serwisów czyta jedno z nich.
Czas
Token wygasa po około dwóch minutach. Rozwiązuj tuż przed wysłaniem, a nie na początku długiego zadania. Wygasanie tokenu captcha wyjaśnia, jak dobrać okno czasowe i odróżnić token wygasły od odrzuconego.
Niewidoczne widżety
Przekaż true jako trzeci argument do solve, gdy widżet jest niewidoczny. Żądanie zawiera wtedy is_invisible: true. Jeśli strona wymaga wywołania zwrotnego JavaScript z tokenem, klient po stronie serwera nie może go uruchomić i lepszym wyborem jest narzędzie do automatyzacji przeglądarki. Wzorce opisuje przewodnik o niewidocznej captcha.
Obsługa błędów
Ponawianie obejmuje przejściowe błędy. Przestań ponawiać przy błędach, które się nie zmienią, na przykład przy złym sitekey lub adresie strony, na której widżet nie działa, i zapisz je do przeglądu. Pełna polityka, w tym kody, przy których należy się zatrzymać, znajduje się w artykule obsługa błędów solvera captcha.
Scrapuj odpowiedzialnie
Jeśli program scrapuje publiczne strony, najpierw sprawdź robots.txt i regulamin serwisu. Dodaj opóźnienie między żądaniami i zachowuj tylko dane osobowe, których potrzebujesz. Tło prawne znajdziesz w artykule czy web scraping jest legalny.
Cennik
Płacisz za każde udane rozwiązanie, a nieudane rozwiązania są bezpłatne. Stawki znajdziesz na stronie cennika. Aby zacząć, zacznij od darmowego kredytu próbnego, bez karty.