Resolva e envie reCAPTCHA com aiohttp

Por solvethree4 min de leitura

Use aiohttp diretamente com a API REST: crie uma tarefa, consulte até que ela esteja pronta e envie solution.token ao destino. Mantenha as solicitações à API e ao destino em sessões separadas para que o token Bearer do seu workspace nunca chegue ao destino.

O exemplo exige Python 3.11+ e coloca a criação, as consultas e o envio ao destino dentro de um único prazo limite de 180 segundos. Esse é um timeout da aplicação, não uma promessa de tempo de resolução.

Nesta página

Defina o site, o proxy e a solicitação ao destino

  • Defina SOLVETHREE_API_KEY com a chave de API do seu workspace. Mantenha-a no servidor.
  • Defina SOLVETHREE_PROXY com a URL do proxy que o serviço deve usar na resolução. O envio ao destino usa esse mesmo proxy.
  • Substitua accounts.example.com e a ação login pelo hostname e pela ação usados na sua integração. O site precisa de um perfil ativo disponível para seu workspace; configure o perfil antes de criar tarefas.
  • Substitua a URL de login do destino e os dados do formulário pela solicitação real da aplicação. O exemplo envia apenas g-recaptcha-response; adicione os outros campos exigidos pelo destino e use o campo de token esperado por ele.

A tarefa envia site, action e um único proxy. Por padrão, ela usa reCAPTCHA Enterprise v3; não adicione os campos type nem url. Se o fluxo do destino precisar de cookies de uma solicitação anterior, faça essas solicitações pela sessão do destino — o exemplo começa com uma sessão nova.

Crie uma vez, consulte e envie

aiohttp · criar uma única vez, consultar periodicamente e 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())

Este exemplo usa REST assíncrono diretamente, não um método assíncrono do SDK oficial de Python. Ele faz um único POST de criação para /v1/tasks, salva o id retornado e usa solicitações GET para consultar essa tarefa. pending e solving mantêm o loop; ready permite o envio; uma tarefa com status failed interrompe o loop.

O campo proxy no corpo da tarefa informa ao solvethree qual proxy usar. Os argumentos proxy e proxy_auth do aiohttp aparecem apenas na chamada HTTP ao destino, não nas chamadas à API. O exemplo separa as credenciais do proxy da URL do servidor proxy e envia o token com os valores retornados de userAgent e secChUa.

As sessões são separadas intencionalmente: apenas a sessão da API tem o cabeçalho Authorization. Os redirecionamentos estão desativados, e os redirecionamentos da API são tratados como erros, em vez de serem seguidos.

Uma solicitação à API retorna um erro HTTP.

O exemplo verifica o status HTTP antes de ler um payload de sucesso.

Experimente: Trate o erro HTTP separadamente da falha da tarefa. Corrija entradas inválidas ou problemas de autorização antes de tentar novamente.

A tarefa chega ao status failed.

Verifique errorCode e error na resposta da tarefa. O exemplo lança uma exceção apenas com errorCode.

Experimente: Pare de consultar essa tarefa e trate a causa informada. Corrija a causa informada antes de tentar novamente.

A tarefa está no status ready, mas a aplicação rejeita a solicitação.

Verifique o campo do token enviado, a URL do destino, a identidade do navegador e o proxy retornados, além dos campos de formulário ou cookies necessários.

Experimente: Envie solution.token com a identidade retornada pelo mesmo proxy. Leia a resposta da aplicação usando os critérios de sucesso desse destino — o status HTTP exibido, por si só, não comprova a aceitação.

Recupere-se de um timeout sem comprar outra resolução

Um timeout na criação não prova que ela falhou. O exemplo mantém as entradas originais em task e exibe seu requestId junto com task_id quando o prazo limite expira. Essas variáveis só persistem enquanto o processo está em execução; os IDs exibidos não são um registro completo de recuperação.

  1. 1.

    Salve a solicitação original antes da criação

    Para recuperar após reinicializações, armazene com segurança o corpo completo da tarefa, incluindo requestId e proxy. O exemplo gera um novo UUID a cada execução, então simplesmente executá-lo novamente cria uma nova solicitação lógica.

  2. 2.

    Retome a tarefa existente

    Se você tem task_id, consulte essa tarefa. Se a criação atingiu o timeout antes de você receber um ID, repita a criação com o corpo da tarefa e o requestId salvos, sem alterações. A API retorna a tarefa existente; alterar as entradas e reutilizar o ID gera um conflito.

  3. 3.

    Verifique se o envio já ocorreu

    O prazo limite também abrange o POST ao destino. Se essa solicitação atingiu o timeout, verifique o resultado na aplicação antes de enviar novamente: ela pode ter recebido a solicitação mesmo que seu cliente não tenha recebido uma resposta. Recuperar a tarefa do solvethree não determina o que aconteceu no destino.

Execute sua primeira tarefa.

Comece com $5 em créditos no espaço de trabalho.

Criar um espaço de trabalho
Resolva e envie reCAPTCHA com aiohttp · solvethree