Scarnox · Blog

Солвер капчі на Java з HttpClient: робочий приклад (2026)

Солвер капчі на Java — це один HTTP-виклик. Java 11 і новіші мають java.net.http.HttpClient, тож єдина залежність цього посібника — Gson для JSON. Ви надсилаєте sitekey і URL сторінки до API Scarnox, читаєте solution.token і відправляєте токен у форму сайту. Посібник охоплює PopularCaptcha, яку Scarnox сьогодні розв'язує в режимі proxyless.

Почніть з безкоштовного пробного кредиту, картка не потрібна і спробуйте код на сторінці, якою володієте, перш ніж використовувати його деінде.

Вимоги

Клас солвера

Клас обгортає один HttpClient, який зберігає cookie, тож сесія може пройти через відправку форми. Метод розв'язання встановлює заголовок 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. Таймаут запиту — 185 секунд, бо API блокується, доки токен не буде готовий. Кроку опитування немає.

Головна програма

Метод main розв'язує з кількома повторами, а потім відправляє токен:

// 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

Чому так структуровано

Один клієнт на солвер. HttpClient є потокобезпечним і призначений для повторного використання. Створення одного на кожен запит марнує з'єднання.

Cookie увімкнено. CookieManager на клієнті зберігає сесію, яка завантажила форму, тож токен надсилається з cookie, які очікує сайт.

Явна перевірка помилки. API повертає errorId зі значенням 0 при успіху. Будь-яке інше значення супроводжується errorCode і errorDescription, і солвер викидає виключення з цим текстом. Токен лежить у solution.token.

Обидва поля капчі. Форма отримує і h-captcha-response, і g-recaptcha-response. Більшість сайтів читають одне з них.

Час

Токен спливає приблизно за дві хвилини. Розв'язуйте безпосередньо перед відправкою, а не на початку довгого завдання. Посібник про термін дії токена капчі пояснює, як розрахувати вікно і відрізнити прострочений токен від відхиленого.

Невидимі віджети

Передайте true третім аргументом у solve, коли віджет невидимий. Тоді запит містить is_invisible: true. Якщо сторінці потрібен JavaScript-колбек із токеном, серверний клієнт його не запустить, і краще підійде інструмент автоматизації браузера. Посібник про невидиму капчу перелічує патерни.

Обробка помилок

Повтори покривають тимчасові збої. Припиняйте повторювати помилки, які не зміняться, наприклад неправильний sitekey або URL сторінки, де віджет не працює, і записуйте їх для перегляду. Повна політика, включно з тим, на яких кодах зупинятися, є в обробці помилок солвера капчі.

Скрапінг відповідально

Якщо програма скрапить публічні сторінки, спершу перевірте robots.txt і умови сайту. Додайте затримку між запитами і зберігайте лише ті персональні дані, які вам потрібні. Щодо правового контексту дивіться чи законний веб-скрапінг.

Ціни

Ви платите за кожне успішне розв'язання, а невдалі розв'язання безкоштовні. Тарифи дивіться на сторінці цін. Щоб почати, почніть з безкоштовного пробного кредиту, картка не потрібна.

Наступні кроки