Scarnox · Blog

Captcha-Solver in Java mit HttpClient: ein funktionierendes Beispiel (2026)

Ein Captcha-Solver in Java ist ein einziger HTTP-Aufruf. Java 11 und neuer bringen java.net.http.HttpClient mit, daher braucht dieser Leitfaden nur eine Abhängigkeit: Gson, für JSON. Sie senden den Sitekey und die Seiten-URL an die Scarnox-API, lesen solution.token und reichen das Token an das Formular der Website weiter. Dieser Leitfaden behandelt PopularCaptcha, das Scarnox heute proxyless löst.

Starten Sie mit einer kostenlosen Testphase, ganz ohne Karte und probieren Sie den Code an einer Seite aus, die Ihnen gehört, bevor Sie ihn anderswo einsetzen.

Voraussetzungen

Die Solver-Klasse

Die Klasse umschließt einen HttpClient, der Cookies speichert, sodass eine Sitzung bis zum Absenden des Formulars erhalten bleibt. Die Lösungsmethode setzt den Bearer-Header, sendet den JSON-Body und prüft 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;
    }

    /** Gibt das Token zurück. Der Aufruf blockiert, bis das Token bereit ist. */
    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();
    }

    /** Sendet das Token an das Formular der Website und gibt den HTTP-Status zurück. */
    public int submit(String pageUrl, String email, String token) throws Exception {
        // Seite zuerst laden, damit das Sitzungs-Cookie gesetzt wird.
        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);
    }
}

Der Header Authorization verwendet das Präfix Bearer. Das Anfrage-Timeout beträgt 185 Sekunden, weil die API blockiert, bis das Token bereit ist. Es gibt keinen Polling-Schritt.

Das Hauptprogramm

Die Methode main löst mit ein paar Wiederholungen und reicht das Token dann ein:

// 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);
    }
}

Ausführen:

export SCARNOX_API_KEY=scarnox_YOURAPIKEY
javac -cp gson-2.10.1.jar *.java
java -cp .:gson-2.10.1.jar Main

Warum es so aufgebaut ist

Ein Client pro Solver. HttpClient ist threadsicher und dafür gedacht, wiederverwendet zu werden. Einen pro Anfrage zu erstellen verschwendet Verbindungen.

Cookies aktiviert. Der CookieManager am Client hält die Sitzung, die das Formular geladen hat, sodass das Token mit dem Cookie gesendet wird, das die Website erwartet.

Explizite Fehlerprüfung. Die API liefert errorId als 0 bei Erfolg. Jeder andere Wert enthält errorCode und errorDescription, und der Solver wirft mit diesem Text eine Ausnahme. Das Token steht unter solution.token.

Beide Captcha-Felder. Das Formular bekommt h-captcha-response und g-recaptcha-response. Die meisten Websites lesen das eine oder das andere.

Timing

Das Token läuft nach etwa zwei Minuten ab. Lösen Sie direkt vor dem Absenden, nicht zu Beginn eines langen Jobs. Ablauf von Captcha-Tokens erklärt, wie Sie das Zeitfenster bemessen und ein abgelaufenes von einem abgelehnten Token unterscheiden.

Unsichtbare Widgets

Übergeben Sie true als dritten Parameter an solve, wenn das Widget unsichtbar ist. Die Anfrage enthält dann is_invisible: true. Braucht die Seite einen JavaScript-Callback mit dem Token, kann ein serverseitiger Client ihn nicht auslösen, und ein Browser-Automatisierungswerkzeug ist die bessere Wahl. Der Leitfaden unsichtbares Captcha listet die Muster auf.

Fehlerbehandlung

Wiederholungen decken vorübergehende Fehler ab. Hören Sie auf, Fehler zu wiederholen, die sich nicht ändern, etwa einen falschen Sitekey oder eine Seiten-URL, auf der das Widget nicht läuft, und protokollieren Sie sie zur Prüfung. Die vollständige Richtlinie, einschließlich der Codes, bei denen Sie aufhören sollten, steht unter Fehlerbehandlung für Captcha-Solver.

Verantwortungsvoll scrapen

Wenn das Programm öffentliche Seiten scrapt, prüfen Sie zuerst die robots.txt und die Nutzungsbedingungen der Website. Fügen Sie eine Verzögerung zwischen den Anfragen hinzu und behalten Sie nur die personenbezogenen Daten, die Sie brauchen. Den rechtlichen Hintergrund finden Sie unter ist Web Scraping legal.

Preise

Sie zahlen pro erfolgreicher Lösung, fehlgeschlagene Lösungen sind kostenlos. Die Tarife finden Sie auf der Preisseite. Zum Einstieg starten Sie mit einer kostenlosen Testphase, ganz ohne Karte.

Nächste Schritte