Ein GeeTest v3 gilt erst als bestanden, wenn alle drei Rückgabewerte – geetest_challenge, geetest_validate und geetest_seccode – dort im DOM ankommen, wo die Seite sie erwartet: verstecktes Formularfeld, JavaScript-Callback oder XHR-Payload. Fehlt einer oder steht er im falschen Feld, weist der Server das Ergebnis ab – obwohl CaptchaAI sauber gelöst hat. Dieser Leitfaden zeigt für Playwright, Puppeteer und Selenium, wie Sie die Werte auslesen, lösen und eintragen.
Die drei Werte, die Sie einfügen
CaptchaAI liefert ein dreiteiliges Ergebnis:
{
"geetest_challenge": "a1b2c3d4e5...modified_challenge",
"geetest_validate": "abc123def456_validate",
"geetest_seccode": "abc123def456_validate|jordan"
}
Alle drei gehören ins Formular – fehlt einer, schlägt die Verifizierung fehl. Jeder Wert hat eine eigene Aufgabe:
geetest_challenge– referenziert die laufende Sitzung.geetest_validate– belegt, dass die Abfrage gelöst wurde.geetest_seccode– liefert den Prüf-Hash gegen Manipulation.
Der Anteil hinter dem Pipe-Zeichen im seccode (hier |jordan) gehört zwingend dazu – kürzen Sie den Wert nicht. CaptchaAI gibt die drei Werte als fertiges Dictionary zurück, sodass Sie sie ohne weitere Umformung übernehmen können.
Wo die Seite die drei Werte erwartet
Bevor Sie etwas eintragen, lohnt ein Blick auf den Übergabepunkt. GeeTest v3 nimmt die Lösung auf drei Wegen entgegen, und jede Seite implementiert genau einen davon:
- Verstecktes Formularfeld: die klassische Variante. Die drei Werte landen in
<input type="hidden">-Feldern und gehen beim normalen Absenden des Formulars mit. - JavaScript-Callback: Das Widget ruft nach erfolgreicher Lösung eine Funktion wie
onSuccessauf. Hier genügt kein Formularfeld – Sie müssen den Callback selbst auslösen. - XHR-Payload: Manche Single-Page-Anwendungen senden die Werte per Fetch/XHR an ein API-Endpoint, ganz ohne sichtbares Formular.
Öffnen Sie die Entwicklertools und prüfen Sie, welchen Weg die Zielseite nutzt, bevor Sie die passende Methode aus den folgenden Schritten wählen. Wird der falsche Übergabepunkt bedient, bleibt die Lösung wirkungslos – selbst wenn alle drei Werte korrekt sind.
Schritt 1: Parameter auslesen und lösen
In allen Frameworks gleich: gt und challenge auslesen und an CaptchaAI übergeben. Die Funktion pollt das Ergebnis und gibt die drei Werte als Dictionary zurück.
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")
Schritt 2: In Playwright (Python) einfügen
Playwright eignet sich hier besonders, weil Sie den Netzwerkverkehr direkt mitschneiden können. Sie lesen die register-Antwort mit, ziehen daraus gt und challenge und lösen anschließend über CaptchaAI. Danach schreiben Sie die drei Werte per page.evaluate ins DOM.
Parameter aus der Register-Antwort auslesen
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"]
Werte in die versteckten Formularfelder eintragen
# Mit CaptchaAI lösen
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")
Methode 2: Den GeeTest-Callback auslösen
Manche Seiten nutzen den GeeTest-Callback statt Formularfelder:
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);
}}
""")
Schritt 3: In Puppeteer (JavaScript) einfügen
In Puppeteer läuft dieselbe Logik in JavaScript ab: Ein response-Listener sammelt die Parameter, danach übergeben Sie die Lösung über page.evaluate an die versteckten Felder. Achten Sie darauf, das form-Element erst nach dem Laden des Widgets abzufragen.
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');
// Mit CaptchaAI lösen (Implementierung wie oben)
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');
}
Schritt 4: In Selenium (Python) einfügen
Selenium bringt keinen eingebauten Response-Listener mit. Hier lesen Sie gt und challenge per execute_script direkt aus den data-Attributen des Widgets und tragen die gelöste Antwort anschließend genauso per JavaScript ein.
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"
)
# Mit CaptchaAI lösen
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()
XHR-basierte Übermittlungen abfangen
Einige Seiten senden das GeeTest-Ergebnis per XHR statt per Formular-POST – typisch für moderne Single-Page-Anwendungen. Fangen Sie die Anfrage ab und ergänzen Sie die drei Werte in der JSON- oder Form-Payload. Ergänzen Sie den ursprünglichen Body, statt ihn zu ersetzen, damit Session-Cookies und CSRF-Token erhalten bleiben:
# 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)
GeeTest v3 in der eigenen Test-Pipeline
GeeTest v3 sitzt im DACH-Raum oft vor Login- oder Terminportalen. Prüfen Sie einen solchen Ablauf nur in eigener oder freigegebener Staging-Umgebung – etwa headless auf einem Hetzner-Worker in GitLab CI: erst auf das Widget warten, dann lösen, eintragen und #submit-button klicken. Halten Sie den Ablauf idempotent, damit ein fehlgeschlagener Lauf einfach wiederholt werden kann, und protokollieren Sie nur die Statuscodes, nie die Token selbst.
Hinweis: Bei personenbezogenen Daten gilt die DSGVO – IP-Adressen und Login-Daten zählen als personenbezogen. Prüfen Sie vorab die Rechtsgrundlage und beschränken Sie Tests auf eigene oder ausdrücklich freigegebene Umgebungen.
Fehlerbehebung
| Problem | Ursache | Lösung |
|---|---|---|
| Token wird nicht akzeptiert | Wert im falschen DOM-Element eingetragen | Feldnamen der drei Werte im Formular prüfen |
| Verifizierung schlägt fehl | Einer der drei Werte fehlt | Sicherstellen, dass alle drei Werte gesetzt sind |
| GeeTest-Fehler direkt nach dem Eintragen | Token veraltet oder doppelt verwendet | Sofort nach der Lösung eintragen, nie zwischenspeichern |
Häufige Fragen
Wie lange ist ein gelöstes GeeTest-Token gültig?
Nur kurz. Lösung und Eintragen sollten unmittelbar aufeinanderfolgen; ein zu spät gesetztes Token wird abgelehnt.
Warum wird mein GeeTest v3 trotz korrekter Lösung abgelehnt?
Meist steht ein Wert im falschen Feld oder einer der drei fehlt. Prüfen Sie die Feldnamen und übermitteln Sie geetest_challenge, geetest_validate und geetest_seccode gemeinsam.
Worin unterscheidet sich das Eintragen bei Playwright, Puppeteer und Selenium?
Kaum – die DOM-Logik ist identisch. Der einzige echte Unterschied liegt beim Auslesen von gt und challenge: Playwright und Puppeteer greifen die register-Antwort über einen Response-Listener ab, während Selenium die Werte per execute_script aus den data-Attributen zieht.
Was tun, wenn die Seite einen benutzerdefinierten Callback-Namen nutzt?
Suchen Sie den Namen in den initGeetest-Optionen im Seitenquelltext. Statt geetest_success rufen Sie dann genau diese Funktion mit den drei Werten auf; andernfalls aktualisiert das Widget seinen Zustand nicht.
Löst CaptchaAI auch GeeTest v4, und was kostet das?
GeeTest v4 ist noch nicht verfügbar, nur als „bald verfügbar" angekündigt – unterstützt wird aktuell GeeTest v3. Abgerechnet wird pro gleichzeitigem Thread mit unbegrenzten Lösungen; für einzelne Pipelines reicht BASIC (15 $/Monat, 5 Threads).
Verwandte Leitfäden
- GeeTest v3 per API lösen
- GeeTest-v3-Slider-Parameter auslesen
- GeeTest-Token im Challenge-Response-Workflow einfügen