Resuelve y envía reCAPTCHA con aiohttp

Por solvethree5 min de lectura

Utiliza aiohttp directamente con la API REST: crea una tarea, consúltala hasta que esté lista y, después, envía solution.token al destino. Mantén las solicitudes a la API y al destino en sesiones separadas para que el token Bearer de tu espacio de trabajo nunca llegue al destino.

El ejemplo requiere Python 3.11+ e incluye la creación, las consultas y el envío al destino dentro de un único plazo de 180 segundos. Se trata de un tiempo de espera de la aplicación, no de un tiempo de resolución garantizado.

En esta página

Configura el sitio, el proxy y la solicitud al destino

  • Establece SOLVETHREE_API_KEY con la clave de API de tu espacio de trabajo. Mantenla en el servidor.
  • Establece SOLVETHREE_PROXY con la URL del proxy que el servicio debe utilizar para la resolución. El envío al destino utiliza ese mismo proxy.
  • Sustituye accounts.example.com y la acción login por el nombre de host y la acción que utiliza tu integración. El sitio necesita un perfil activo disponible para tu espacio de trabajo; configura el perfil antes de crear tareas.
  • Sustituye la URL de inicio de sesión del destino y los datos del formulario por la solicitud real de la aplicación. El ejemplo solo envía g-recaptcha-response; añade los demás campos que requiera tu destino y utiliza el campo de token que espere.

La tarea envía site, action y un único proxy. Utiliza reCAPTCHA Enterprise v3 de forma predeterminada; no añadas los campos type ni url. Si el flujo del destino necesita cookies de una solicitud anterior, realiza esas solicitudes a través de la sesión del destino: el ejemplo comienza con una sesión nueva.

Crea una sola vez, consulta y después envía

aiohttp · crear una sola vez, consultar periódicamente y enviar
# pip install aiohttp
import asyncio
import os
import uuid
from urllib.parse import urlsplit, unquote
import aiohttp

async def main():
    api = "https://api.solvethree.com/v1"
    proxy = os.environ["SOLVETHREE_PROXY"]
    task = {"site": "accounts.example.com", "action": "login", "proxy": proxy, "requestId": str(uuid.uuid4())}
    task_id = None
    try:
        async with asyncio.timeout(180):
            async with aiohttp.ClientSession(headers={"Authorization": "Bearer " + os.environ["SOLVETHREE_API_KEY"]}) as client:
                async with client.post(api + "/tasks", params={"waitMs": 0}, json=task, allow_redirects=False) as response:
                    response.raise_for_status()
                    if response.status >= 300: raise RuntimeError("Unexpected API redirect")
                    result = await response.json()
                task_id = result["id"]
                while result["status"] in ("pending", "solving"):
                    async with client.get(api + "/tasks/" + task_id, params={"waitMs": 15000}, allow_redirects=False) as response:
                        response.raise_for_status()
                        if response.status >= 300: raise RuntimeError("Unexpected API redirect")
                        result = await response.json()
                    if result["status"] in ("pending", "solving"): await asyncio.sleep(1)
                if result["status"] != "ready":
                    raise RuntimeError("Task failed: " + str(result.get("errorCode", "unknown")))
            solution = result["solution"]
            # A separate session prevents the API Bearer header reaching the target.
            proxy_url = urlsplit(proxy)
            netloc = proxy_url.netloc.rsplit("@", 1)[-1]
            server = f"{proxy_url.scheme}://{netloc}"
            auth = aiohttp.BasicAuth(unquote(proxy_url.username), unquote(proxy_url.password or "")) if proxy_url.username else None
            async with aiohttp.ClientSession() as target:
                async with target.post("https://accounts.example.com/login", proxy=server, proxy_auth=auth,
                    headers={"User-Agent": solution["userAgent"], "sec-ch-ua": solution["secChUa"]},
                    data={"g-recaptcha-response": solution["token"]}, allow_redirects=False) as response:
                    print("Application HTTP status:", response.status)
    except TimeoutError:
        print("Timed out; recover requestId", task["requestId"], "taskId", task_id)
        raise

asyncio.run(main())

Esto utiliza REST asíncrono directamente, no un método asíncrono del SDK oficial de Python. Realiza un único POST de creación a /v1/tasks, guarda el id devuelto y utiliza solicitudes GET para consultar esa tarea. pending y solving mantienen el bucle; ready permite el envío; una tarea en estado failed detiene el bucle.

El proxy del cuerpo de la tarea indica a solvethree qué proxy utilizar. Los argumentos proxy y proxy_auth de aiohttp aparecen solo en la llamada HTTP al destino, no en las llamadas a la API. El ejemplo separa las credenciales del proxy de la URL del servidor proxy y envía el token con los valores devueltos de userAgent y secChUa.

Las sesiones están separadas deliberadamente: solo la sesión de la API tiene la cabecera Authorization. Las redirecciones están desactivadas, y las redirecciones de la API se tratan como errores en lugar de seguirse.

Una solicitud a la API devuelve un error HTTP.

El ejemplo comprueba el estado HTTP antes de leer un cuerpo de respuesta de éxito.

Prueba esto: Gestiona el error HTTP por separado del fallo de la tarea. Corrige las entradas no válidas o los problemas de autorización antes de volver a intentarlo.

La tarea pasa a failed.

Revisa errorCode y error en la respuesta de la tarea. El ejemplo lanza una excepción solo con errorCode.

Prueba esto: Deja de consultar esa tarea y soluciona la causa indicada. Corrige la causa indicada antes de volver a intentarlo.

La tarea está en estado ready, pero la aplicación rechaza la solicitud.

Revisa el campo de token enviado, la URL de destino, la identidad de navegador y el proxy devueltos, así como los campos de formulario o las cookies necesarios.

Prueba esto: Envía solution.token con la identidad devuelta a través del mismo proxy. Interpreta la respuesta de la aplicación según los criterios de éxito de ese destino: el estado HTTP mostrado por sí solo no confirma la aceptación.

Recupérate de un tiempo de espera agotado sin pagar otra resolución

Que se agote el tiempo de espera de creación no demuestra que la creación haya fallado. El ejemplo conserva las entradas originales en task y muestra su requestId junto con task_id cuando vence el plazo. Esas variables solo se conservan mientras el proceso está en ejecución; los ID mostrados no constituyen un registro de recuperación completo.

  1. 1.

    Guarda la solicitud original antes de la creación

    Para poder recuperarte tras un reinicio, conserva de forma segura el cuerpo completo de la tarea, incluidos su requestId y su proxy. El ejemplo genera un UUID nuevo en cada ejecución, por lo que volver a ejecutarlo crea una nueva solicitud lógica.

  2. 2.

    Retoma la tarea existente

    Si tienes task_id, obtén esa tarea. Si se agotó el tiempo de espera de creación antes de que recibieras un ID, repite la creación con el cuerpo de la tarea y el requestId guardados, sin modificarlos. La API devuelve la tarea existente; cambiar las entradas mientras reutilizas el ID provoca un conflicto.

  3. 3.

    Comprueba si el envío ya se realizó

    El plazo también abarca el POST al destino. Si se agotó el tiempo de espera de esa solicitud, comprueba el resultado en la aplicación antes de volver a enviarla: puede haber recibido la solicitud aunque tu cliente no haya recibido una respuesta. Recuperar la tarea de solvethree no permite determinar qué ocurrió en el destino.

Ejecuta tu primera tarea.

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

Crear un espacio de trabajo
Resuelve y envía reCAPTCHA con aiohttp · solvethree