Un curseur GeeTest v3 se résout à partir de deux valeurs, et rien d'autre : gt, l'identifiant statique du site, et challenge, le jeton de session régénéré à chaque affichage. Toute la difficulté tient dans le challenge : il expire en quelques dizaines de secondes, capturez-le et envoyez-le à CaptchaAI sans attendre.
Ce tutoriel couvre trois méthodes d'extraction complémentaires, la résolution du curseur via l'API, puis l'injection de la solution dans le formulaire. Les exemples sont fournis en Python et en JavaScript, directement réutilisables sur vos propres pages.
Les deux paramètres à extraire : gt et challenge
| Paramètre | Rôle | Statique ou dynamique | Exemple |
|---|---|---|---|
gt |
Identifiant du site (32 caractères hexadécimaux) | Statique par site | 019924a82c70bb123aae90d483b6a0ed |
challenge |
Jeton de session (32 caractères hexadécimaux) | Régénéré à chaque chargement | a3f5c8d2e1b04a6789012345678abcdf |
api_server |
Sous-domaine API GeeTest (facultatif) | Statique par site | api-na.geetest.com |
Le gt ne bouge pas d'une requête à l'autre. Le challenge, lui, est régénéré à chaque affichage du CAPTCHA : extrayez-en un neuf pour chaque tentative de résolution. Trois points à garder en tête avant de coder :
- Le
gts'extrait une seule fois et se met en cache, car il ne change pas. - Le
challengese réextrait à chaque tentative, sans exception. - Le champ
api_serverest facultatif : ne l'ajoutez que si la cible s'appuie dessus.
Méthode 1 : intercepter l'appel d'enregistrement
La plupart des intégrations de GeeTest v3 récupèrent le défi depuis un endpoint d'enregistrement appelé au chargement de la page. C'est l'approche la plus fiable : vous lisez gt et challenge directement dans une réponse JSON, sans dépendre du balisage HTML. La réponse ressemble à ceci :
{
"success": 1,
"challenge": "a3f5c8d2e1b04a6789012345678abcdf",
"gt": "019924a82c70bb123aae90d483b6a0ed",
"new_captcha": true
}
Python (requests)
Une session requests suit les redirections et conserve les cookies, ce qui suffit pour la plupart des pages de connexion. Repérez l'URL d'enregistrement, appelez-la, puis lisez les deux champs :
import requests
import re
session = requests.Session()
html = session.get("https://example.com/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}")
JavaScript (interception réseau avec Puppeteer)
Quand l'appel d'enregistrement est déclenché par du JavaScript, un navigateur headless comme Puppeteer capte la réponse au vol. Écoutez l'événement response et filtrez les URL qui contiennent register ou captcha :
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://example.com/login', { waitUntil: 'networkidle2' });
console.log('Final params:', geetestParams);
Méthode 2 : lire les paramètres dans le code source
Certains sites exposent les paramètres directement dans le HTML ou dans des scripts en ligne. Une expression régulière suffit alors à les récupérer, sans requête supplémentaire ni navigateur :
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éthode 3 : intercepter le hook initGeetest
GeeTest v3 s'initialise via l'appel initGeetest(). En l'interceptant avant l'exécution des scripts de la page, vous récupérez la configuration complète, gt et challenge compris :
// 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://example.com/login', { waitUntil: 'networkidle2' });
const config = await page.evaluate(() => window.__geetestConfig);
// config = { gt: "019924a...", challenge: "a3f5c8d...", product: "bind", ... }
Quelle méthode d'extraction choisir ?
Les trois approches visent le même résultat, mais ne demandent pas le même effort.
- Commencez par la méthode 1 : stable, elle ne réclame qu'une requête HTTP.
- Basculez sur la méthode 2 lorsque les valeurs figurent déjà dans le code source.
- Réservez la méthode 3 aux intégrations qui masquent l'appel réseau derrière du JavaScript.
Résoudre le curseur GeeTest v3 avec CaptchaAI
Envoyez les deux paramètres à CaptchaAI avec la méthode geetest, puis interrogez le résultat jusqu'à obtenir la solution. Réservez cette extraction à vos environnements ou à des cibles autorisées ; si la page traite des données personnelles, limitez la collecte au strict nécessaire (RGPD).
Python
Le script envoie la tâche à in.php, récupère un identifiant, puis interroge res.php toutes les 5 secondes jusqu'à la réponse. La boucle s'arrête dès que le statut passe à 1 :
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://example.com/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
La logique est identique avec Axios : un POST pour soumettre la tâche, puis un GET en boucle pour interroger le résultat :
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://example.com/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);
Injecter la solution dans le formulaire
La réponse de CaptchaAI pour GeeTest v3 contient trois champs : challenge, validate et seccode. Injectez-les dans des champs cachés avant de soumettre le formulaire :
# 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"])
Dépannage
La plupart des échecs viennent d'un challenge expiré ou d'un gt copié d'un autre site. Le tableau suivant regroupe les cas les plus fréquents et leur correctif :
| Problème | Cause | Correctif |
|---|---|---|
ERROR_BAD_PARAMETERS |
gt ou challenge absent |
Les deux sont obligatoires ; extrayez de nouvelles valeurs |
challenge périmé |
Le jeton de défi a expiré | Extrayez un nouveau challenge juste avant l'envoi |
Mauvaise valeur gt |
Copiée depuis un autre site | Le gt est propre à chaque site ; réextrayez-le sur la cible |
| Solution refusée par le site | Le défi a expiré pendant la résolution | Réduisez le délai entre l'extraction et la soumission |
CAPCHA_NOT_READY en boucle |
La résolution est encore en cours | Continuez d'interroger res.php toutes les 5 s, sans raccourcir l'intervalle |
FAQ
Les questions ci-dessous reviennent le plus souvent lors d'une première intégration de GeeTest v3.
Faut-il piloter un navigateur pour extraire le challenge ?
Pas toujours. Si le défi provient d'un appel d'enregistrement (méthode 1), une simple requête HTTP avec requests suffit. Sinon, passez par un navigateur headless comme Puppeteer.
Pourquoi le challenge change-t-il à chaque chargement ?
C'est un jeton de session à usage unique : GeeTest en émet un neuf à chaque affichage pour empêcher le rejeu. Seul le gt, l'identifiant du site, reste stable.
CaptchaAI prend-il en charge GeeTest v4 ?
Pas encore : seul GeeTest v3 est pris en charge. GeeTest v4 est annoncé comme à venir, sans date publique.
Combien de temps un défi GeeTest reste-t-il exploitable ?
En général 60 à 120 secondes. Envoyez le challenge à CaptchaAI dans la foulée pour éviter qu'il n'expire avant la validation.
Résolvez vos CAPTCHA GeeTest v3 avec CaptchaAI
Créez votre compte et récupérez votre clé API sur captchaai.com, puis lancez votre première résolution GeeTest v3 avec les scripts ci-dessus.