Tutorials

Injection de jetons GeeTest dans les cadres d'automatisation du navigateur

Vous avez résolu un GeeTest v3, mais le formulaire refuse toujours la soumission ? Dans la grande majorité des cas, le problème n'est pas la résolution : c'est l'endroit où vous déposez les valeurs. GeeTest v3 renvoie trois éléments — geetest_challenge, geetest_validate et geetest_seccode — et chacun doit atterrir exactement là où le site l'attend. Ce guide montre l'injection correcte dans Playwright, Puppeteer et Selenium, avec la résolution côté API CaptchaAI.

Où déposer les valeurs : trois cibles possibles

Avant d'écrire une ligne de code, identifiez comment la page consomme le résultat. Il n'existe que trois cibles, et un site n'en utilise généralement qu'une :

  • Champs de formulaire cachés — le cas le plus fréquent : trois input[type=hidden] que vous remplissez avant de soumettre.
  • Callback JavaScript — le widget lit la solution via getValidate() ; il faut alors surcharger la valeur de retour et émettre l'événement de réussite.
  • Requête XHR — le résultat part dans le corps d'un appel asynchrone que vous interceptez pour y injecter le trio.

Repérez la cible en inspectant la soumission dans l'onglet réseau, puis appliquez la méthode correspondante ci-dessous.

Les trois valeurs renvoyées par GeeTest v3

Après résolution, CaptchaAI renvoie un résultat en trois parties :

{
  "geetest_challenge": "a1b2c3d4e5...modified_challenge",
  "geetest_validate": "abc123def456_validate",
  "geetest_seccode": "abc123def456_validate|jordan"
}

Chaque valeur joue un rôle distinct, et l'absence d'une seule fait échouer la vérification :

  • geetest_challenge — identifie la session de défi en cours.
  • geetest_validate — prouve que le défi a bien été résolu.
  • geetest_seccode — porte un hachage anti-falsification lié à la session.

Étape 1 : extraire gt et challenge, puis résoudre

Le point de départ est le même partout : récupérez gt et challenge sur la page, puis envoyez-les à CaptchaAI et interrogez le résultat jusqu'à obtenir les trois valeurs.

import requests
import time

def solve_geetest(gt, challenge, page_url):
    resp = requests.post("https://ocr.captchaai.com/in.php", data={
        "key": "YOUR_API_KEY",
        "method": "geetest",
        "gt": gt,
        "challenge": challenge,
        "pageurl": page_url,
        "json": 1
    })
    task_id = resp.json()["request"]

    for _ in range(60):
        time.sleep(3)
        result = requests.get("https://ocr.captchaai.com/res.php", params={
            "key": "YOUR_API_KEY",
            "action": "get",
            "id": task_id,
            "json": 1
        })
        data = result.json()
        if data["status"] == 1:
            return data["request"]  # Returns dict with three values
    raise TimeoutError("GeeTest solve timed out")

À noter pour les équipes francophones (France, Belgique, Suisse, Maghreb) : ces trois valeurs sont des secrets de session. Dans une logique RGPD de minimisation, ne les écrivez pas en clair dans vos logs applicatifs et purgez-les dès la soumission validée.

Étape 2 : injecter les valeurs dans Playwright (Python)

Capturer gt et challenge

GeeTest publie ses paramètres dans la réponse register. Interceptez-la au vol plutôt que de fouiller le DOM :

from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch(headless=False)
    page = browser.new_page()

    # Capture GeeTest registration response
    geetest_data = {}
    def capture_geetest(response):
        if "register" in response.url and response.status == 200:
            try:
                data = response.json()
                if "gt" in data and "challenge" in data:
                    geetest_data.update(data)
            except Exception:
                pass

    page.on("response", capture_geetest)
    page.goto("https://example.com/login")
    page.wait_for_selector(".geetest_holder")

    gt = geetest_data["gt"]
    challenge = geetest_data["challenge"]

Méthode 1 : renseigner les champs de formulaire cachés

L'approche la plus courante : créez (ou remplissez) les trois input cachés attendus par le formulaire, puis soumettez.

# Solve with CaptchaAI
solution = solve_geetest(gt, challenge, page.url)

# Method 1: Set hidden form fields
page.evaluate(f"""
  const fields = {{
    'geetest_challenge': '{solution["geetest_challenge"]}',
    'geetest_validate': '{solution["geetest_validate"]}',
    'geetest_seccode': '{solution["geetest_seccode"]}'
  }};
  for (const [name, value] of Object.entries(fields)) {{
    let input = document.querySelector(`input[name="${{name}}"]`);
    if (!input) {{
      input = document.createElement('input');
      input.type = 'hidden';
      input.name = name;
      document.querySelector('form').appendChild(input);
    }}
    input.value = value;
  }}
""")

# Submit the form
page.click("#submit-button")

Méthode 2 : déclencher le callback GeeTest

Certains sites n'utilisent pas de champs cachés : ils lisent la solution via le callback JavaScript du widget. Dans ce cas, forcez la valeur de retour et émettez l'événement de réussite.

page.evaluate(f"""
  // Find the GeeTest captcha object
  if (window.captchaObj) {{
    // Simulate a successful solve
    const result = {{
      geetest_challenge: '{solution["geetest_challenge"]}',
      geetest_validate: '{solution["geetest_validate"]}',
      geetest_seccode: '{solution["geetest_seccode"]}'
    }};

    // Override getValidate to return our solution
    window.captchaObj.getValidate = function() {{ return result; }};

    // Trigger the success callback
    const successEvent = new Event('geetest_success');
    document.dispatchEvent(successEvent);
  }}
""")

Étape 3 : injecter les valeurs dans Puppeteer (JavaScript)

La logique est identique côté Node.js : on capture les paramètres via l'événement response, on résout, puis on écrit les trois input cachés avant de cliquer.

const puppeteer = require('puppeteer');

async function solveAndInject() {
  const browser = await puppeteer.launch({ headless: false });
  const page = await browser.newPage();

  // Capture GeeTest params
  let gt, challenge;
  page.on('response', async (response) => {
    if (response.url().includes('register') && response.status() === 200) {
      try {
        const data = await response.json();
        if (data.gt && data.challenge) {
          gt = data.gt;
          challenge = data.challenge;
        }
      } catch (e) {}
    }
  });

  await page.goto('https://example.com/login');
  await page.waitForSelector('.geetest_holder');

  // Solve with CaptchaAI (implementation from earlier)
  const solution = await solveCaptcha(gt, challenge, page.url());

  // Inject the three values
  await page.evaluate((sol) => {
    // Set hidden inputs
    const form = document.querySelector('form');
    ['geetest_challenge', 'geetest_validate', 'geetest_seccode'].forEach(name => {
      let input = document.querySelector(`input[name="${name}"]`);
      if (!input) {
        input = document.createElement('input');
        input.type = 'hidden';
        input.name = name;
        form.appendChild(input);
      }
      input.value = sol[name];
    });
  }, solution);

  await page.click('#submit-button');
}

Étape 4 : injecter les valeurs dans Selenium (Python)

Selenium n'expose pas l'interception réseau aussi simplement : lisez gt et challenge depuis les attributs de données du widget, puis injectez le trio avec execute_script.

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

driver = webdriver.Chrome()
driver.get("https://example.com/login")

# Wait for GeeTest widget
WebDriverWait(driver, 15).until(
    EC.presence_of_element_located((By.CLASS_NAME, "geetest_holder"))
)

# Extract gt and challenge from the page
gt = driver.execute_script(
    "return document.querySelector('[data-gt]')?.dataset.gt"
)
challenge = driver.execute_script(
    "return document.querySelector('[data-challenge]')?.dataset.challenge"
)

# Solve with CaptchaAI
solution = solve_geetest(gt, challenge, driver.current_url)

# Inject via JavaScript
driver.execute_script(f"""
    var fields = {{
        'geetest_challenge': '{solution["geetest_challenge"]}',
        'geetest_validate': '{solution["geetest_validate"]}',
        'geetest_seccode': '{solution["geetest_seccode"]}'
    }};
    var form = document.querySelector('form');
    for (var name in fields) {{
        var input = document.querySelector('input[name="' + name + '"]');
        if (!input) {{
            input = document.createElement('input');
            input.type = 'hidden';
            input.name = name;
            form.appendChild(input);
        }}
        input.value = fields[name];
    }}
""")

driver.find_element(By.ID, "submit-button").click()

Soumissions envoyées via XHR

Intercepter et compléter la requête

Tous les sites ne postent pas un formulaire : beaucoup envoient le résultat GeeTest dans le corps d'une requête XHR. Interceptez la route et ajoutez les trois valeurs à la charge utile avant de la laisser passer.

# Playwright: Intercept the XHR and inject values
def handle_route(route):
    if "login" in route.request.url and route.request.method == "POST":
        # Modify the POST data to include our solution
        post_data = route.request.post_data
        # Add GeeTest values to the request
        route.continue_(post_data=modified_data)
    else:
        route.continue_()

page.route("**/api/login**", handle_route)

Dépannage

Problème Cause Correctif
« La validation du GeeTest a échoué » Une des trois valeurs manque Vérifiez que challenge, validate et seccode sont bien injectés
Challenge expiré avant l'injection Trop de délai entre l'extraction et la résolution Extrayez et résolvez dans la foulée, sans étape intermédiaire
Le formulaire part mais sans les valeurs Noms de champs ou sélecteur de formulaire erronés Inspectez le vrai formulaire pour relever les noms exacts
Le callback ne se déclenche pas Le site emploie un nom de callback personnalisé Repérez-le dans les options initGeetest
Valeurs injectées mais le widget reste affiché L'état visuel du widget n'a pas été rafraîchi Déclenchez le callback de réussite par programme

FAQ

CaptchaAI prend-il en charge GeeTest v4 ?

Non — pas encore. Seul GeeTest v3 est pris en charge aujourd'hui via la méthode geetest. La prise en charge de GeeTest v4 est annoncée comme à venir : ne construisez pas votre flux dessus pour l'instant.

Pourquoi le challenge expire-t-il avant que j'aie pu l'injecter ?

Parce que challenge a une durée de vie courte. Si trop de temps s'écoule entre la capture des paramètres et l'injection des valeurs, GeeTest invalide la session. Enchaînez extraction, résolution et injection sans pause, et évitez d'ouvrir un point d'arrêt au milieu du parcours.

Quel plan CaptchaAI prévoir pour résoudre du GeeTest à grande échelle ?

La facturation est basée sur les threads (résolutions simultanées), sans coût par résolution. Le plan BASIC ($15/mois, 5 threads) suffit à un script d'automatisation isolé ; passez à STANDARD ($30/mois, 15 threads) ou ADVANCE ($90/mois, 50 threads) quand plusieurs workers résolvent en parallèle.

Puis-je réutiliser les mêmes valeurs GeeTest d'une soumission à l'autre ?

Non. geetest_seccode inclut un hachage lié à la session ; rejouer d'anciennes valeurs échoue à la vérification côté serveur. Résolvez à la demande, injectez, soumettez, puis repartez d'un nouveau challenge à la requête suivante.

Articles connexes

Prochaines étapes

Vous savez maintenant poser les trois valeurs dans n'importe quel framework. Récupérez votre clé API CaptchaAI et lancez votre première résolution GeeTest v3.

Les commentaires sont désactivés pour cet article.