So lösen Sie ein Pydoll-Captcha mit CapSkip in asynchronem Python

Um ein Pydoll-Captcha zu lösen, lesen Sie den sitekey von der Seite, lösen ihn mit dem AsyncCapSkip-Client von CapSkip auf demselben asyncio-Event-Loop, schreiben das Token in das Textarea g-recaptcha-response und führen den Callback der Seite aus oder senden das Formular ab. Pydoll 3.0 hat keinen eigenen reCAPTCHA-Code, das Token muss also von einem Löser kommen. CapSkip läuft auf Ihrem eigenen Windows-PC, daher fallen bei einem Crawl, der viele Seiten öffnet, keine Kosten pro Lösung an. Dieser Leitfaden baut den gesamten Pydoll-Captcha-Ablauf in etwa 80 Zeilen und behandelt die Fallen, die unter Windows Zeit kosten.
Was Sie brauchen
- CapSkip läuft auf einem Windows-PC, mit eingeschaltetem reCAPTCHA-Löser. Die Einrichtungsanleitung führt durch den ersten Start.
- Python 3.10 oder neuer. Pydoll 3.0.0 und das Paket capskip 1.3.0 setzen es beide voraus. Alles unten lief unter Windows 11 mit Python 3.14.0, und das vollständige Skript lief außerdem unter Python 3.11.7. Python 3.10, 3.12 und 3.13 wurden nicht ausgeführt.
- Google Chrome, installiert im Standardordner Program Files. Der Leitfaden nutzte Chrome 155, und das vollständige Skript lief außerdem gegen Chrome for Testing 146.
- Eine PowerShell-Eingabeaufforderung. Die Befehle verwenden PowerShell-Syntax.
- Eine Seite, die Ihnen gehört, mit einer reCAPTCHA-v2-Checkbox. Das Beispielformular ist ein einfaches Registrierungsformular. Passen Sie die Selektoren an Ihre Seite an.
- Eine Adresse für den Löser. Im Local-Modus antwortet CapSkip auf 127.0.0.1 nur für dieses Gerät. Im Server-Modus lauscht es auf Ihrer Netzwerkadresse oder öffentlichen IP, sodass Pydoll auf einem anderen PC, einem VPS oder einer gehosteten Plattform es über die API aufrufen kann. Eingestellt wird beides unter Verbindungseinstellungen.
Die Messungen unten stammen von einem Mock-CapSkip und einer Mock-Registrierungsseite (nachgebaute Testattrappen) auf demselben PC, verwendet mit dem echten Pydoll, dem echten SDK, einem echten Chrome und dem echten Skript api.js von Google. Das Verhalten von Browser und SDK gilt auch für Ihre eigene Website. Die Lösezeiten und die Regeln der Seite zum Annehmen und Ablehnen sind dagegen die des Mocks. Ein Lösevorgang gegen ein laufendes CapSkip mit dem öffentlichen Test-sitekey von Google und den Standardeinstellungen dauerte 9,0 Sekunden und lieferte ein Token mit 1.764 Zeichen. Die Lösezeiten schwanken von Lauf zu Lauf. Dieses Token wurde nirgends verwendet, und eine Live-Website wurde nicht ausprobiert.
Wo Pydoll aufhört und CapSkip anfängt
Pydoll steuert Chrome über das DevTools-Protokoll, ganz ohne WebDriver dazwischen. Es gibt Ihnen den Browser, die Elemente der Seite und eine Möglichkeit, JavaScript auszuführen. Sein einziger Captcha-Code in 3.0.0 ist ein Helfer für die Checkbox von Cloudflare Turnstile. Dieser Helfer findet die Checkbox und klickt sie an. Er liest nie ein Token und löst nie einen Fehler aus, wenn nichts passiert. Eine reCAPTCHA-Verarbeitung gibt es überhaupt nicht. Eine reCAPTCHA-v2-Checkbox löst ein Dienst, der ein Token zurückgibt, und die Seite braucht dieses Token dann an der richtigen Stelle. Diese Aufgabe übernimmt hier CapSkip, und der Rest dieses Leitfadens ist die Verbindungslogik dazwischen.
Schritt 1: Das Projekt anlegen
Verwenden Sie eine virtuelle Umgebung in einem leeren Ordner. Die Befehle rufen das Python der Umgebung über dessen Pfad auf, Sie müssen sie also nicht aktivieren, und eine restriktive Richtlinie für PowerShell-Skripte stört nicht. Führen Sie jedes Skript unten mit dem Python der Umgebung aus, zum Beispiel .\.venv\Scripts\python.exe solve.py. Eine ältere Pydoll-Installation, etwa 2.27.0, würde schon beim ersten Import des Skripts unten mit ImportError: cannot import name ‘Chrome’ from ‘pydoll’ scheitern. Pydoll 3.0 exportiert Chrome auf oberster Ebene.
# Windows PowerShell, in an empty folder python -m venv .venv .\.venv\Scripts\python.exe -m pip install pydoll-python capskip
Schritt 2: Chrome starten und die Seite öffnen
Speichern Sie dies als solve.py. Es ist das kleinste Pydoll-Skript, das Ihre Seite öffnet und ihren Titel ausgibt. Der Browser startet innerhalb eines async-with-Blocks und schließt sich daher wieder, wenn der Block endet.
# pip install pydoll-python capskip
import asyncio
from pydoll import Chrome
from pydoll.browser.options import ChromiumOptions
PAGE = 'https://example.com/page-with-recaptcha'
async def main():
options = ChromiumOptions()
options.headless = True # set to False to watch the window
async with Chrome(options=options) as browser:
tab = await browser.start()
await tab.go_to(PAGE)
print(await tab.title())
asyncio.run(main())Pydoll sucht Chrome nur an zwei Orten: in Program Files und Program Files (x86), jeweils unter Google, Chrome, Application. Ein Chrome, das für einen einzelnen Benutzer unter AppData installiert wurde, wird nicht gefunden, und das Skript bricht mit InvalidBrowserPath ab. Setzen Sie options.binary_location auf den vollständigen Pfad von chrome.exe, um das zu beheben. Ein Pfad, der nicht existiert, scheitert stattdessen mit FailedToStartBrowser. Dieselbe Zeile richtet Pydoll auch auf Chrome for Testing aus, und das funktionierte ebenfalls. Pydoll gleicht Browserversionen mit nichts ab, daher liefen 146 und 155 beide.
Der Start ist unter Windows langsamer, als man erwartet. Auf dem Test-PC dauerte browser.start() mit den Standardeinstellungen und Headless-Chrome 155 2,8 Sekunden. Das erste go_to brauchte auf einer Seite ohne externes Skript 2,1 Sekunden mehr und auf der Registrierungsseite, die das Skript von Google lädt, etwa 3,1 Sekunden. Pydoll verbindet sich mit localhost, und localhost versucht zuerst die IPv6-Adresse. Der Debugging-Port von Chrome lauscht nicht auf IPv6, und Windows braucht etwa 2 Sekunden, um eine Loopback-Verbindung abzulehnen, daher zahlt jede neue Verbindung zu Chrome etwa 2 Sekunden, bevor die IPv4-Verbindung funktioniert. Das gilt für den Browser und für jeden zusätzlichen Tab, den Sie öffnen. Pydoll hat dafür keine Einstellung. Localhost innerhalb des Python-Prozesses auf 127.0.0.1 auflösen zu lassen, beseitigt die Verzögerung in einem Test, patcht aber die Standardbibliothek, deshalb lässt dieser Leitfaden es sein. Planen Sie die Verzögerung ein, wenn Sie einen Lauf messen.
Der with-Block schließt Chrome bei einem normalen Ende, nach einer Exception darin und nach einem abgebrochenen Task und entfernt den temporären Profilordner. Wird der Python-Prozess mit taskkill /F beendet, entfällt das. Im Test blieben dabei 13 chrome.exe-Prozesse am Laufen und der Profilordner auf der Festplatte. Strg+C in einer Konsole wurde nicht getestet. Wenn Sie einen Lauf abgeschossen haben, suchen Sie im Task Manager nach chrome.exe-Prozessen, bevor Sie den nächsten starten.
Schritt 3: prüfen, ob CapSkip antwortet
Bevor Sie einen Browser hinzufügen, vergewissern Sie sich, dass der Aufruf des Lösers für sich funktioniert. Speichern Sie dies als first_solve.py. AsyncCapSkip ist der asynchrone Client aus demselben Paket. Das SDK liest keine Umgebungsvariablen, daher stehen Host und Port im Client. Die Platzhalter sind wörtlich gemeint. YOUR_SITEKEY und die Seitenadresse müssen von einer Seite stammen, die wirklich ein reCAPTCHA-v2-Widget enthält, sonst antwortet CapSkip mit einem Fehler. Kopieren Sie den sitekey aus dem Seitenquelltext, wo er der Wert von data-sitekey ist. Schritt 4 liest ihn für Sie von der Seite aus. Führen Sie die Datei mit .\.venv\Scripts\python.exe first_solve.py aus.
# pip install capskip
import asyncio
from capskip import AsyncCapSkip
async def main():
solver = AsyncCapSkip(host='127.0.0.1', port=8080)
result = await solver.recaptcha(
sitekey='YOUR_SITEKEY',
url='https://example.com/page-with-recaptcha',
)
print(result['code'][:30])
asyncio.run(main())Schritt 4: den sitekey von der Seite lesen
Der sitekey steht auf der Seite, und Pydoll kann ihn lesen. Ein normales Widget ist ein div mit der Klasse g-recaptcha und einem Attribut data-sitekey. Ein Widget, das die Seite mit grecaptcha.render() zeichnet, hat kein solches div. Es hinterlässt ein iframe, dessen Adresse den sitekey in ihrem k-Parameter trägt. Diese Funktion behandelt beides mit einem Selektor, und am Tag-Namen erkennt sie, was sie bekommen hat. Fügen Sie sie in solve.py oberhalb von async def main() ein.
from urllib.parse import parse_qs, urlparse
async def find_sitekey(tab):
# A normal widget carries data-sitekey. A widget drawn by
# grecaptcha.render() only leaves its iframe, with k=<sitekey>.
found = await tab.query(
'.g-recaptcha[data-sitekey], '
'iframe[src*="recaptcha/api2/anchor"]',
timeout=10,
)
if found.tag_name == 'iframe':
src = found.get_attribute('src')
return parse_qs(urlparse(src).query)['k'][0]
return found.get_attribute('data-sitekey')Zwei Pydoll-Details sind hier wichtig. get_attribute ist eine gewöhnliche Funktion, keine Coroutine, rufen Sie es also nicht mit await auf, und es gibt None zurück, wenn das Attribut fehlt. Und query löst ElementNotFound aus, wenn nichts passt. Mit einem Timeout wartet es und löst stattdessen WaitElementTimeout aus, was die richtige Wahl für ein Widget ist, das einen Moment nach dem Laden der Seite erscheint. Ein Widget, das 3 Sekunden nach dem Laden eingefügt wurde, wurde etwa 3 Sekunden später gefunden, mit dem Timeout von 10 Sekunden in find_sitekey.
Schritt 5: auf demselben Event-Loop lösen
Pydoll läuft auf asyncio, und Ihr Aufruf zum Lösen läuft auf demselben Loop. Deshalb kommt es auf die Wahl des Clients an. Fügen Sie dies ebenfalls oberhalb von async def main() ein und verfahren Sie mit der Funktion aus Schritt 6 genauso.
from capskip import AsyncCapSkip
solver = AsyncCapSkip(host='127.0.0.1', port=8080)
async def solve(sitekey, url):
result = await solver.recaptcha(sitekey, url)
return result['code']Der synchrone CapSkip-Client würde ebenfalls funktionieren, blockiert aber den Loop für die gesamte Lösung, und Pydoll antwortet Chrome nicht mehr, solange er wartet. Ein Mock-Lauf zählte, wie oft die Seite antwortete, wenn sie alle 50 Millisekunden nach der Uhrzeit gefragt wurde, während eine Mock-Lösung von 3 Sekunden lief. Die längste Lücke ist die längste Zeit zwischen zwei Antworten:
| Client | Lösezeit | Zähler-Ticks | Längste Lücke |
|---|---|---|---|
| Der synchrone CapSkip-Client, direkt in der asynchronen Funktion aufgerufen | 3.76 s | 5 | 3.78 s |
| CapSkip in asyncio.to_thread | 3.76 s | 65 | 0.06 s |
| AsyncCapSkip | 5.0 s | 68 | 0.3 s |
Die Seite selbst lief im ersten Fall weiter. Nur die Python-Seite fror ein: Python kann auf nichts reagieren, was die Seite tut, bis die Lösung endet, daher werden Ereignisse aus einem zweiten Tab verspätet behandelt. AsyncCapSkip brauchte hier etwa eine Sekunde länger, aber das hängt davon ab, wo seine nächste Abfrage nach der Lösung liegt, und bei Mock-Lösungen von 2, 4 und 8 Sekunden war es früher fertig als die Thread-Variante. Was es immer kostet, sind etwa 0,15 Sekunden blockierter Loop pro Anfrage, weil es jedes Mal einen neuen HTTP-Client baut. Das ist immer noch besser als ein eingefrorener Loop.
Schritt 6: das Token in die Seite schreiben
Ein gelöstes reCAPTCHA ist ein Token. Die Seite erwartet es in einem Textarea namens g-recaptcha-response, und wenn das Widget einen Callback hat, erwartet die Seite, dass der Callback mit dem Token läuft. Diese Funktion tut beides. Sie nutzt execute_script von Pydoll und gibt True zurück, wenn sie einen Callback gefunden und ausgeführt hat. Fügen Sie sie ebenfalls oberhalb von async def main() ein.
import json
FILL_JS = """
(function (token) {
var seen = new Set(), fn = null;
document.querySelectorAll('textarea[name="g-recaptcha-response"]')
.forEach(function (box) { box.value = token; });
(function walk(o, depth) {
if (!o || typeof o !== 'object' || seen.has(o) || depth > 6) return;
seen.add(o);
for (var k in o) {
var v; try { v = o[k]; } catch (e) { continue; }
if (k === 'callback' && !fn) {
fn = typeof v === 'string' ? window[v] : v;
}
if (v && typeof v === 'object' && !(v instanceof Node)) {
walk(v, depth + 1);
}
}
})(window.___grecaptcha_cfg, 0);
if (typeof fn === 'function') fn(token);
return typeof fn === 'function';
})(%s)
"""
async def fill_token(tab, token):
reply = await tab.execute_script(
FILL_JS % json.dumps(token), return_by_value=True)
outcome = reply['result']
if 'exceptionDetails' in outcome:
error = outcome['exceptionDetails']['exception']
raise RuntimeError(error['description'])
return outcome['result']['value'] # True if a callback ranDas JavaScript durchläuft das eigene Konfigurationsobjekt von Google, um den Callback zu finden, denn die Schlüssel darin sind kurze Namen, die Google ändern kann. Ein Attribut data-callback wird dort als Name einer Funktion gespeichert, daher schlägt der Durchlauf den Namen auf window nach. Ein Callback, der an grecaptcha.render() übergeben wurde, wird als die Funktion selbst gespeichert. Beide Fälle liefen gegen das echte api.js von Google.
Pydoll gibt die rohe DevTools-Antwort von execute_script zurück, der Wert steht also in reply[‘result’][‘result’][‘value’], und Sie müssen den Aufruf mit await ausführen. Ein Objekt kommt als Referenz zurück, außer Sie übergeben return_by_value=True. Ein JavaScript-Fehler löst in Python keine Exception aus. Er fügt der Antwort exceptionDetails hinzu, weshalb die Funktion danach sucht und einen eigenen RuntimeError auslöst.
Schritt 7: alles zusammensetzen
Löschen Sie async def main() und die Zeile asyncio.run(main()) am Ende von solve.py, behalten Sie die Funktionen darüber und fügen Sie an deren Stelle dies ein. Es löst, nachdem die Seite geladen ist, denn der sitekey kommt von der Seite, und es löst direkt vor der Verwendung des Tokens. Wenn ein Callback lief, übernimmt die Seite. Wenn nicht, klickt das Skript auf den Absenden-Button des Formulars und wartet auf die Navigation.
async def main():
options = ChromiumOptions()
options.headless = True # set to False to watch the window
async with Chrome(options=options) as browser:
tab = await browser.start()
await tab.go_to(PAGE)
sitekey = await find_sitekey(tab)
token = await solve(sitekey, await tab.current_url())
if not await fill_token(tab, token):
button = await tab.query('form button[type="submit"]')
async with tab.expect_navigation():
await button.click()
print(await tab.title())
asyncio.run(main())Auf einer Seite, auf der ein Callback das Absenden übernimmt, gibt es nichts anzuklicken. Diese Version ersetzt main() auf dieselbe Weise. Was als Nächstes passiert, hängt von der Seite ab, warten Sie also auf etwas, das die Seite selbst ändert. Diese Version wartet darauf, dass sich ein Ergebniselement auf der Testseite von waiting auf etwas anderes ändert. Ändern Sie den Selektor auf das, was Ihre Seite aktualisiert.
async def main():
options = ChromiumOptions()
options.headless = True # set to False to watch the window
async with Chrome(options=options) as browser:
tab = await browser.start()
await tab.go_to(PAGE)
sitekey = await find_sitekey(tab)
token = await solve(sitekey, await tab.current_url())
if await fill_token(tab, token):
# The page's own callback takes over. What it does next is
# page specific: this page writes its answer into #result.
await tab.wait_for_script(
"var r = document.querySelector('#result');"
"return r && r.textContent !== 'waiting';"
)
print(await (await tab.query('#result')).text())
asyncio.run(main())Das gesamte Skript
Hier ist solve.py in einem Stück, mit allen Importen am Anfang. Es ersetzt die Blöcke, die Sie einzeln hinzugefügt haben, löschen Sie diese also zuerst. Auf der einfachen Formularseite gab es Welcome aus, und die Seite akzeptierte das Token einmal. Auf einer Seite, deren Callback das Absenden übernimmt, kann diese Version den alten Titel ausgeben, bevor die Seite wechselt, nehmen Sie dort daher die Callback-Variante von oben.
# pip install pydoll-python capskip
import asyncio
import json
from urllib.parse import parse_qs, urlparse
from capskip import AsyncCapSkip
from pydoll import Chrome
from pydoll.browser.options import ChromiumOptions
PAGE = 'https://example.com/page-with-recaptcha'
solver = AsyncCapSkip(host='127.0.0.1', port=8080)
# Put the token in every response box, then run the page's callback.
FILL_JS = """
(function (token) {
var seen = new Set(), fn = null;
document.querySelectorAll('textarea[name="g-recaptcha-response"]')
.forEach(function (box) { box.value = token; });
(function walk(o, depth) {
if (!o || typeof o !== 'object' || seen.has(o) || depth > 6) return;
seen.add(o);
for (var k in o) {
var v; try { v = o[k]; } catch (e) { continue; }
if (k === 'callback' && !fn) {
fn = typeof v === 'string' ? window[v] : v;
}
if (v && typeof v === 'object' && !(v instanceof Node)) {
walk(v, depth + 1);
}
}
})(window.___grecaptcha_cfg, 0);
if (typeof fn === 'function') fn(token);
return typeof fn === 'function';
})(%s)
"""
async def find_sitekey(tab):
# A normal widget carries data-sitekey. A widget drawn by
# grecaptcha.render() only leaves its iframe, with k=<sitekey>.
found = await tab.query(
'.g-recaptcha[data-sitekey], '
'iframe[src*="recaptcha/api2/anchor"]',
timeout=10,
)
if found.tag_name == 'iframe':
src = found.get_attribute('src')
return parse_qs(urlparse(src).query)['k'][0]
return found.get_attribute('data-sitekey')
async def solve(sitekey, url):
result = await solver.recaptcha(sitekey, url)
return result['code']
async def fill_token(tab, token):
reply = await tab.execute_script(
FILL_JS % json.dumps(token), return_by_value=True)
outcome = reply['result']
if 'exceptionDetails' in outcome:
error = outcome['exceptionDetails']['exception']
raise RuntimeError(error['description'])
return outcome['result']['value'] # True if a callback ran
async def main():
options = ChromiumOptions()
options.headless = True # set to False to watch the window
async with Chrome(options=options) as browser:
tab = await browser.start()
await tab.go_to(PAGE)
sitekey = await find_sitekey(tab)
token = await solve(sitekey, await tab.current_url())
if not await fill_token(tab, token):
button = await tab.query('form button[type="submit"]')
async with tab.expect_navigation():
await button.click()
print(await tab.title())
asyncio.run(main())Zwei Tabs gleichzeitig lösen
Zwei Tabs können mit asyncio.gather gleichzeitig lösen. Zwei Lösungen von je 3 Sekunden, gemeinsam gestartet, waren gemeinsam in unter 4 Sekunden fertig, mit verschiedenen Tokens. Die Falle sind Cookies. Tabs im Standard-Browser-Kontext teilen sich einen Cookie-Speicher, daher kann das zweite Laden der Seite das Session- oder CSRF-Cookie ersetzen, mit dem der erste Tab gleich absenden will. Die Mock-Website setzt bei jedem Laden der Seite ein neues Session-Cookie, wie es manche echten Websites tun, daher ersetzte das zweite Laden das Cookie des ersten Tabs, und eines von zwei gleichzeitigen Absendungen wurde abgelehnt. Als jeder Tab seinen eigenen Browser-Kontext bekam, war das behoben, und beide Absendungen wurden akzeptiert. Diese Version ersetzt main() auf dieselbe Weise und klickt auf Absenden, passt also zu einem einfachen Formular.
async def solve_page(tab):
sitekey = await find_sitekey(tab)
token = await solve(sitekey, await tab.current_url())
await fill_token(tab, token)
button = await tab.query('form button[type="submit"]')
async with tab.expect_navigation():
await button.click()
return await tab.title()
async def main():
options = ChromiumOptions()
options.headless = True
async with Chrome(options=options) as browser:
await browser.start()
# One browser context per tab: tabs in the default context
# share cookies, and a second page load can replace the first
# tab's session or CSRF cookie.
contexts = [await browser.create_browser_context() for _ in range(2)]
tabs = [
await browser.new_tab(PAGE, browser_context_id=context)
for context in contexts
]
print(await asyncio.gather(*[solve_page(tab) for tab in tabs]))
for context in contexts:
await browser.delete_browser_context(context)
asyncio.run(main())Pydoll gegen CapSkip auf einem anderen Rechner ausführen
Pydoll und der Löser müssen sich keinen PC teilen. Schalten Sie CapSkip in den Verbindungseinstellungen in den Server-Modus, dann lauscht es auf Ihrer Netzwerkadresse oder öffentlichen IP. Für stabilen Fernzugriff wird eine statische öffentliche IP empfohlen. Danach ändert sich nur die Zeile mit dem Client. Der Löser bleibt Ihr eigener Windows-Rechner, und abgerechnet wird pro Lösung weiterhin nichts.
# CapSkip on another Windows PC, in Server mode
solver = AsyncCapSkip(
host='192.168.1.50', # that PC's network address
port=8080,
apiKey='YOUR_API_KEY', # only if API key validation is on
)Beachten Sie zwei Dinge. Das SDK spricht mit CapSkip über reines HTTP, daher wandert der API-Schlüssel im Klartext. Halten Sie diese Verbindung in einem privaten Netzwerk oder einem VPN, und aktivieren Sie die API-Key-Validierung. Und CapSkip öffnet seinen Port beim Start des Server-Modus für jede Adresse, daher grenzen Sie ihn mit einer Blockierregel der Windows Firewall für die Adressen ein, die Sie nicht erwarten. Mit AsyncCapSkip scheitert eine nicht erreichbare Adresse nach etwa 5 Sekunden mit einer NetworkException mit leerer Meldung. Das Remote-Setup wurde für diesen Leitfaden nicht ausgeführt, es wurde nur der Konstruktor gelesen.
Timeouts und wie ein Fehlschlag aussieht
Lösen Sie und senden Sie zügig ab. Ein reCAPTCHA-Token funktioniert einmal und läuft ab, lösen Sie also, nachdem die Seite geladen ist, und kurz bevor Sie absenden. Die strenge Mock-Website wies ein zweites Absenden desselben Tokens mit einer 403 zurück, was die eigene Regel des Mocks ist. Die Seite zum Ablauf des reCAPTCHA-Tokens nennt die Zeiten. So sah jeder Fehlschlag mit AsyncCapSkip und den Standardeinstellungen gegen den Mock aus:
| Fall | Was Sie sehen | Nach |
|---|---|---|
| CapSkip läuft nicht | NetworkException: All connection attempts failed | 2.2 s |
| CapSkip nimmt den Aufruf an und verstummt | NetworkException mit leerer Meldung | 5.3 s |
| Die Lösung ist nicht möglich | ApiException: ERROR_CAPTCHA_UNSOLVABLE | 0.7 s |
| Eine Lösung von 30 s mit recaptchaTimeout auf 6 | TimeoutException: timeout 6.0 exceeded | 8.7 s |
| Dieselbe Lösung in asyncio.wait_for mit 4 s | Das eigene TimeoutError von Python (asyncio.TimeoutError unter Python 3.10) | 4.0 s |
Setzen Sie recaptchaTimeout am Client, nicht am Aufruf. Ein Argument timeout am Aufruf löst TypeError mit der Meldung got multiple values for keyword argument ‘timeout’ aus. Das SDK prüft sein Limit nur zwischen den Abfragen, weshalb das Limit von 6 Sekunden erst bei 8,7 Sekunden ausgelöst wurde. Umhüllen Sie den Aufruf mit asyncio.wait_for, wenn eine harte Obergrenze wichtig ist. Das bricht nur das Warten auf Ihrer Seite ab. CapSkip behält den Job bis zu seinen eigenen Limits, was mit der echten App nicht getestet wurde. Pydoll hat eigene Fehler. Eine Navigation, die länger dauert, als go_to erlaubt, löst PageLoadTimeout aus, und eine verweigerte Verbindung löst NavigationError mit net::ERR_CONNECTION_REFUSED in der Meldung aus. Pydoll hat außerdem eine Klasse TimeoutException, und CapSkip hat eine mit demselben Namen. Es sind verschiedene Klassen, importieren Sie also die Module und schreiben Sie capskip.TimeoutException, wenn Sie die des Lösers abfangen.
Häufige Fehler bei einem Pydoll-Captcha
| Was Sie sehen | Ursache | Beheben |
|---|---|---|
| ImportError: cannot import name ‘Chrome’ from ‘pydoll’ | Pydoll 2.x ist installiert | Aktualisieren Sie auf pydoll-python 3 |
| InvalidBrowserPath: No valid browser path found | Chrome ist für einen Benutzer installiert, nicht in Program Files | options.binary_location setzen |
| FailedToStartBrowser: The browser exited with code 21 | Ein fest vorgegebenes user-data-dir wird bereits von einem anderen Browser verwendet | Verwenden Sie einen anderen Profilordner oder keinen |
| ArgumentAlreadyExistsInOptions | Dasselbe ChromiumOptions-Objekt wurde für einen zweiten Browser wiederverwendet, oder Sie haben ein Flag hinzugefügt, das Pydoll selbst hinzufügt | Bauen Sie für jeden Browser ein neues Options-Objekt |
| WaitElementTimeout bei der sitekey-Abfrage | Die Seite hat noch kein Widget, oder der Selektor passt nicht darauf | Prüfen Sie die Seite in einem sichtbaren Fenster und erhöhen Sie dann das Timeout |
| NetworkException: All connection attempts failed | CapSkip läuft nicht, oder der Port ist falsch | Starten Sie CapSkip und prüfen Sie die Verbindungseinstellungen |
| ApiException: ERROR_CAPTCHA_UNSOLVABLE | CapSkip konnte die Lösung nicht abschließen | Prüfen Sie den sitekey und die Seitenadresse und versuchen Sie es dann einmal erneut. Siehe ERROR_CAPTCHA_UNSOLVABLE |
| RuntimeError: ReferenceError, ein Name ist nicht definiert | Das JavaScript in fill_token hat innerhalb der Seite eine Exception ausgelöst | Führen Sie dasselbe Skript in der Browser-Konsole aus |
| Die Seite meldet, das Captcha sei fehlgeschlagen, kein Fehler in Python | Das Token wurde zweimal verwendet, oder der Callback lief nicht | Lösen Sie direkt vor jedem Absenden erneut |
| FailedToStartBrowser: Could not launch | options.binary_location zeigt auf eine Datei, die es nicht gibt | Prüfen Sie den Pfad zu chrome.exe |
| Jeder Lauf dauert etwa 4 Sekunden länger als die eigentliche Arbeit | Jede neue Verbindung zu Chrome zahlt die localhost-Verzögerung von etwa 2 Sekunden | Verwenden Sie einen Browser und einen Tab wieder: ein zweites go_to im selben Tab dauerte unter 0,2 Sekunden |
FAQ
Löst Pydoll reCAPTCHA von selbst?
Nein. Pydoll 3.0.0 hat keinen reCAPTCHA-Code. Sein einziger Captcha-Helfer sucht nach einer Cloudflare-Turnstile-Checkbox und klickt sie an, was keine Lösung ist und kein Token zurückgibt.
Funktioniert das mit Headless-Chrome?
Das vollständige Skript lief headless und mit sichtbarem Fenster gegen die Mock-Seite, auf Chrome 155 und Chrome for Testing 146. Wie eine Live-Website einen Headless-Browser behandelt, wurde nicht getestet, und das ist eine andere Frage als die nach dem Token.
Kann ich Pydoll auf einem anderen PC ausführen und CapSkip zu Hause?
Ja, wenn dieser PC CapSkip erreichen kann. Schalten Sie CapSkip in den Server-Modus und setzen Sie Host und Port im Client. Für stabilen Fernzugriff über das Internet wird eine statische öffentliche IP empfohlen. Der Schlüssel und die Seitenadresse wandern im Klartext über reines HTTP, bevorzugen Sie daher ein VPN oder ein privates Netzwerk und fügen Sie in der Windows Firewall eine Blockierregel für andere Adressen hinzu. Für diesen Leitfaden wurde kein Remote-Setup getestet.
Was ist mit unsichtbarem reCAPTCHA, v3 und Enterprise?
Der recaptcha-Aufruf des SDK nimmt dafür Optionen entgegen: invisible auf 1, enterprise auf 1 und version auf v3 gesetzt. Auf der Pydoll-Seite muss die Seite anders ausgelesen werden, etwa der Aktionsname für v3, und keine dieser Varianten wurde für diesen Leitfaden ausgeführt. Zum v3-Aufruf des Lösers selbst siehe reCAPTCHA v3 in Python lösen.
Die Kurzfassung
Ein Pydoll-Captcha-Ablauf besteht aus fünf Teilen: den sitekey aus data-sitekey oder aus der iframe-Adresse lesen, mit AsyncCapSkip lösen, damit der Event-Loop lebendig bleibt, das Token in g-recaptcha-response schreiben, den Callback der Seite ausführen oder absenden und jedem parallelen Tab seinen eigenen Browser-Kontext geben. Lösen Sie direkt vor dem Absenden, und setzen Sie recaptchaTimeout am Client, mit asyncio.wait_for um den Aufruf als harte Obergrenze.
- Alle Captcha-Typen, die das Python-Paket verarbeitet, finden Sie auf der Python-Captcha-Solver-Seite.
- Das Checkbox-reCAPTCHA in einem einfachen Python-Skript: reCAPTCHA v2 in Python lösen.
- Wie der Checkbox-Solver funktioniert: die reCAPTCHA-v2-Solver-Seite.
- Eine ähnliche Browser-Bibliothek mit eigener API: Captchas in nodriver lösen.
Ein Browser-Crawl wird langsam, lange bevor er teuer wird, und ein lokaler Captcha-Löser auf Ihrem eigenen Windows-Rechner sorgt dafür, dass die Lösungen keine Rechnung verursachen.
