Tutorials

Extração e solução de parâmetros do controle deslizante GeeTest v3

Todo desafio de controle deslizante do GeeTest v3 trava por falta dos mesmos dois valores: gt (identificador fixo do site) e challenge (token de sessão que muda a cada carregamento). Sem os dois, nenhuma API — CaptchaAI incluída — devolve uma solução válida.

Resposta direta: capture os dois parâmetros por um destes caminhos e envie para a CaptchaAI resolver:

  • interceptando a chamada ao endpoint de registro (Método 1);
  • lendo o HTML/JS já carregado na página (Método 2);
  • com um hook em initGeetest() (Método 3).

Depois, envie gt e challenge ao endpoint in.php da CaptchaAI com method=geetest.


O que são gt e challenge no GeeTest v3

Parâmetro Descrição Estático/Dinâmico Exemplo
gt Identificador do site (hexadecimal de 32 caracteres) Estático por site 019924a82c70bb123aae90d483b6a0ed
challenge Token de sessão (hexadecimal de 32 caracteres) Carregamento dinâmico por página a3f5c8d2e1b04a6789012345678abcdf
api_server Subdomínio da API GeeTest (opcional) Estático por site api-na.geetest.com

Três regras resolvem a maior parte dos erros de parâmetro:

  • gt não muda entre requisições — capture uma vez e reutilize;
  • challenge expira a cada novo carregamento do desafio; enviar um valor antigo é a causa mais comum de solução rejeitada;
  • o GeeTest v4 abandona esse par em favor de um captcha_id único e não usa gt/challenge — parâmetros diferentes dos que este guia cobre.

Em um time de QA testando um fluxo de cadastro com controle deslizante GeeTest v3 a partir do Brasil, o padrão prático é extrair challenge no mesmo passo que dispara a submissão — nunca em uma etapa anterior do script — e manter os dados capturados (payloads de rede, tokens) só no ambiente interno de staging, alinhado com o que a LGPD já exige para qualquer coleta feita durante testes automatizados.


Método 1: capturar a chamada ao endpoint de registro

Na maioria das implementações, o GeeTest v3 busca o desafio em um endpoint de registro assim que a página carrega.

A resposta desse endpoint chega neste formato — é dele que você lê gt e challenge:

{
  "success": 1,
  "challenge": "a3f5c8d2e1b04a6789012345678abcdf",
  "gt": "019924a82c70bb123aae90d483b6a0ed",
  "new_captcha": true
}

Em Python, com a lib requests

import requests
import re

session = requests.Session()
html = session.get("https://staging.example.com/qa-login").text

# Find the register endpoint
register_url = re.search(
    r'(https?://[^"\']+(?:register|captcha|geetest)[^"\']*)',
    html
)

if register_url:
    resp = session.get(register_url.group(1)).json()
    gt = resp["gt"]
    challenge = resp["challenge"]
    print(f"gt: {gt}")
    print(f"challenge: {challenge}")

Em JavaScript, interceptando a rede no Puppeteer

const puppeteer = require('puppeteer');

const browser = await puppeteer.launch();
const page = await browser.newPage();

let geetestParams = {};

page.on('response', async (response) => {
  const url = response.url();
  if (url.includes('register') || url.includes('captcha')) {
    try {
      const json = await response.json();
      if (json.gt && json.challenge) {
        geetestParams = {
          gt: json.gt,
          challenge: json.challenge,
        };
        console.log('Captured GeeTest params:', geetestParams);
      }
    } catch (e) {}
  }
});

await page.goto('https://staging.example.com/qa-login', { waitUntil: 'networkidle2' });
console.log('Final params:', geetestParams);

Método 2: extrair direto da fonte da página

Quando não há uma chamada de registro separada, o site costuma embutir gt e challenge em um destes dois lugares:

  • atributos de dados no HTML (data-gt, data-challenge);
  • variáveis JavaScript inline na própria página.
import re

# From data attributes
gt_match = re.search(r'data-gt=["\']([a-f0-9]{32})', html)
challenge_match = re.search(r'data-challenge=["\']([a-f0-9]{32})', html)

# From JavaScript variables
if not gt_match:
    gt_match = re.search(r'gt\s*[=:]\s*["\']([a-f0-9]{32})', html)
if not challenge_match:
    challenge_match = re.search(r'challenge\s*[=:]\s*["\']([a-f0-9]{32})', html)

gt = gt_match.group(1) if gt_match else None
challenge = challenge_match.group(1) if challenge_match else None
print(f"gt={gt}, challenge={challenge}")

Método 3: interceptar o initGeetest

O GeeTest v3 sempre inicializa por uma chamada a initGeetest(). Se os dois métodos acima não capturarem nada, injete um hook nessa função antes que os scripts da própria página rodem:

// Puppeteer: inject before page scripts
await page.evaluateOnNewDocument(() => {
  window.__geetestConfig = null;
  const origInit = window.initGeetest;

  Object.defineProperty(window, 'initGeetest', {
    set(fn) {
      this._initGeetest = function(config, callback) {
        window.__geetestConfig = config;
        console.log('GeeTest config:', JSON.stringify(config));
        return fn(config, callback);
      };
    },
    get() { return this._initGeetest; }
  });
});

await page.goto('https://staging.example.com/qa-login', { waitUntil: 'networkidle2' });

const config = await page.evaluate(() => window.__geetestConfig);
// config = { gt: "019924a...", challenge: "a3f5c8d...", product: "bind", ... }

Ordem prática para decidir qual método tentar primeiro:

  1. Método 1 — cobre a maioria dos sites e não depende de manter um navegador aberto.
  2. Método 2 — mais rápido quando os valores já vêm embutidos no HTML.
  3. Método 3 — último recurso, reservado para sites que só expõem gt/challenge dentro do initGeetest().

Como enviar gt e challenge para a CaptchaAI

Com os dois parâmetros em mãos, o envio segue o padrão de qualquer tarefa CaptchaAI.

POST para in.php envia a tarefa; polling em res.php a cada poucos segundos devolve a solução assim que o status muda.

Python

import requests
import time

API_KEY = "YOUR_API_KEY"

# Submit
resp = requests.post("https://ocr.captchaai.com/in.php", data={
    "key": API_KEY,
    "method": "geetest",
    "gt": gt,
    "challenge": challenge,
    "pageurl": "https://staging.example.com/qa-login",
    "json": "1",
}).json()

if resp["status"] != 1:
    raise Exception(f"Submit error: {resp['request']}")

task_id = resp["request"]

# Poll
for _ in range(30):
    time.sleep(5)
    result = requests.get("https://ocr.captchaai.com/res.php", params={
        "key": API_KEY, "action": "get", "id": task_id, "json": "1"
    }).json()

    if result["status"] == 1:
        solution = result["request"]
        print(f"Solution: {solution}")
        break
    if result["request"] != "CAPCHA_NOT_READY":
        raise Exception(f"Error: {result['request']}")

JavaScript

const submit = await axios.post('https://ocr.captchaai.com/in.php', null, {
  params: {
    key: 'YOUR_API_KEY',
    method: 'geetest',
    gt: geetestParams.gt,
    challenge: geetestParams.challenge,
    pageurl: 'https://staging.example.com/qa-login',
    json: 1,
  }
});
const taskId = submit.data.request;

let solution = null;
for (let i = 0; i < 30; i++) {
  await new Promise(r => setTimeout(r, 5000));
  const poll = await axios.get('https://ocr.captchaai.com/res.php', {
    params: { key: 'YOUR_API_KEY', action: 'get', id: taskId, json: 1 }
  });
  if (poll.data.status === 1) {
    solution = poll.data.request;
    break;
  }
}
console.log('Solution:', solution);

Injetando a solução no formulário

O retorno da CaptchaAI para GeeTest v3 traz três campos que precisam ir para inputs ocultos do formulário antes do envio:

  • challenge;
  • validate;
  • seccode.
# solution is a pipe-delimited string or JSON object
# Parse and inject into the form
import json

sol = json.loads(solution) if isinstance(solution, str) else solution

driver.execute_script("""
    const form = document.querySelector('form');
    function addHidden(name, value) {
        let input = form.querySelector(`input[name="${name}"]`);
        if (!input) {
            input = document.createElement('input');
            input.type = 'hidden';
            input.name = name;
            form.appendChild(input);
        }
        input.value = value;
    }
    addHidden('geetest_challenge', arguments[0]);
    addHidden('geetest_validate', arguments[1]);
    addHidden('geetest_seccode', arguments[2]);
""", sol["challenge"], sol["validate"], sol["seccode"])

Perguntas frequentes

Por quanto tempo o challenge do GeeTest v3 fica válido?

Normalmente entre 60 e 120 segundos. Extraia e envie para a CaptchaAI na sequência — não guarde o valor para reaproveitar depois.

Qual dos três métodos de extração devo usar primeiro?

Comece pelo Método 1 (endpoint de registro): é o mais confiável porque devolve gt e challenge já formatados em JSON. Só recorra aos Métodos 2 e 3 se o site não expuser essa chamada.

Preciso de um navegador headless para capturar os parâmetros?

Só se o site carregar gt/challenge via JavaScript depois do carregamento inicial da página (Métodos 1 e 3). Quando os valores já vêm no HTML (Método 2), uma requisição simples com requests ou axios resolve, sem abrir navegador.

O GeeTest v4 já está disponível na CaptchaAI?

Ainda não. O suporte a GeeTest v4 está "em breve" — hoje a CaptchaAI resolve GeeTest v3, que é o que este guia cobre.

Por que a solução volta rejeitada mesmo com gt e challenge corretos?

Quase sempre é timing: o challenge expirou entre a extração e o envio, ou entre receber a solução da CaptchaAI e injetá-la no formulário. Reduza esse intervalo e evite qualquer pausa manual no meio do fluxo.


Problemas comuns e como corrigir

A maior parte das falhas de resolução do GeeTest v3 cai em uma destas quatro causas:

Problema Causa Correção
ERROR_BAD_PARAMETERS Faltando gt ou challenge Ambos são obrigatórios; extraia os dois valores novamente
Desafio obsoleto O token de challenge expirou Extraia um novo desafio imediatamente antes de enviar
Valor gt errado Copiado de outro site gt é específico por site; extraia de novo a partir do alvo
Solução rejeitada pelo site O desafio expirou durante a resolução Reduza o intervalo entre a extração e o envio

Se o erro persistir mesmo com um challenge recém-extraído, confirme que o pageurl enviado à CaptchaAI é exatamente a URL onde o controle deslizante foi carregado — um pageurl divergente derruba a solução mesmo com os parâmetros certos.


Resolva CAPTCHAs GeeTest v3 com a CaptchaAI

Abra uma conta e pegue sua chave de API em captchaai.com.

O plano BASIC (US$ 15/mês, 5 threads) já é suficiente para testar o fluxo completo antes de escalar para volume.


Guias relacionados

  1. Como detectar CAPTCHA pelo console do navegador
  2. Como extrair parâmetros do reCAPTCHA
  3. Como extrair a sitekey do Cloudflare Turnstile
Os comentários estão desativados para este artigo.