Bevor Sie eine Cloudflare Turnstile-Herausforderung lösen können, müssen Sie sie auf der Seite erkennen und den Sitekey extrahieren. Turnstile kann über HTML-Attribute, JavaScript-API-Aufrufe eingebettet oder nach dem Rendern der Seite dynamisch geladen werden. In diesem Leitfaden werden alle Erkennungsmethoden behandelt – vom einfachen HTML-Parsing bis zur Laufzeit-JavaScript-Analyse.
Implementierungsmethoden für Turnstilee
Turnstilee werden an Standorten auf drei Arten eingebaut, wobei jede einen anderen Erkennungsansatz erfordert:
| Methode | Wie es funktioniert | Erkennungsschwierigkeit |
|---|---|---|
| HTML implizit | <div class="cf-turnstile" data-sitekey="..."> in der Seitenquelle |
Einfach (statisches HTML) |
| JavaScript explizit | turnstile.render() im Skript aufgerufen |
Mittel (JS analysieren) |
| Dynamisches Laden | Widget wird nach Benutzeraktion oder XHR geladen | Schwer (erfordert JS-Ausführung) |
Methode 1: Statische HTML-Erkennung
Die einfachste Turnstile-Integration verwendet die Klasse cf-turnstile und das Attribut data-sitekey:
import re
import requests
def detect_turnstile_html(url):
"""Detect Turnstile from static HTML."""
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 Chrome/120.0.0.0",
"Accept": "text/html,*/*;q=0.8",
"Accept-Language": "en-US,en;q=0.9",
}
response = requests.get(url, headers=headers, timeout=15)
html = response.text
result = {
"turnstile_found": False,
"sitekey": None,
"mode": None,
"theme": None,
"action": None,
"script_loaded": False,
}
# Check for Turnstile script
if "challenges.cloudflare.com/turnstile" in html:
result["script_loaded"] = True
# Check for widget container
if "cf-turnstile" in html:
result["turnstile_found"] = True
# Extract sitekey
sitekey_match = re.search(
r'data-sitekey=["\']([0-9x][A-Za-z0-9_-]+)["\']', html
)
if sitekey_match:
result["sitekey"] = sitekey_match.group(1)
# Extract mode
if 'data-size="invisible"' in html:
result["mode"] = "invisible"
elif 'data-appearance="interaction-only"' in html:
result["mode"] = "non-interactive"
else:
result["mode"] = "managed"
# Extract theme
theme_match = re.search(r'data-theme=["\'](\w+)["\']', html)
if theme_match:
result["theme"] = theme_match.group(1)
# Extract action
action_match = re.search(r'data-action=["\']([^"\']+)["\']', html)
if action_match:
result["action"] = action_match.group(1)
return result
# Usage
info = detect_turnstile_html("https://example.com/login")
if info["turnstile_found"]:
print(f"Sitekey: {info['sitekey']}")
print(f"Mode: {info['mode']}")
Methode 2: JavaScript-API-Erkennung
Einige Websites verwenden turnstile.render() anstelle von HTML-Attributen:
import re
def detect_turnstile_js_api(html):
"""Detect Turnstile from JavaScript render calls."""
patterns = [
# turnstile.render('#element', {sitekey: '...'})
r"turnstile\.render\s*\(\s*['\"]([^'\"]+)['\"]\s*,\s*\{([^}]+)\}",
# turnstile.render(element, {sitekey: '...'})
r"turnstile\.render\s*\([^,]+,\s*\{([^}]+)\}",
]
for pattern in patterns:
match = re.search(pattern, html, re.DOTALL)
if match:
config_text = match.group(match.lastindex)
# Extract sitekey from config object
sitekey_match = re.search(
r"sitekey\s*:\s*['\"]([0-9x][A-Za-z0-9_-]+)['\"]", config_text
)
# Extract callback
callback_match = re.search(
r"callback\s*:\s*(\w+|function)", config_text
)
# Extract action
action_match = re.search(
r"action\s*:\s*['\"]([^'\"]+)['\"]", config_text
)
# Extract appearance
appearance_match = re.search(
r"appearance\s*:\s*['\"]([^'\"]+)['\"]", config_text
)
return {
"found": True,
"method": "javascript_api",
"sitekey": sitekey_match.group(1) if sitekey_match else None,
"callback": callback_match.group(1) if callback_match else None,
"action": action_match.group(1) if action_match else None,
"appearance": appearance_match.group(1) if appearance_match else None,
}
return {"found": False, "method": None}
Methode 3: Dynamische Ladeerkennung (Selenium/Puppeteer)
Wenn Turnstile nach der Seiteninteraktion dynamisch geladen wird:
Python (Selen)
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
import re
def detect_turnstile_dynamic(url):
"""Detect dynamically loaded Turnstile using Selenium."""
options = webdriver.ChromeOptions()
options.add_argument("--disable-blink-features=AutomationControlled")
driver = webdriver.Chrome(options=options)
try:
driver.get(url)
# Wait for page to fully load
WebDriverWait(driver, 10).until(
lambda d: d.execute_script("return document.readyState") == "complete"
)
result = {
"turnstile_found": False,
"sitekey": None,
"iframe_present": False,
"response_field": False,
}
# Check for Turnstile iframe
iframes = driver.find_elements(By.CSS_SELECTOR, "iframe[src*='challenges.cloudflare.com']")
if iframes:
result["turnstile_found"] = True
result["iframe_present"] = True
# Check for cf-turnstile container
containers = driver.find_elements(By.CSS_SELECTOR, ".cf-turnstile, [data-sitekey]")
for container in containers:
sitekey = container.get_attribute("data-sitekey")
if sitekey:
result["turnstile_found"] = True
result["sitekey"] = sitekey
# Check for hidden response field
response_fields = driver.find_elements(
By.CSS_SELECTOR, "[name='cf-turnstile-response'], [name='g-recaptcha-response']"
)
if response_fields:
result["response_field"] = True
# Check page source for JS API render
page_source = driver.page_source
js_match = re.search(
r"sitekey\s*:\s*['\"]([0-9x][A-Za-z0-9_-]+)['\"]", page_source
)
if js_match and not result["sitekey"]:
result["sitekey"] = js_match.group(1)
result["turnstile_found"] = True
return result
finally:
driver.quit()
Node.js (Puppenspieler)
const puppeteer = require("puppeteer");
async function detectTurnstileDynamic(url) {
const browser = await puppeteer.launch({
headless: "new",
args: ["--disable-blink-features=AutomationControlled"],
});
const page = await browser.newPage();
const result = {
turnstileFound: false,
sitekey: null,
iframePresent: false,
responseField: false,
scriptUrl: null,
};
// Monitor network for Turnstile script
page.on("response", (response) => {
if (response.url().includes("challenges.cloudflare.com/turnstile")) {
result.scriptUrl = response.url();
}
});
await page.goto(url, { waitUntil: "networkidle2" });
// Check for Turnstile container
const sitekey = await page.evaluate(() => {
const el = document.querySelector(
".cf-turnstile, [data-sitekey]"
);
return el ? el.getAttribute("data-sitekey") : null;
});
if (sitekey) {
result.turnstileFound = true;
result.sitekey = sitekey;
}
// Check for Turnstile iframe
const iframes = await page.$$("iframe[src*='challenges.cloudflare.com']");
if (iframes.length > 0) {
result.turnstileFound = true;
result.iframePresent = true;
}
// Check for response field
const responseField = await page.$(
"[name='cf-turnstile-response']"
);
result.responseField = !!responseField;
await browser.close();
return result;
}
detectTurnstileDynamic("https://example.com/login").then(console.log);
Umfassende Erkennungsklasse
import re
import requests
class TurnstileDetector:
"""Detect Cloudflare Turnstile across all implementation methods."""
TURNSTILE_SCRIPT = "challenges.cloudflare.com/turnstile"
SITEKEY_PATTERNS = [
r'data-sitekey=["\']([0-9x][A-Za-z0-9_-]+)["\']',
r"sitekey\s*:\s*['\"]([0-9x][A-Za-z0-9_-]+)['\"]",
r"siteKey\s*[=:]\s*['\"]([0-9x][A-Za-z0-9_-]+)['\"]",
r"TURNSTILE_SITE_KEY\s*[=:]\s*['\"]([0-9x][A-Za-z0-9_-]+)['\"]",
]
def __init__(self, url, html=None):
self.url = url
self.html = html
if not self.html:
self._fetch()
def _fetch(self):
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 Chrome/120.0.0.0",
"Accept": "text/html,*/*;q=0.8",
"Accept-Language": "en-US,en;q=0.9",
}
response = requests.get(self.url, headers=headers, timeout=15)
self.html = response.text
def detect(self):
"""Run all detection methods and return results."""
return {
"url": self.url,
"turnstile_present": self.has_turnstile(),
"sitekey": self.extract_sitekey(),
"mode": self.detect_mode(),
"implementation": self.detect_implementation(),
"script_loaded": self.has_script(),
"response_field": self.has_response_field(),
"action": self.extract_action(),
"theme": self.extract_theme(),
}
def has_turnstile(self):
return (
self.has_script()
or "cf-turnstile" in self.html
or self.extract_sitekey() is not None
)
def has_script(self):
return self.TURNSTILE_SCRIPT in self.html
def has_response_field(self):
return "cf-turnstile-response" in self.html
def extract_sitekey(self):
for pattern in self.SITEKEY_PATTERNS:
match = re.search(pattern, self.html)
if match:
return match.group(1)
return None
def detect_mode(self):
if 'data-size="invisible"' in self.html or "size: 'invisible'" in self.html:
return "invisible"
if 'data-appearance="interaction-only"' in self.html:
return "non-interactive"
if "cf-turnstile" in self.html:
return "managed"
return "unknown"
def detect_implementation(self):
if "cf-turnstile" in self.html and re.search(r"data-sitekey=", self.html):
return "html_implicit"
if "turnstile.render" in self.html:
return "javascript_explicit"
if self.has_script() and not "cf-turnstile" in self.html:
return "dynamic_loading"
return "unknown"
def extract_action(self):
match = re.search(r'data-action=["\']([^"\']+)["\']', self.html)
if match:
return match.group(1)
match = re.search(r"action\s*:\s*['\"]([^'\"]+)['\"]", self.html)
return match.group(1) if match else None
def extract_theme(self):
match = re.search(r'data-theme=["\'](\w+)["\']', self.html)
return match.group(1) if match else "auto"
# Usage
detector = TurnstileDetector("https://example.com/login")
info = detector.detect()
if info["turnstile_present"]:
print(f"Sitekey: {info['sitekey']}")
print(f"Mode: {info['mode']}")
print(f"Implementation: {info['implementation']}")
Lösung nach Erkennung
Sobald es erkannt wurde, lösen Sie es mit CaptchaAI:
import requests
import time
API_KEY = "YOUR_API_KEY"
def solve_detected_turnstile(detection_result):
"""Solve Turnstile using detection results."""
if not detection_result["turnstile_present"]:
raise ValueError("No Turnstile detected")
if not detection_result["sitekey"]:
raise ValueError("Sitekey not found — may need browser-based extraction")
params = {
"key": API_KEY,
"method": "turnstile",
"sitekey": detection_result["sitekey"],
"pageurl": detection_result["url"],
"json": 1,
}
# Include action if present
if detection_result.get("action"):
params["action"] = detection_result["action"]
submit = requests.post("https://ocr.captchaai.com/in.php", data=params)
task_id = submit.json()["request"]
for _ in range(60):
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.get("status") == 1:
return result["request"]
raise TimeoutError("Turnstile solve timed out")
# Full workflow
detector = TurnstileDetector("https://example.com/signup")
info = detector.detect()
if info["turnstile_present"]:
token = solve_detected_turnstile(info)
print(f"Token: {token[:50]}...")
Randfälle
| Szenario | Herausforderung | Lösung |
|---|---|---|
| Sitekey in externer JS-Datei | Nicht im Seiten-HTML | Analysieren Sie verknüpfte JavaScript-Dateien nach Sitekey-Mustern |
| Sitekey aus der API-Antwort | Nach XHR-Aufruf geladen | Überwachen Sie Netzwerkanfragen für Sitekey in JSON-Antworten |
| Mehrere Turnstile-Widgets | Verschiedene Sitekeys auf derselben Seite | Ordnen Sie den Sitekey dem spezifischen Formular zu, das Sie einreichen |
| Turnstile im Schatten DOM | Nicht über reguläre Selektoren zugänglich | Verwenden Sie shadowRoot.querySelector im Browserkontext |
| Serverseitig gerenderter Sitekey | Eingebettet in Vorlagenvariablen | Überprüfen Sie die <script>-Tags auf Konfigurationsobjekte |
| Turnstile hinter der Authentifizierung | Auf der öffentlichen Seite nicht sichtbar | Zuerst authentifizieren, dann erkennen |
Fehlerbehebung
| Symptom | Ursache | Beheben |
|---|---|---|
| Skript-Tag gefunden, aber kein Sitekey | JS-API-Rendering mit Konfiguration aus einer anderen Quelle | Überprüfen Sie alle verknüpften JS-Dateien und XHR-Antworten |
| Falscher Sitekey extrahiert | Mehrere CAPTCHA-Widgets auf der Seite | Ordnen Sie Sitekeys den umgebenden Formularelementen zu |
| Die Erkennung funktioniert, aber die Lösung schlägt fehl | Für die Validierung erforderlicher Aktionsparameter | Fügen Sie den Wert data-action in die Lösungsanforderung ein |
| Widget nicht im ursprünglichen HTML | Dynamisches Laden nach Benutzerinteraktion | Verwenden Sie Selenium/Puppeteer für die Ganzseitendarstellung |
cf-turnstile-response-Feld leer |
Widget ist noch nicht fertig | Warten Sie, bis das Widget vollständig geladen ist |
Häufig gestellte Fragen
Können Turnstile-Standortschlüssel geändert werden?
Ja. Site-Betreiber können Sitekeys jederzeit ändern. Extrahieren Sie den Sitekey immer frisch von der Seite, anstatt ihn fest zu codieren.
Benötige ich den Aktionsparameter?
Nur wenn die Site es serverseitig validiert. Wenn data-action im HTML vorhanden ist, fügen Sie es in Ihre Lösungsanforderung ein, um optimale Ergebnisse zu erzielen.
Was passiert, wenn ich den Sitekey nicht finden kann?
Der Sitekey kann in einer externen JavaScript-Datei, einer API-Antwort oder dynamisch generiert sein. Verwenden Sie die Browser-DevTools (Registerkarte „Netzwerk“), um es zu finden, oder verwenden Sie Selenium/Puppeteer, um es nach dem Rendern der gesamten Seite zu extrahieren.
Beeinflusst die Erkennungsmethode die Lösung?
Nein. Der Turnstile-Solver von CaptchaAI funktioniert gleich, unabhängig davon, wie das Widget implementiert wurde. Sie benötigen lediglich den Sitekey und die Seiten-URL.
Zusammenfassung
Die Erkennung von Cloudflare Turnstile erfordert eine Überprüfung auf das Turnstile-Skript-Tag, den cf-turnstile-Container, data-sitekey-Attribute und turnstile.render()-Aufrufe. Verwenden Sie statisches HTML-Parsing für einfache Integrationen und Selenium/Puppeteer für dynamisch geladene Widgets. Sobald es erkannt wurde, lösen Sie es mit dem Turnstile-Solver von CaptchaAI unter Verwendung des extrahierten Sitekeys – alle Modi werden identisch mit einer Erfolgsquote von 100 % behandelt.
Verwandte Artikel
- Cloudflare Turnstile Sitekey-Extraktion
- GeeTest vs. reCAPTCHA Vergleich
- Cloudflare Turnstile Fehler beheben