Использование CAPTCHA API с Selenium в Java

Автор: solvethree5 мин на чтение

Сначала запросите токен, затем до перехода на страницу настройте Chrome с тем же прокси и возвращёнными userAgent и secChUa. Поместите solution.token в поле CAPTCHA вашего приложения, отправьте форму и дождитесь состояния успешного выполнения в приложении, а не просто готовой задачи.

В примерах используются вспомогательный REST-класс на Java и Selenium ChromeDriver. Автоматическое обнаружение CAPTCHA и браузерный плагин не используются.

На этой странице

Подготовьте проект Java и целевой сайт

  1. 1.

    Настройте проект

    Используйте Java 21 или новее. В проекте Maven установите уровень компиляции Java 21 и добавьте Gson 2.15.0 и org.seleniumhq.selenium:selenium-java:4.52.0. Настройте Chrome и ChromeDriver по документации Selenium для Chrome.

  2. 2.

    Сохраните оба файла Java

    Сохраните первый пример как src/main/java/Solve.java, а второй — как src/main/java/BrowserSolve.java. Оставьте их в одном пакете; в предоставленных файлах нет объявления пакета. BrowserSolve вызывает Solve.api, а не Solve.main.

  3. 3.

    Активируйте профиль сайта

    Настройте целевое имя хоста в консоли solvethree и убедитесь, что его профиль активен для вашего рабочего пространства. Отправка имени хоста и действия не создаёт профиль. См. настройку профиля сайта.

  4. 4.

    Настройте окружение

    Задайте три переменные окружения ниже в процессе, который будет запускать BrowserSolve. Храните API-ключ рабочего пространства в доверенном процессе Java, а не на странице или в браузерном JavaScript.

Переменные окружения для BrowserSolve
Переменные окружения для BrowserSolve
ПеременнаяЗначение
SOLVETHREE_API_KEYAPI-ключ вашего рабочего пространства.
SOLVETHREE_PROXYURL HTTP-прокси с разрешённым доступом по IP, без встроенных имени пользователя или пароля, пути, строки запроса и фрагмента.
TARGET_URLПолный URL страницы приложения с формой.

Для этого примера требуется прокси с разрешённым доступом по IP, поскольку передача учётных данных прокси через его URL в Chrome не поддерживается. Одно и то же значение прокси передаётся в API задач и Chrome.

Добавьте Solve.java для запросов к API

Solve.api отправляет аутентифицированные запросы к https://api.solvethree.com/v1, проверяет HTTP-статус перед разбором успешного ответа и ограничивает время каждого запроса оставшимся временем до локального предельного срока.

Java 21+ · Solve.java (зависимость Gson)
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.*;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.UUID;

public class Solve {
    static final HttpClient HTTP = HttpClient.newBuilder()
        .connectTimeout(Duration.ofSeconds(15)).build();
    static final String KEY = System.getenv("SOLVETHREE_API_KEY");

    static JsonObject api(String path, JsonObject body, long deadline) throws Exception {
        long remaining = deadline - System.nanoTime();
        if (remaining <= 0) throw new IllegalStateException("Keep the task ID and resume polling");
        var builder = HttpRequest.newBuilder(URI.create("https://api.solvethree.com/v1" + path))
            .timeout(Duration.ofNanos(Math.min(remaining, Duration.ofSeconds(30).toNanos())))
            .header("Authorization", "Bearer " + KEY)
            .header("Content-Type", "application/json");
        if (body == null) builder.GET();
        else builder.POST(HttpRequest.BodyPublishers.ofString(body.toString()));
        var response = HTTP.send(builder.build(), HttpResponse.BodyHandlers.ofString());
        if (response.statusCode() < 200 || response.statusCode() >= 300)
            throw new IllegalStateException("API HTTP " + response.statusCode());
        return JsonParser.parseString(response.body()).getAsJsonObject();
    }
    public static void main(String[] args) throws Exception {
        String proxy = System.getenv("SOLVETHREE_PROXY");
        if (KEY == null || proxy == null) throw new IllegalArgumentException("Set the API key and proxy");
        var input = new JsonObject();
        input.addProperty("site", "accounts.example.com");
        input.addProperty("action", "login");
        input.addProperty("proxy", proxy);
        input.addProperty("requestId", UUID.randomUUID().toString());
        // Save input before creation if the task must be recovered after restart.
        long deadline = System.nanoTime() + Duration.ofSeconds(180).toNanos();
        var task = api("/tasks?waitMs=0", input, deadline);
        while (task.get("status").getAsString().equals("pending") || task.get("status").getAsString().equals("solving")) {
            String id = URLEncoder.encode(task.get("id").getAsString(), StandardCharsets.UTF_8);
            task = api("/tasks/" + id + "?waitMs=15000", null, deadline);
            if (task.get("status").getAsString().equals("pending") || task.get("status").getAsString().equals("solving"))
                Thread.sleep(1000);
        }
        if (!task.get("status").getAsString().equals("ready"))
            throw new IllegalStateException("Task failed: " + task.get("errorCode")
                + ": " + task.get("error"));
        var solution = task.getAsJsonObject("solution");
        // Submit solution.token with userAgent/secChUa and the original proxy.
        System.out.println("Ready task: " + task.get("id").getAsString());
    }
}

Отдельный метод main демонстрирует создание и опрос задачи. Его имя хоста accounts.example.com и действие login — заглушки. Для интеграции с Selenium используйте этот файл как вспомогательный класс и запускайте вместо него BrowserSolve; запуск обоих методов main создаст отдельные задачи.

Для запроса reCAPTCHA нужны имя хоста в site, action приложения и ровно одно из полей proxy или proxyCandidates. Не добавляйте type или url. Если recaptchaType не указан, выбирается enterprise-v3, который и используется в этих примерах.

Запустите Chrome с данными браузера из готового решения

BrowserSolve получает site из TARGET_URL, создаёт задачу и опрашивает её, пока статус — pending или solving. Только задача со статусом ready предоставляет решение для использования в браузере.

Java · передача готового решения в Chrome
// Save as BrowserSolve.java beside Solve.java from the Java REST quickstart.
// Add Selenium Java 4.52.0 and Gson 2.15.0 to your Maven dependencies; Java 21+.
import com.google.gson.JsonObject;
import java.net.URI;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.Map;
import java.util.UUID;
import org.openqa.selenium.By;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.support.ui.WebDriverWait;

public class BrowserSolve {
    public static void main(String[] args) throws Exception {
        String target = System.getenv("TARGET_URL");
        String proxy = System.getenv("SOLVETHREE_PROXY");
        URI proxyUri = URI.create(proxy);
        if (proxyUri.getUserInfo() != null) throw new IllegalArgumentException("Use an IP-allowlisted proxy");
        var input = new JsonObject();
        input.addProperty("site", URI.create(target).getHost());
        input.addProperty("action", "login");
        input.addProperty("proxy", proxy);
        input.addProperty("requestId", UUID.randomUUID().toString());
        long deadline = System.nanoTime() + Duration.ofSeconds(180).toNanos();
        var task = Solve.api("/tasks?waitMs=0", input, deadline);
        while (task.get("status").getAsString().matches("pending|solving")) {
            String id = URLEncoder.encode(task.get("id").getAsString(), StandardCharsets.UTF_8);
            task = Solve.api("/tasks/" + id + "?waitMs=15000", null, deadline);
            if (task.get("status").getAsString().matches("pending|solving")) Thread.sleep(1000);
        }
        if (!task.get("status").getAsString().equals("ready")) throw new IllegalStateException("Task failed");
        var solution = task.getAsJsonObject("solution");
        var options = new ChromeOptions();
        options.addArguments("--proxy-server=" + proxy, "--user-agent=" + solution.get("userAgent").getAsString());
        var browser = new ChromeDriver(options);
        try {
            browser.executeCdpCommand("Network.enable", Map.of());
            browser.executeCdpCommand("Network.setExtraHTTPHeaders", Map.of("headers",
                Map.of("sec-ch-ua", solution.get("secChUa").getAsString())));
            browser.get(target);
            var wait = new WebDriverWait(browser, Duration.ofSeconds(20));
            var field = wait.until(driver -> driver.findElement(By.name("g-recaptcha-response")));
            browser.executeScript("arguments[0].value=arguments[1]; arguments[0].dispatchEvent(new Event('input',{bubbles:true}));",
                field, solution.get("token").getAsString());
            browser.findElement(By.cssSelector("button[type=submit]")).click();
            wait.until(driver -> driver.findElement(By.id("success"))); // Your application's success marker.
        } finally { browser.quit(); }
    }
}

Порядок важен: ChromeOptions задаёт прокси и полученный user agent, затем ChromeDriver через CDP задаёт полученный заголовок sec-ch-ua до вызова browser.get(target). Не открывайте страницу сначала с другими параметрами идентификации, чтобы изменить их лишь при отправке токена.

В этом примере намеренно передаётся один proxy, чтобы Chrome мог использовать тот же прокси без выбора из нескольких вариантов. Целевой URL должен соответствовать имени хоста и действию, указанным для задачи.

Адаптируйте форму перед запуском BrowserSolve

Браузерный пример рассчитан на вашу собственную форму с полем ответа CAPTCHA, кнопкой отправки и элементом успешного выполнения. Измените эти значения под своё приложение перед запуском BrowserSolve.main с зависимостями Maven в classpath.

  • action: замените login на действие, которое ваше приложение использует при этой отправке.
  • g-recaptcha-response: проверьте имя поля, которое фактически отправляет ваша форма. Пример присваивает ему solution.token и отправляет всплывающее событие input.
  • button[type=submit]: замените этот селектор, если он не указывает на кнопку отправки нужной формы.
  • success: замените этот ID элемента на маркер состояния, который появляется только после успешной текущей отправки.

Присваивание значения полю и отправка события — не универсальный способ вызова обработчика CAPTCHA. Если логика формы вашего приложения не использует это поле или событие, адаптируйте этап отправки к её фактическому механизму привязки. Проверьте исходящий запрос: он должен содержать solution.token, а не ID задачи или старый токен.

Заключительное ожидание WebDriver проверяет наличие элемента успешного выполнения в приложении. Выбирайте этот маркер внимательно: элемент, присутствовавший до отправки, ничего не доказывает о новом запросе. Готовая задача означает, что solvethree сгенерировал решение; принятие запроса целевым приложением подтверждается его ответом.

Обрабатывайте ошибки без лишнего создания задач

API возвращает HTTP-ответ с кодом, не означающим успех.

Проверяйте HTTP-ответ отдельно от статуса задачи. Solve.api выбрасывает исключение, прежде чем обработать его как успешный ответ.

Попробуйте: Выясните причину ошибки запроса перед повторной попыткой. Проверьте поля запроса и активный профиль сайта, а не отправляйте снова и снова те же некорректные данные.

Задача переходит в статус failed.

Прочитайте errorCode и error в ответе задачи. Общее исключение BrowserSolve их не отображает, хотя отдельный пример Solve.java отображает.

Попробуйте: Прекратите опрос этой задачи и включите эти поля в обработку ошибок. Средства за неудачные решения возвращаются; не считайте, что любая ошибка связана с балансом.

Истекает время ожидания создания или локальный предельный срок.

Проверьте, есть ли у вас ID задачи или сохранённые исходное тело запроса и requestId. Тайм-аут соединения не доказывает, что создание не удалось.

Попробуйте: Возобновите получение результата по ID задачи или восстановите создание с тем же requestId и идентичными входными данными. Сохраните тело запроса до создания, если восстановление должно быть возможно после перезапуска; повторный запуск приведённого метода main генерирует новый requestId.

Задача готова, но приложение отклоняет отправку.

Проверьте отправляемый токен, действие, целевой URL, прокси, user agent и sec-ch-ua. Убедитесь, что приложение действительно отправило новое присвоенное значение поля.

Попробуйте: Исправьте несовпадающее значение или механизм передачи данных при отправке, прежде чем платить за ещё одно решение. Используйте прокси, с которым решение было получено, вместе с возвращёнными параметрами идентификации браузера.

Предельный срок API в 180 секунд и ожидание приложения в 20 секунд — локальные ограничения, выбранные для примеров, а не гарантии сервиса. Дополнительные сведения об ошибках API см. в разделе Ошибки CAPTCHA API.

Запустите первую задачу.

Начните с $5 на балансе рабочего пространства.

Создать рабочее пространство
Использование CAPTCHA API с Selenium в Java · solvethree