Wer GeeTest v3 automatisiert lösen will, scheitert fast immer an derselben Stelle: Ein einmal kopierter challenge-Wert funktioniert nach wenigen Sekunden nicht mehr. Die eigentliche Arbeit ist deshalb das Extrahieren der richtigen Parameter zum richtigen Zeitpunkt. Sie brauchen zwei Werte von der Zielseite: gt, die statische Site-ID, und challenge, ein bei jedem Aufruf neu erzeugtes Sitzungstoken. Dieser Leitfaden zeigt drei Extraktionswege und wie Sie beide an CaptchaAI übergeben.
Die zwei Parameter, die GeeTest v3 verlangt
| Parameter | Beschreibung | Statisch/Dynamisch | Beispiel |
|---|---|---|---|
gt |
Site-ID (32-stellige Hexadezimalzahl) | Statisch pro Site | 019924a82c70bb123aae90d483b6a0ed |
challenge |
Sitzungstoken (32-stellige Hexadezimalzahl) | Dynamisch pro Seitenladevorgang | a3f5c8d2e1b04a6789012345678abcdf |
api_server |
GeeTest API-Subdomain (optional) | Statisch pro Site | api-na.geetest.com |
Der gt-Wert bleibt über alle Anfragen identisch. Der challenge-Wert wird bei jedem Laden neu vergeben und läuft nach kurzer Zeit ab – für jeden Versuch ziehen Sie einen frischen challenge. api_server ist optional und nur bei abweichender GeeTest-Subdomain relevant.
GeeTest v3 begegnet Ihnen vor allem bei Login- und Registrierungsformularen sowie bei Termin- und Visa-Portalen, wie sie im DACH-Raum verbreitet sind. Erkennen lässt sich der Typ am Slider-Widget und an Netzwerkanfragen zu einer geetest.com-Subdomain. Welche der drei folgenden Methoden greift, hängt davon ab, wann und wie die Seite gt und challenge lädt – probieren Sie sie in dieser Reihenfolge durch.
In der Praxis läuft diese Extraktion oft auf einem Headless-Worker, etwa einer kleinen VPS bei Hetzner oder netcup. Verarbeiten Sie dabei personenbezogene Daten, prüfen Sie vorab Ihre DSGVO-Grundlage; auch IP-Adressen gelten als personenbezogen. Alle Beispiele nutzen example.com als Platzhalter – ersetzen Sie die URL durch Ihre eigene Zielseite.
Methode 1: Den Register-Aufruf abfangen
Die meisten GeeTest-v3-Implementierungen holen den challenge von einem Registrierungsendpunkt – die Antwort enthält beide Werte auf einen Schlag:
{
"success": 1,
"challenge": "a3f5c8d2e1b04a6789012345678abcdf",
"gt": "019924a82c70bb123aae90d483b6a0ed",
"new_captcha": true
}
Python (requests)
Eine Session liest die JSON-Antwort des Registrierungsaufrufs aus:
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 (Puppeteer-Netzwerkanalyse)
Lädt die Seite den challenge per JavaScript nach, greifen Sie ihn mit Puppeteer im response-Event ab:
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);
Methode 2: Parameter aus dem Seitenquelltext lesen
Manche Sites betten gt und challenge direkt ins HTML ein. Dann lesen Sie beide per regulärem Ausdruck aus – zuerst aus data--Attributen, ersatzweise aus JavaScript-Variablen:
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}")
Methode 3: initGeetest abfangen
GeeTest v3 wird über initGeetest() initialisiert. Geben weder Register-Aufruf noch Seitenquelltext die Werte preis, fangen Sie den Aufruf vor den Seitenskripten ab:
// 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", ... }
Dieser Weg liefert neben gt und challenge oft auch das Feld product.
GeeTest v3 mit CaptchaAI lösen
Liegen gt und challenge vor, übermitteln Sie beide mit der pageurl an in.php und fragen das Ergebnis über res.php ab – sobald status auf 1 springt, steht die Lösung bereit. Bei hohem Durchsatz lohnt sich eine Wiederholungslogik mit exponentiellem Backoff, statt das Polling-Intervall pauschal zu erhöhen.
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://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
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);
Die Lösung ins Formular einfügen
Die CaptchaAI-Antwort besteht aus drei Feldern – challenge, validate und seccode –, die Sie als versteckte Felder ins Formular schreiben, bevor Sie es absenden:
# 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"])
Häufige Fehler und ihre Ursachen
Die meisten Fehlversuche gehen auf abgelaufene Parameter zurück:
| Problem | Ursache | Lösung |
|---|---|---|
ERROR_BAD_PARAMETERS |
gt oder challenge fehlt |
Beides ist erforderlich; frische Werte extrahieren |
Veralteter challenge-Wert |
Das challenge-Token ist abgelaufen | Unmittelbar vor dem Absenden einen neuen challenge extrahieren |
Falscher gt-Wert |
Von einer anderen Website kopiert | gt ist standortspezifisch; erneut aus der Zielseite extrahieren |
| Lösung von der Site abgelehnt | Der challenge ist während des Lösens abgelaufen |
Zeit zwischen Extraktion und Übermittlung verkürzen |
Häufige Fragen
Warum muss ich den challenge-Wert bei jedem Versuch neu extrahieren?
Weil GeeTest ihn pro Seitenaufruf neu erzeugt und er nach rund 60–120 Sekunden abläuft. Ein zwischengespeicherter Wert wird abgelehnt – ziehen Sie den challenge direkt vor der Übermittlung.
Kann ich denselben gt-Wert für mehrere Anfragen wiederverwenden?
Ja. gt ist die statische Site-ID und bleibt pro Website konstant. Nur der dynamische challenge muss bei jedem Versuch frisch gezogen werden.
Welche Felder liefert CaptchaAI für die Formularübermittlung zurück?
Drei: challenge, validate und seccode. Diese tragen Sie als versteckte Formularfelder (geetest_challenge, geetest_validate, geetest_seccode) ein, bevor Sie das Formular absenden.
Wie finde ich den Register-Aufruf manuell?
Öffnen Sie die Browser-DevTools, wechseln Sie in den Reiter „Netzwerk" und laden Sie die Seite neu. Filtern Sie nach register oder gettype und suchen Sie die Antwort, die gt und challenge enthält – genau diesen Endpunkt automatisieren Sie anschließend.
Was kostet das Lösen von GeeTest v3 mit CaptchaAI?
CaptchaAI rechnet Thread-basiert ab, nicht pro Lösung. Der Einstieg ist BASIC ab 15 $/Monat mit 5 Threads und unbegrenzten Lösungen pro Thread; ADVANCE (90 $/Monat, 50 Threads) erhöht nur die Zahl paralleler Anfragen. Kein Aufpreis nach CAPTCHA-Typ.
GeeTest v3 in Ihre Automatisierung integrieren
Ziehen Sie gt und challenge frisch, übergeben Sie beide an CaptchaAI und fügen Sie die zurückgegebenen Felder ins Formular ein. Holen Sie sich Ihren API-Schlüssel unter captchaai.com.