Usa la API de CAPTCHA con Selenium en Java

Por solvethree6 min de lectura

Solicita primero el token y después configura Chrome con el mismo proxy y los valores userAgent y secChUa devueltos antes de navegar. Introduce solution.token en el campo CAPTCHA de tu aplicación, envía el formulario y espera el estado de éxito de la aplicación, no solo una tarea lista.

Los ejemplos usan una clase auxiliar REST de Java y Selenium ChromeDriver. No intervienen la detección automática de CAPTCHA ni un complemento de navegador.

En esta página

Prepara el proyecto Java y el sitio de destino

  1. 1.

    Configura el proyecto

    Usa Java 21 o posterior. En un proyecto Maven, establece el nivel de compilación de Java en 21 y añade Gson 2.15.0 y org.seleniumhq.selenium:selenium-java:4.52.0. Configura Chrome y ChromeDriver siguiendo la documentación de Selenium para Chrome.

  2. 2.

    Guarda ambos archivos Java

    Guarda el primer ejemplo como src/main/java/Solve.java y el segundo como src/main/java/BrowserSolve.java. Mantenlos en el mismo paquete; los archivos proporcionados no tienen declaración de paquete. BrowserSolve llama a Solve.api, no a Solve.main.

  3. 3.

    Activa el perfil del sitio

    Configura el nombre de host de destino en la consola de solvethree y asegúrate de que su perfil esté activo para tu espacio de trabajo. Enviar un nombre de host y una acción no crea un perfil. Consulta la configuración del perfil del sitio.

  4. 4.

    Configura el entorno

    Configura las tres variables de entorno siguientes en el proceso que ejecutará BrowserSolve. Mantén la clave de API del espacio de trabajo en tu proceso Java de confianza, no en la página ni en el JavaScript del navegador.

Variables de entorno para BrowserSolve
Variables de entorno para BrowserSolve
VariableValor
SOLVETHREE_API_KEYLa clave de API de tu espacio de trabajo.
SOLVETHREE_PROXYUna URL de proxy HTTP con lista de IP permitidas, sin nombre de usuario ni contraseña integrados, ruta, consulta o fragmento.
TARGET_URLLa URL completa de la página de la aplicación que contiene el formulario.

Este ejemplo requiere un proxy con lista de IP permitidas porque no permite pasar credenciales de proxy a través de la URL de proxy de Chrome. Se envía el mismo valor de proxy a la API de tareas y a Chrome.

Añade Solve.java para las solicitudes a la API

Solve.api envía solicitudes autenticadas a https://api.solvethree.com/v1, comprueba el estado HTTP antes de analizar una respuesta correcta y limita cada solicitud al tiempo restante del plazo local.

Java 21+ · Solve.java (dependencia de 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());
    }
}

El método independiente main muestra la creación de tareas y su consulta periódica. Su nombre de host accounts.example.com y su acción login son valores de ejemplo. Para la integración con Selenium, usa este archivo como clase auxiliar y ejecuta BrowserSolve en su lugar; ejecutar ambos métodos main crearía tareas independientes.

Una solicitud de reCAPTCHA necesita un nombre de host en site, la action de la aplicación y exactamente uno de los campos proxy o proxyCandidates. No añadas type ni url. Si se omite recaptchaType, se selecciona enterprise-v3, que es lo que usan estos ejemplos.

Inicia Chrome con la identidad de la solución lista

BrowserSolve obtiene site a partir de TARGET_URL, crea la tarea y la consulta periódicamente mientras su estado sea pending o solving. Solo una tarea ready proporciona la solución que usa el navegador.

Java · pasar una solución lista a 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(); }
    }
}

El orden importa: ChromeOptions configura el proxy y el agente de usuario devuelto; después, ChromeDriver configura el encabezado sec-ch-ua devuelto mediante CDP antes de browser.get(target). No navegues primero con una identidad distinta para cambiarla solo al enviar el token.

Este ejemplo envía deliberadamente un único proxy, para que Chrome pueda usar ese mismo proxy sin tener que elegir entre varios candidatos. Mantén la URL de destino coherente con el nombre de host y la acción usados para la tarea.

Adapta el formulario antes de ejecutar BrowserSolve

El ejemplo de navegador está dirigido a un formulario propio con un campo de respuesta CAPTCHA, un botón de envío y un elemento de éxito. Cambia estos valores específicos de la aplicación antes de ejecutar BrowserSolve.main con las dependencias de Maven en su classpath.

  • action: sustituye login por la acción que usa tu aplicación para este envío.
  • g-recaptcha-response: confirma el nombre del campo que realmente envía tu formulario. El ejemplo asigna solution.token y dispara un evento input que se propaga.
  • button[type=submit]: sustituye este selector si no identifica el botón de envío del formulario previsto.
  • success: sustituye este ID de elemento por un estado que solo aparezca después de que el envío actual se complete correctamente.

Asignar un valor a un campo y disparar un evento no constituye un callback universal de CAPTCHA. Si la lógica del formulario de tu aplicación no utiliza ese campo o evento, adapta el paso de envío a su vinculación real. Inspecciona la solicitud saliente para confirmar que contiene solution.token, no el ID de la tarea ni un token antiguo.

La espera final de WebDriver comprueba la presencia de un elemento de éxito de la aplicación. Elige ese indicador con cuidado: un elemento que ya estaba presente antes del envío no demuestra nada sobre la nueva solicitud. Una tarea lista significa que solvethree ha generado una solución; la respuesta de la aplicación determina si el destino la ha aceptado.

Gestiona los fallos sin crear otra tarea innecesariamente

La API devuelve una respuesta HTTP que no indica éxito.

Comprueba la respuesta HTTP por separado del estado de la tarea. Solve.api lanza una excepción antes de tratarla como una respuesta de éxito.

Prueba esto: Diagnostica el error de la solicitud antes de reintentarlo. Confirma los campos de la solicitud y el perfil de sitio activo en lugar de enviar repetidamente los mismos datos no válidos.

La tarea pasa a failed.

Lee errorCode y error en la respuesta de la tarea. La excepción genérica de BrowserSolve no los muestra, aunque el ejemplo independiente de Solve.java sí lo hace.

Prueba esto: Deja de consultar esa tarea e incluye esos campos en la gestión de errores. Las resoluciones fallidas se reembolsan; no supongas que todos los fallos se deben a un problema de créditos.

La creación agota el tiempo de espera o vence el plazo local.

Determina si ya tienes un ID de tarea o si guardaste el cuerpo de la solicitud original y requestId. Que una conexión agote el tiempo de espera no demuestra que la creación haya fallado.

Prueba esto: Reanuda la consulta con el ID de la tarea o recupera la creación con el mismo requestId y datos de entrada idénticos. Guarda el cuerpo antes de la creación si la recuperación debe ser posible tras un reinicio; volver a ejecutar el método main proporcionado genera un requestId nuevo.

La tarea está lista, pero la aplicación rechaza el envío.

Inspecciona el token saliente, la acción, la URL de destino, el proxy, el agente de usuario y sec-ch-ua. Confirma que la aplicación haya enviado realmente el valor recién asignado al campo.

Prueba esto: Corrige el valor que no coincide o la vinculación del envío antes de pagar por otra resolución. Mantén juntos el proxy que funcionó y la identidad devuelta.

El plazo de 180 segundos de la API y la espera de 20 segundos de la aplicación son límites locales elegidos para los ejemplos, no garantías del servicio. Para obtener más detalles sobre los errores de la API, consulta Errores de la API de CAPTCHA.

Ejecuta tu primera tarea.

Empieza con $5 de crédito en tu espacio de trabajo.

Crear un espacio de trabajo
Usa la API de CAPTCHA con Selenium en Java · solvethree