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
- Résoudre GeeTest v3 avec Node.js
- Résolution de GeeTest v3 en Node.js avec CaptchaAI
- Résoudre les CAPTCHA en automatisation de navigateur mobile
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.