Use Cases

Resolvendo CAPTCHAs em sites chineses com CaptchaAI

Resolver o CAPTCHA de um site chinês depende de dois ajustes: enviar a imagem com language=2, para que o OCR leia ideogramas em vez de letras latinas, e tratar o GeeTest v3 como um desafio de parâmetros (gt e challenge), não como um arrastar de mouse. O resto é igual a qualquer integração com a API da CaptchaAI: envie a tarefa para in.php, consulte res.php e aplique o valor no formulário.

Na prática, o que quebra quase nunca é a resolução em si: é o encoding errado, o cookie de sessão que não acompanha a imagem baixada e o TTL curto do challenge.

Tipos de CAPTCHA que aparecem em sites chineses

O método da API depende do widget, e a distribuição aqui é bem diferente da de portais brasileiros:

Tipo de CAPTCHA Onde costuma aparecer Método na CaptchaAI
Imagem com caracteres chineses Portais públicos, bases acadêmicas Imagem/OCR com language=2
Operação aritmética em mandarim Formulários de cadastro Imagem/OCR
Quebra-cabeça deslizante GeeTest v3 Baidu, Bilibili e outras plataformas grandes GeeTest v3
Clique nos caracteres em ordem Instrução em chinês sobre a imagem Imagem/OCR (coordenadas)
reCAPTCHA v2 Versões internacionais de sites chineses reCAPTCHA v2

Escopo, para evitar retrabalho: a CaptchaAI cobre o GeeTest v3; o v4 aparece apenas como "em breve", e hCaptcha e FunCaptcha (Arkose Labs) não estão entre os tipos atendidos.

Como identificar o desafio antes de escrever código

Cinco minutos no inspetor do navegador economizam horas de integração errada:

  • Imagem simples: uma tag <img> que muda a cada carregamento, com um campo de texto ao lado.
  • GeeTest v3: um script do fornecedor e uma requisição inicial devolvem gt (fixo por site) e challenge (novo a cada sessão).
  • Clique nos caracteres: a instrução em chinês pede uma ordem; o retorno vem como coordenadas.
  • reCAPTCHA v2: o widget é o mesmo do resto do mundo e preenche g-recaptcha-response.

Python: OCR de caracteres chineses e GeeTest v3

As imagens costumam trazer instruções como 请输入验证码 ("digite o código de verificação"), e é o language=2 que faz o reconhecimento funcionar. O exemplo tem três funções: resolver a imagem de um arquivo local, baixá-la dentro da mesma sessão HTTP — o que preserva o cookie — e enviar um desafio GeeTest v3.

import requests
import base64
import time

API_KEY = "YOUR_API_KEY"
SUBMIT_URL = "https://ocr.captchaai.com/in.php"
RESULT_URL = "https://ocr.captchaai.com/res.php"


def solve_chinese_image_captcha(image_path: str) -> str:
    """Solve a Chinese character image CAPTCHA."""
    with open(image_path, "rb") as f:
        image_b64 = base64.b64encode(f.read()).decode()

    resp = requests.post(SUBMIT_URL, data={
        "key": API_KEY,
        "method": "base64",
        "body": image_b64,
        "language": 2,          # 2 = Chinese characters supported
        "json": 1,
    }, timeout=30).json()

    if resp.get("status") != 1:
        raise RuntimeError(f"Submit failed: {resp.get('request')}")

    task_id = resp["request"]
    start = time.monotonic()

    while time.monotonic() - start < 120:
        time.sleep(5)
        poll = requests.get(RESULT_URL, params={
            "key": API_KEY, "action": "get",
            "id": task_id, "json": 1,
        }, timeout=15).json()

        if poll.get("request") == "CAPCHA_NOT_READY":
            continue
        if poll.get("status") == 1:
            return poll["request"]
        raise RuntimeError(f"Solve failed: {poll.get('request')}")

    raise RuntimeError("Timeout")


def solve_chinese_captcha_from_url(captcha_url: str, cookies: dict = None) -> str:
    """Download and solve a Chinese CAPTCHA from a URL."""
    session = requests.Session()
    if cookies:
        session.cookies.update(cookies)

    resp = session.get(captcha_url, timeout=15)
    image_b64 = base64.b64encode(resp.content).decode()

    submit = requests.post(SUBMIT_URL, data={
        "key": API_KEY,
        "method": "base64",
        "body": image_b64,
        "language": 2,
        "json": 1,
    }, timeout=30).json()

    if submit.get("status") != 1:
        raise RuntimeError(f"Submit: {submit.get('request')}")

    task_id = submit["request"]
    for _ in range(24):
        time.sleep(5)
        poll = requests.get(RESULT_URL, params={
            "key": API_KEY, "action": "get", "id": task_id, "json": 1,
        }, timeout=15).json()

        if poll.get("request") == "CAPCHA_NOT_READY":
            continue
        if poll.get("status") == 1:
            return poll["request"]
        raise RuntimeError(f"Solve: {poll.get('request')}")

    raise RuntimeError("Timeout")


# --- GeeTest on Chinese platforms ---

def solve_geetest_chinese(gt: str, challenge: str, pageurl: str) -> dict:
    """Solve GeeTest v3 commonly found on Baidu, Bilibili, etc."""
    resp = requests.post(SUBMIT_URL, data={
        "key": API_KEY,
        "method": "geetest",
        "gt": gt,
        "challenge": challenge,
        "pageurl": pageurl,
        "json": 1,
    }, timeout=30).json()

    if resp.get("status") != 1:
        raise RuntimeError(f"Submit: {resp.get('request')}")

    task_id = resp["request"]
    for _ in range(36):
        time.sleep(5)
        poll = requests.get(RESULT_URL, params={
            "key": API_KEY, "action": "get", "id": task_id, "json": 1,
        }, timeout=15).json()

        if poll.get("request") == "CAPCHA_NOT_READY":
            continue
        if poll.get("status") == 1:
            # GeeTest returns challenge, validate, seccode
            return poll["request"]
        raise RuntimeError(f"Solve: {poll.get('request')}")

    raise RuntimeError("Timeout")


# Usage — Chinese government portal
text = solve_chinese_image_captcha("chinese_captcha.png")
print(f"Chinese CAPTCHA text: {text}")

# GeeTest on a Chinese platform
geetest_result = solve_geetest_chinese(
    gt="b46d1900d0a894591f1561f8c35670a7",
    challenge="dynamic_challenge_string",
    pageurl="https://www.example.cn/login",
)

A consulta ao res.php roda a cada 5 segundos, e a primeira já costuma trazer o resultado: menos de 0,5 s em imagens e menos de 12 s no GeeTest v3. A resposta do GeeTest vem com challenge, validate e seccode — os três seguem no envio do formulário.

O mesmo fluxo em Node.js

Se o coletor já roda em Node.js, a tradução é direta: URLSearchParams, fetch e o mesmo par in.php / res.php. Mantenha um teto de tentativas por tarefa — 24 para imagem, 36 para GeeTest.

const API_KEY = "YOUR_API_KEY";
const SUBMIT_URL = "https://ocr.captchaai.com/in.php";
const RESULT_URL = "https://ocr.captchaai.com/res.php";
const fs = require("fs");

async function solveChineseImageCaptcha(imagePath) {
  const imageB64 = fs.readFileSync(imagePath, "base64");

  const body = new URLSearchParams({
    key: API_KEY,
    method: "base64",
    body: imageB64,
    language: "2",
    json: "1",
  });

  const resp = await (await fetch(SUBMIT_URL, { method: "POST", body })).json();
  if (resp.status !== 1) throw new Error(`Submit: ${resp.request}`);

  const taskId = resp.request;
  for (let i = 0; i < 24; i++) {
    await new Promise((r) => setTimeout(r, 5000));
    const url = `${RESULT_URL}?key=${API_KEY}&action=get&id=${taskId}&json=1`;
    const poll = await (await fetch(url)).json();
    if (poll.request === "CAPCHA_NOT_READY") continue;
    if (poll.status === 1) return poll.request;
    throw new Error(`Solve: ${poll.request}`);
  }
  throw new Error("Timeout");
}

async function solveGeeTest(gt, challenge, pageurl) {
  const body = new URLSearchParams({
    key: API_KEY,
    method: "geetest",
    gt,
    challenge,
    pageurl,
    json: "1",
  });

  const resp = await (await fetch(SUBMIT_URL, { method: "POST", body })).json();
  if (resp.status !== 1) throw new Error(`Submit: ${resp.request}`);

  const taskId = resp.request;
  for (let i = 0; i < 36; i++) {
    await new Promise((r) => setTimeout(r, 5000));
    const url = `${RESULT_URL}?key=${API_KEY}&action=get&id=${taskId}&json=1`;
    const poll = await (await fetch(url)).json();
    if (poll.request === "CAPCHA_NOT_READY") continue;
    if (poll.status === 1) return poll.request;
    throw new Error(`Solve: ${poll.request}`);
  }
  throw new Error("Timeout");
}

// Usage
const text = await solveChineseImageCaptcha("chinese_captcha.png");
console.log(`Chinese text: ${text}`);

Encoding, sessão e rede: onde a integração falha

Situação O que fazer
Caracteres voltam ilegíveis Force UTF-8 na requisição e na leitura da resposta
O site exige cookie de sessão Baixe a imagem na mesma sessão que envia o formulário
Parâmetros do GeeTest no JavaScript Extraia gt e challenge da página ou do endpoint que os gera
Limite de requisições nas CDNs da região Reduza a cadência e use backoff exponencial
A imagem muda a cada carregamento Baixe uma vez, resolva e envie; recarregar invalida o desafio

Uma equipe de e-commerce em Curitiba confere toda semana fichas técnicas de fornecedores em portais aos quais já tem acesso contratado: cerca de 400 páginas por rodada, com CAPTCHA de caracteres chineses na primeira visita da sessão. Do Brasil, o RTT até a Ásia custa algumas centenas de milissegundos por requisição, e rodar os workers em sa-east-1 não encurta essa distância: o ganho vem de paralelizar. Com o plano BASIC (US$ 15/mês, 5 threads), cinco desafios seguem em paralelo e a rodada termina em bem menos de uma hora; a cobrança é por thread concorrente, com resoluções ilimitadas no mês. Se o catálogo dobrar, o passo natural é o STANDARD (US$ 30/mês, 15 threads).

Se a coleta envolver dados pessoais, as obrigações da LGPD — ou do RGPD, em Portugal — valem mesmo para dados vindos de fora do país: colete só o que o contrato autoriza.

Solução de problemas

Sintoma Causa provável Correção
Texto resolvido com caracteres trocados Encoding incompatível Decodifique a resposta como UTF-8 de ponta a ponta
challenge do GeeTest recusado O valor tem TTL curto Envie logo após o carregamento; nunca reaproveite
O OCR devolve letras latinas Mistura de escrita chinesa e latina Envie language=2 para forçar o reconhecimento de ideogramas
O formulário rejeita a resposta correta Cookies não mantidos Use a mesma sessão para baixar a imagem e enviar
Taxa de resolução baixa em portais públicos Ideogramas em traços densos Capture em resolução maior

Perguntas frequentes

O GeeTest v4 já está disponível?

Não. O v4 é descrito como "em breve" e ainda não pode ser resolvido pela API; em produção está o GeeTest v3, pelo método geetest. hCaptcha e FunCaptcha (Arkose Labs) também não são atendidos.

Preciso de um navegador headless para resolver esses desafios?

Não para o CAPTCHA em si: o envio e a consulta são requisições HTTP comuns. O navegador só entra se a página exigir JavaScript para gerar o challenge — e muitos sites expõem esse par em um endpoint que você pode chamar direto.

O OCR reconhece chinês simplificado e tradicional?

Sim. O solucionador de imagem trata as duas escritas (简体 e 繁體) com o mesmo language=2. A biblioteca cobre mais de 27.500 variantes de CAPTCHA de imagem, sem cobrança extra por tipo.

Como sei se o widget deslizante é mesmo GeeTest v3?

Procure o par de parâmetros: o GeeTest v3 expõe um gt fixo por site e um challenge que muda a cada sessão. Sem esse par, é provável que seja um controle proprietário, e o método geetest não se aplica.

Quanto custa manter uma coleta com CAPTCHAs chineses?

O custo acompanha a concorrência, não o número de desafios. Os planos são por thread, com resoluções ilimitadas no mês: BASIC (US$ 15/mês, 5 threads) atende rotinas pequenas; ADVANCE (US$ 90/mês, 50 threads) sustenta um pipeline contínuo.

Artigos relacionados

Próximas etapas

Pegue sua chave de API da CaptchaAI, rode o exemplo em Python contra uma imagem salva e só depois conecte o coletor.

Guias relacionados:

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