Use Cases

CAPTCHA no Playwright: como resolver com a API da CaptchaAI

Um teste ou script em Playwright trava assim que esbarra em um CAPTCHA: o auto-wait garante que o elemento fique visível, mas não resolve desafio nenhum sozinho. A saída é combinar as duas peças — o Playwright cuida da navegação em Chromium, Firefox e WebKit, e a API da CaptchaAI resolve o desafio no lado do servidor, devolvendo um token que você injeta na página antes de enviar o formulário. É a mesma divisão de responsabilidades que já vale para Selenium e Puppeteer: o framework de automação nunca resolve o CAPTCHA sozinho, porque isso exige um serviço dedicado do outro lado da chamada HTTP.

Pré-requisitos para integrar Playwright e CaptchaAI

  • Python: pip install playwright requests e depois playwright install
  • Node.js: npm install playwright axios
  • Chave de API da CaptchaAI: disponível em captchaai.com

Python: Playwright + CaptchaAI

Configuração inicial. A função abaixo envia a sitekey da página para a CaptchaAI e faz o polling do resultado até o token ficar pronto — o mesmo par in.php/res.php usado em qualquer linguagem, só muda o cliente HTTP:

from playwright.sync_api import sync_playwright
import requests
import time

API_KEY = "YOUR_API_KEY"

def solve_recaptcha(site_key, page_url):
    resp = requests.get("https://ocr.captchaai.com/in.php", params={
        "key": API_KEY,
        "method": "userrecaptcha",
        "googlekey": site_key,
        "pageurl": page_url
    })
    if not resp.text.startswith("OK|"):
        raise Exception(resp.text)
    task_id = resp.text.split("|")[1]

    for _ in range(60):
        time.sleep(5)
        result = requests.get("https://ocr.captchaai.com/res.php", params={
            "key": API_KEY, "action": "get", "id": task_id
        })
        if result.text == "CAPCHA_NOT_READY": continue
        if result.text.startswith("OK|"): return result.text.split("|")[1]
        raise Exception(result.text)
    raise TimeoutError()

Exemplo completo de login. Fluxo de ponta a ponta: abrir a página de staging, preencher usuário e senha, detectar o widget de reCAPTCHA, resolver via API e só então injetar o token antes de clicar em enviar:

def login_with_captcha(url, username, password):
    with sync_playwright() as p:
        browser = p.chromium.launch(headless=True)
        context = browser.new_context(
            user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
        )
        page = context.new_page()
        page.goto(url)

        # Fill login form
        page.fill("#username", username)
        page.fill("#password", password)

        # Check for reCAPTCHA
        recaptcha = page.query_selector(".g-recaptcha")
        if recaptcha:
            site_key = recaptcha.get_attribute("data-sitekey")
            print(f"Solving reCAPTCHA: {site_key}")

            token = solve_recaptcha(site_key, page.url)

            # Inject token
            page.evaluate(f"""
                document.getElementById('g-recaptcha-response').innerHTML = '{token}';
                document.getElementById('g-recaptcha-response').style.display = '';
            """)

        # Submit
        page.click('button[type="submit"]')
        page.wait_for_load_state("networkidle")

        print(f"Current URL: {page.url}")
        content = page.content()

        browser.close()
        return content

result = login_with_captcha(
    "https://staging.example.com/qa-login",
    "user@example.com",
    "password123"
)

Versão assíncrona. Para suítes de teste que já rodam sobre asyncio (pytest-asyncio, por exemplo), a mesma lógica de polling funciona com async_playwright e aiohttp no lugar de requests, sem bloquear o event loop enquanto o token não fica pronto:

from playwright.async_api import async_playwright
import aiohttp
import asyncio

async def solve_recaptcha_async(site_key, page_url):
    async with aiohttp.ClientSession() as session:
        params = {
            "key": API_KEY, "method": "userrecaptcha",
            "googlekey": site_key, "pageurl": page_url
        }
        async with session.get("https://ocr.captchaai.com/in.php", params=params) as resp:
            text = await resp.text()
            task_id = text.split("|")[1]

        for _ in range(60):
            await asyncio.sleep(5)
            params = {"key": API_KEY, "action": "get", "id": task_id}
            async with session.get("https://ocr.captchaai.com/res.php", params=params) as resp:
                text = await resp.text()
                if text == "CAPCHA_NOT_READY": continue
                if text.startswith("OK|"): return text.split("|")[1]
                raise Exception(text)
        raise TimeoutError()

async def main():
    async with async_playwright() as p:
        browser = await p.chromium.launch(headless=True)
        page = await browser.new_page()
        await page.goto("https://example.com/form")

        site_key = await page.get_attribute(".g-recaptcha", "data-sitekey")
        token = await solve_recaptcha_async(site_key, page.url)

        await page.evaluate(f"document.getElementById('g-recaptcha-response').innerHTML = '{token}'")
        await page.click('button[type="submit"]')
        await browser.close()

asyncio.run(main())

Node.js: Playwright + CaptchaAI

A mesma sequência de três passos — enviar a sitekey, consultar o resultado, injetar o token — implementada com axios e a API chromium do Playwright, para quem mantém a suíte de testes em JavaScript ou TypeScript:

const { chromium } = require("playwright");
const axios = require("axios");

const API_KEY = "YOUR_API_KEY";

async function solveRecaptcha(siteKey, pageUrl) {
  const submit = await axios.get("https://ocr.captchaai.com/in.php", {
    params: {
      key: API_KEY,
      method: "userrecaptcha",
      googlekey: siteKey,
      pageurl: pageUrl,
    },
  });
  const taskId = submit.data.split("|")[1];

  while (true) {
    await new Promise((r) => setTimeout(r, 5000));
    const result = await axios.get("https://ocr.captchaai.com/res.php", {
      params: { key: API_KEY, action: "get", id: taskId },
    });
    if (result.data === "CAPCHA_NOT_READY") continue;
    if (result.data.startsWith("OK|")) return result.data.split("|")[1];
    throw new Error(result.data);
  }
}

(async () => {
  const browser = await chromium.launch({ headless: true });
  const page = await browser.newPage();
  await page.goto("https://staging.example.com/qa-login");

  // Fill form
  await page.fill("#username", "user@example.com");
  await page.fill("#password", "password123");

  // Solve CAPTCHA
  const siteKey = await page.getAttribute(".g-recaptcha", "data-sitekey");
  if (siteKey) {
    const token = await solveRecaptcha(siteKey, page.url());
    await page.evaluate(
      (t) => (document.getElementById("g-recaptcha-response").innerHTML = t),
      token
    );
  }

  // Submit
  await page.click('button[type="submit"]');
  await page.waitForLoadState("networkidle");

  console.log("Logged in:", page.url());
  await browser.close();
})();

Como resolver o Cloudflare Turnstile no Playwright

A detecção segue o mesmo padrão do reCAPTCHA, trocando apenas o seletor CSS e o parâmetro method enviado à CaptchaAI — o restante do fluxo de polling e injeção do token é idêntico ao exemplo anterior:

# Detect Turnstile
turnstile = page.query_selector(".cf-turnstile")
if turnstile:
    site_key = turnstile.get_attribute("data-sitekey")

    resp = requests.get("https://ocr.captchaai.com/in.php", params={
        "key": API_KEY, "method": "turnstile",
        "sitekey": site_key, "pageurl": page.url
    })
    task_id = resp.text.split("|")[1]

    # Poll and inject...

Solução de problemas comuns

Problema Correção
page.query_selector retorna None O CAPTCHA carrega de forma dinâmica; use page.wait_for_selector() antes de ler o atributo
O envio do token ao endpoint de verificação falha Confirme se o textarea de resposta tem o ID esperado pelo formulário — sites customizados às vezes renomeiam g-recaptcha-response
O Playwright trava dentro do Docker Instale as dependências de navegador com playwright install-deps antes de rodar o container
O CAPTCHA reaparece depois de resolvido O site pode exigir a execução de um callback JS; dispare-o manualmente via page.evaluate()
O polling nunca retorna OK\| Verifique o saldo de threads do plano CaptchaAI e o tempo de expiração do token na página de destino

Testando em staging sem travar o pipeline de QA

Ambientes de teste costumam reexibir o mesmo CAPTCHA a cada execução, então trate a chamada à CaptchaAI como parte do pipeline de QA, não como uma exceção isolada tratada caso a caso.

Use sempre domínios de staging (staging.example.com) e dados fictícios nos testes — nunca aponte o script de automação para o site de produção de terceiros.

  • Se os workers de CI rodam em uma região como sa-east-1 (São Paulo), meça a latência de round-trip até in.php/res.php: o tempo de rede soma ao tempo de resolução do desafio e pode explicar timeouts que parecem falha da CaptchaAI.
  • Ao logar dados de formulário de teste (e-mail, CPF, endereço), considere as obrigações da LGPD para retenção e descarte desses registros — mesmo em ambiente de QA os dados fictícios devem seguir uma política de expiração.
  • Trate a falha de token como qualquer outro erro de asserção: capture a Exception do polling e reporte no relatório do teste, em vez de deixar o Playwright estourar um timeout genérico sem contexto.

Playwright, Selenium ou Puppeteer: qual usar para automação com CAPTCHA?

A tabela abaixo compara os três frameworks mais usados em automação de navegador — a integração com a CaptchaAI é idêntica nos três casos: extrair a sitekey, resolver via API, injetar o token.

Recurso Playwright Selenium Puppeteer
Linguagens Python, Node.js, C#, Java Python, Java, C#, Ruby, JS Node.js
Navegadores Chromium, Firefox, WebKit Chrome, Firefox, Edge, Safari Chromium
Espera automática ✅ Nativa ❌ Esperas manuais ⚠️ Parcial
Interceptação de rede ⚠️ Limitada
Integração com a CaptchaAI ✅ Mesma API ✅ Mesma API ✅ Mesma API

Na prática, o auto-wait nativo do Playwright reduz a flakiness de testes que dependem de um widget de CAPTCHA carregar de forma assíncrona — mas isso só resolve a parte da interação com a página, não a parte da resolução do desafio em si.

Perguntas frequentes

A espera automática do Playwright resolve CAPTCHAs sozinha?

Não. O auto-wait garante que os elementos estejam visíveis e prontos antes da interação, mas ele não resolve o desafio. Para isso você precisa enviar a sitekey à API da CaptchaAI e injetar o token retornado antes do envio do formulário.

O CaptchaAI resolve hCaptcha no Playwright?

Não. O hCaptcha não é suportado atualmente pela CaptchaAI, independentemente do framework de automação usado. A API cobre reCAPTCHA v2/v3, Cloudflare Turnstile e Challenge, GeeTest v3 e CAPTCHAs de imagem — consulte a documentação para a lista completa de tipos suportados.

Preciso reabrir o navegador a cada CAPTCHA resolvido?

Não. Uma única instância de browser ou context do Playwright pode resolver vários desafios em sequência ao longo do teste; o token só precisa ser injetado antes de cada envio de formulário, não a cada lançamento do navegador.

O CaptchaAI funciona com Playwright rodando em Docker ou CI/CD?

Sim. A chamada à API é uma requisição HTTP comum e não depende do ambiente do navegador. Rode playwright install-deps na imagem para evitar falha de dependência e trate o CAPTCHA como qualquer outra chamada de rede dentro do pipeline.

Quanto custa resolver CAPTCHA nos testes automatizados?

Os planos da CaptchaAI são cobrados por thread simultânea, com resolução ilimitada dentro de cada thread — o plano BASIC (US$ 15/mês, 5 threads) já cobre a maioria das suítes de QA de times pequenos. Para execução mais paralela em pipelines de CI maiores, os planos ADVANCE (US$ 90/mês, 50 threads) ou PREMIUM (US$ 170/mês, 100 threads) escalam sem taxa adicional por solução.

Guias relacionados

Os comentários estão desativados para este artigo.