So lösen Sie reCAPTCHA in MechanicalSoup und senden das Formular ab

MechanicalSoup führt das Captcha-Widget nie aus. Das Feld, das das Widget ausfüllen würde, fehlt deshalb meist im Formular, und der Versuch, es zu setzen, löst einen LinkNotFoundError aus. Die Lösung für ein MechanicalSoup-Captcha besteht aus drei Schritten. Lesen Sie den Sitekey aus der Seite, die MechanicalSoup bereits geparst hat, und lösen Sie das Captcha mit CapSkip, das auf Ihrem eigenen Windows-Rechner läuft. Fügen Sie dann das Feld g-recaptcha-response mit new_control selbst hinzu, bevor Sie submit_selected aufrufen. Um den Rest kümmert sich MechanicalSoup: das Session-Cookie, das versteckte CSRF-Feld und den Referer-Header, auf den ein strenger Server prüft. Dieser Leitfaden behandelt die reCAPTCHA-Checkbox, den unsichtbaren Button, v3, Enterprise, die anderen Widgets, die CapSkip löst, viele Anmeldungen gleichzeitig und den Betrieb des Lösers auf einem anderen Rechner.
Was Sie brauchen
- CapSkip auf einem Windows-Rechner, mit eingeschaltetem reCAPTCHA-Löser.
- Python 3.10 oder neuer, das Paket capskip und MechanicalSoup. Die Beispiele liefen mit capskip 1.3.0 und MechanicalSoup 1.4.0, die requests, Beautiful Soup und lxml mitinstallieren.
- Die URL der Seite mit dem Formular. Der Sitekey, die Formularfelder und der Widget-Typ stammen alle aus dem HTML dieser Seite.
- 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, damit ein Skript auf einem VPS oder einem anderen PC es über die API aufrufen kann. Eingestellt werden beide Modi in den Verbindungseinstellungen.
# Python 3.10 or later; MechanicalSoup brings requests, bs4 and lxml pip install capskip mechanicalsoup
Schritt 1: Öffnen Sie die Seite und finden Sie den Sitekey
Ein StatefulBrowser ist eine requests-Session mit einer angehängten, geparsten Seite. Er behält jedes Cookie, das die Website setzt, folgt Weiterleitungen und merkt sich die Adresse, bei der er gelandet ist. Öffnen Sie die Login-Seite und suchen Sie nach dem reCAPTCHA-Element:
# pip install capskip mechanicalsoup
import mechanicalsoup
PAGE_URL = "https://example.com/login"
# One StatefulBrowser per session: it keeps cookies, follows redirects
# and sends the current page as Referer when it submits a form.
browser = mechanicalsoup.StatefulBrowser(raise_on_404=True)
browser.open(PAGE_URL)
# The checkbox div, or the button on invisible reCAPTCHA, holds the key.
widget = browser.page.select_one(".g-recaptcha[data-sitekey]")
if widget is None:
raise RuntimeError("No reCAPTCHA widget in the HTML; see the v3 section.")
sitekey = widget["data-sitekey"]
# The form around the widget; a page often has a search form first.
browser.select_form(widget.find_parent("form") or 'form[method="post" i]')
print(sitekey, browser.url)Das Checkbox-Widget ist ein div mit der Klasse g-recaptcha und einem Attribut data-sitekey. Das unsichtbare reCAPTCHA setzt dieselbe Klasse und dasselbe Attribut meist stattdessen auf den Absende-Button, sodass ein einziger Selektor beides findet. Eine Seite, die api.js mit render=explicit lädt, baut das Widget aus ihrem eigenen Skript auf und übergibt den Schlüssel an grecaptcha.render. Lesen Sie ihn in diesem Fall in den DevTools aus diesem Aufruf aus. Wählen Sie das Formular um das Widget herum aus und nicht das erste Formular der Seite. Viele Websites haben im Header ein Suchformular, und select_form wählt standardmäßig genau dieses. Sitzt das Widget außerhalb seines Formulars, nimmt der Fallback das erste Formular, das per POST absendet. Hat die Seite zwei davon, übergeben Sie stattdessen die id des Login-Formulars.
browser.url ist die Adresse, von der die Seite nach allen Weiterleitungen kam. Genau diese URL senden Sie an den Löser, denn ein Sitekey ist für die Domains seiner Website registriert, und ein Lösevorgang für die falsche Seite ist verschwendet. raise_on_404 sorgt dafür, dass eine fehlende Seite schon bei open mit einem deutlichen Fehler scheitert und nicht erst weiter hinten im Code.
Schritt 2: Lösen Sie das reCAPTCHA mit CapSkip
Ein einziger Aufruf des Pakets capskip erledigt das Lösen. CapSkip löst das Captcha auf Ihrem Windows-Rechner und gibt das Token zurück, das das Widget in das Formular geschrieben hätte:
from capskip import CapSkip
solver = CapSkip(host="127.0.0.1", port=8080)
# enterprise.js in place of api.js means reCAPTCHA Enterprise.
enterprise = int("recaptcha/enterprise.js" in str(browser.page))
if widget.name == "button" and widget.get("data-action"):
# Google's v3 button binding: a g-recaptcha button with an action is v3.
result = solver.recaptcha(sitekey=sitekey, url=browser.url, version="v3",
action=widget["data-action"], enterprise=enterprise)
else:
# A g-recaptcha button, or data-size="invisible", is the invisible widget.
invisible = widget.name == "button" or widget.get("data-size") == "invisible"
result = solver.recaptcha(sitekey=sitekey, url=browser.url,
invisible=int(invisible), enterprise=enterprise)
token = result["code"]
print(token[:40]) # what the widget would have written into the formDie Checkbox ist der reine Aufruf, und das unsichtbare reCAPTCHA ist derselbe Aufruf mit invisible auf 1. Ein g-recaptcha-Button, der zusätzlich data-action trägt, ist die Button-Bindung von Google für v3. Das Skript löst ihn deshalb als v3 mit dieser action. Seiten, die enterprise.js statt api.js laden, verwenden reCAPTCHA Enterprise, und enterprise=1 teilt CapSkip das mit. Das Token steht in result["code"]. Es ist rund zwei Minuten gültig und lässt sich nur einmal verifizieren. Lösen Sie also direkt vor dem Absenden und lösen Sie für jede Anmeldung neu.
recaptcha pollt bis zu recaptchaTimeout, standardmäßig 300 Sekunden. Setzen Sie einen anderen Wert im Konstruktor, denn ein timeout pro Aufruf löst einen TypeError aus: Die Methode übergibt bereits selbst eines. CapSkip hat zusätzlich eigene Zeitlimits. Eine Aufgabe wartet bis zu 250 Sekunden (Wait Timeout) auf einen freien reCAPTCHA-Thread, und das Lösen hat danach ein eigenes Limit: Row Timeout, standardmäßig 250 Sekunden. Läuft eines davon ab, lässt CapSkip die Aufgabe scheitern, und das SDK löst eine ApiException mit ERROR_CAPTCHA_UNSOLVABLE aus. Der hier verwendete CapSkip-Client setzt für seine eigenen HTTP-Aufrufe kein Timeout. Ein CapSkip, das die Verbindung annimmt und nie antwortet, hält den Aufruf also offen.
Schritt 3: Fügen Sie das Feld hinzu, das das Widget geschrieben hätte, und senden Sie dann ab
An diesem Schritt stolpert jedes MechanicalSoup-Captcha-Skript. In einem echten Browser erzeugt das Skript von Google eine versteckte Textarea namens g-recaptcha-response und füllt sie, sobald das Captcha gelöst ist. MechanicalSoup parst das HTML, das der Server gesendet hat, und führt kein JavaScript aus, daher existiert dieses Feld auf den meisten Seiten nicht. Eine Zuweisung auf dem üblichen Weg löst dann einen LinkNotFoundError mit der Meldung No valid element named g-recaptcha-response aus. new_control fügt das Feld zum ausgewählten Formular hinzu:
# This line raises LinkNotFoundError on most pages: the widget script
# creates the field in a real browser, so the parsed HTML lacks it.
# browser["g-recaptcha-response"] = token
# Add the field the widget would have written, replacing any copy.
browser.new_control("hidden", "g-recaptcha-response", token)
browser["email"] = "YOUR_EMAIL"
browser["password"] = "YOUR_PASSWORD"
response = browser.submit_selected()
print(response.status_code, browser.url)new_control entfernt jedes Feld mit demselben Namen, bevor es sein eigenes hinzufügt. Das ist wichtig auf Seiten, die den noscript-Fallback von Google behalten. Dieser Fallback enthält eine Textarea mit demselben Namen, und der Parser von MechanicalSoup liest sie als Teil des Formulars. Auf diesen Seiten funktioniert auch die einfache Zuweisung, aber new_control deckt beide Arten von Seiten mit einer Zeile ab.
submit_selected sendet jedes Feld des Formulars, einschließlich des versteckten CSRF-Tokens, über dieselbe Session, die die Seite geladen hat. Außerdem sendet es die Adresse der Seite als Referer-Header. Reines requests sendet keinen, und auch die Low-Level-Methode Browser.submit von MechanicalSoup tut das nicht. Manche Frameworks, darunter Django, lehnen einen HTTPS-Formular-POST ohne Referer oder Origin ab, ganz gleich, wie korrekt Cookie und Token sind. Bleiben Sie also bei submit_selected auf dem Browser-Objekt, das die Seite geöffnet hat. Hat das Formular mehrere Absende-Buttons, übergeben Sie den Namen des gewünschten als btnName. Und ein 200 beweist nicht, dass die Anmeldung funktioniert hat. Viele Websites beantworten ein abgelehntes Captcha mit demselben Formular und einem 200. Prüfen Sie browser.page daher auf etwas, das nur eine angemeldete Seite zeigt.
reCAPTCHA v3 hat kein Widget, das sich finden ließe
v3 fragt den Nutzer nichts. Die Website lädt das Skript von Google mit ihrem Sitekey in der Adresse, nach render=, und ihr eigenes Skript fordert ein Token mit einem action-Namen wie login an. Die meisten v3-Seiten schreiben das Token dann selbst in ein verstecktes input-Feld. Lesen Sie beide Werte aus dem HTML aus, achten Sie darauf, ob das Skript api.js oder enterprise.js ist, und übergeben Sie sie mit version auf v3. Bindet eine Seite v3 stattdessen an einen Button, erledigt das bereits Schritt 2.
import re
# A v3 page has no widget element; the key sits in the script tags.
html = str(browser.page)
key = re.search(r"recaptcha/(api|enterprise)\.js\?[^\"'<>\s]*?\brender=(?!explicit\b)([\w-]+)", html)
action = re.search(r"execute\(\s*[^,()]+,\s*\{\s*[\"']?action[\"']?\s*:\s*[\"']([\w/]+)", html)
if key is None:
raise RuntimeError("No v3 render key in the HTML; check DevTools.")
v3 = solver.recaptcha(sitekey=key.group(2), url=browser.url, version="v3",
enterprise=int(key.group(1) == "enterprise"),
**({"action": action.group(1)} if action else {}))
browser.select_form('form[method="post" i]')
# The field the page's own script fills in; read its name in DevTools.
browser.new_control("hidden", "g-recaptcha-response", v3["code"])Führen Sie auf einer v3-Seite Schritt 1 nur bis browser.open aus, dann die Zeilen aus Schritt 2, die den Löser erzeugen, dann diesen Block und dann die Zugangsdaten und submit_selected aus Schritt 3. Die action ist wichtig, weil das Backend der Website meist prüft, ob das Token für die erwartete action ausgestellt wurde. Findet das Muster keine action, geht der Aufruf ohne action hinaus, und CapSkip verwendet verify, was eine Website, die login erwartet, ablehnt. Steht der execute-Aufruf in einem externen Skript, lesen Sie die action in den DevTools aus und übergeben Sie sie selbst. Der Feldname ist der, den das Skript der Seite ausfüllt, oft g-recaptcha-response, aber nicht immer. Prüfen Sie ihn deshalb in den DevTools, indem Sie einmal von Hand absenden. Füllen Sie dann die Zugangsdaten aus und rufen Sie submit_selected wie oben auf. Die reCAPTCHA-v3-Dokumentation von Google zeigt die Aufrufe von render und execute, auf die diese Muster passen. Der Leitfaden zum Lösen von reCAPTCHA v3 in Python behandelt den Aufruf selbst ausführlicher.
Andere Widgets funktionieren genauso
Jedes Widget, das CapSkip löst, folgt in MechanicalSoup demselben Muster: Schlüssel auslesen, lösen, das Feld hinzufügen, das das Widget erzeugt hätte, absenden. Nur der Selektor, der Feldname und die Methode ändern sich:
| Widget | Wo der Schlüssel steht | Hinzuzufügendes Feld | capskip-Methode |
|---|---|---|---|
| reCAPTCHA v2 checkbox | div.g-recaptcha, data-sitekey | g-recaptcha-response | solver.recaptcha() |
| reCAPTCHA v2 unsichtbar | der Button mit der Klasse g-recaptcha oder ein div mit data-size=invisible | g-recaptcha-response | solver.recaptcha() mit invisible=1 |
| reCAPTCHA v3 | api.js?render= oder enterprise.js?render= in der Skriptadresse oder ein g-recaptcha-Button mit data-action | das input-Feld, das das Skript der Seite füllt | solver.recaptcha() mit version="v3" und der action |
| reCAPTCHA Enterprise | enterprise.js statt api.js | wie bei v2 oder v3 | solver.recaptcha() mit enterprise=1 |
| Cloudflare-Turnstile-Widget | div.cf-turnstile, data-sitekey | cf-turnstile-response | solver.turnstile() |
| Friendly Captcha | div.frc-captcha, data-sitekey | frc-captcha-response bei v2, frc-captcha-solution bei v1 | solver.friendly_captcha() mit version="v1" oder "v2" |
| CaptchaFox | div.captchafox, data-sitekey | cf-captcha-response | solver.captchafox() |
CaptchaFox ist das einzige Widget mit einer zusätzlichen Regel. Sein Token gehört zu dem Browser, der es ausgestellt hat. Wenn das Ergebnis also einen userAgent enthält, setzen Sie ihn vor dem Absenden mit set_user_agent auf dem Browser-Objekt, denn sonst sendet MechanicalSoup python-requests und seinen eigenen Namen. Der Leitfaden zum Lösen von CaptchaFox in Python behandelt den Build des Widgets und die Wiederholungen.
Vollständiges lauffähiges Beispiel
Hier ist eine vollständige MechanicalSoup-Captcha-Anmeldung in einer Datei. login öffnet die Seite in einem neuen Browser-Objekt, findet das reCAPTCHA als Checkbox, unsichtbare Variante oder v3, löst es, fügt das Feld hinzu und sendet ab:
# pip install capskip mechanicalsoup
# login.py: run with python login.py
import os
import re
import mechanicalsoup
from capskip import CapSkip, CapSkipError, TimeoutException
PAGE_URL = "https://example.com/login"
# render= carries a v3 key on api.js or enterprise.js; render=explicit is a v2 page.
V3_KEY = re.compile(r"recaptcha/(api|enterprise)\.js\?[^\"'<>\s]*?\brender=(?!explicit\b)([\w-]+)")
# The action passed to execute, whether the key is a string or a variable.
V3_ACTION = re.compile(r"execute\(\s*[^,()]+,\s*\{\s*[\"']?action[\"']?\s*:\s*[\"']([\w/]+)")
solver = CapSkip(
apiKey=os.getenv("CAPSKIP_API_KEY", "capskip"),
host=os.getenv("CAPSKIP_HOST", "127.0.0.1"),
port=int(os.getenv("CAPSKIP_PORT", "8080")),
)
def solve_page(browser):
"""Solve the page's reCAPTCHA and select its form. Returns the token."""
html = str(browser.page)
enterprise = int("recaptcha/enterprise.js" in html)
widget = browser.page.select_one(".g-recaptcha[data-sitekey]")
if widget is not None:
browser.select_form(widget.find_parent("form") or 'form[method="post" i]')
if widget.name == "button" and widget.get("data-action"):
# Google's v3 button binding: plain api.js, the action on the button.
return solver.recaptcha(sitekey=widget["data-sitekey"], url=browser.url, version="v3",
action=widget["data-action"], enterprise=enterprise)["code"]
invisible = widget.name == "button" or widget.get("data-size") == "invisible"
return solver.recaptcha(sitekey=widget["data-sitekey"], url=browser.url,
invisible=int(invisible), enterprise=enterprise)["code"]
key = V3_KEY.search(html)
if key is None:
raise RuntimeError(f"No reCAPTCHA found on {browser.url}")
action = V3_ACTION.search(html)
browser.select_form('form[method="post" i]')
return solver.recaptcha(sitekey=key.group(2), url=browser.url, version="v3",
enterprise=int(key.group(1) == "enterprise"),
**({"action": action.group(1)} if action else {}))["code"]
def login(email, password, page_url=PAGE_URL):
# A new browser per login, so sessions never share cookies.
with mechanicalsoup.StatefulBrowser(raise_on_404=True) as browser:
browser.open(page_url)
token = solve_page(browser)
browser.new_control("hidden", "g-recaptcha-response", token)
browser["email"] = email
browser["password"] = password
response = browser.submit_selected()
return response.status_code, browser.url
if __name__ == "__main__":
try:
print(login("YOUR_EMAIL", "YOUR_PASSWORD"))
except TimeoutException:
raise SystemExit("gave up after recaptchaTimeout")
except CapSkipError as exc:
raise SystemExit(f"solve failed: {type(exc).__name__}: {exc}")
except mechanicalsoup.LinkNotFoundError as exc:
# A 404, or a form or field the page does not have.
raise SystemExit(f"not found: {str(exc) or PAGE_URL}")
except RuntimeError as exc:
raise SystemExit(str(exc))Jeder Aufruf von login baut seinen eigenen StatefulBrowser und schließt ihn am Ende, sodass zwei Anmeldungen nie Cookies teilen. Der with-Block schließt die Session auch dann, wenn ein Schritt scheitert. Die meisten Fehler verraten, wo Sie suchen müssen. Ein LinkNotFoundError bedeutet einen 404, ein fehlendes Formular oder einen Feldnamen, der nicht zur Seite passt. Ein RuntimeError bedeutet, dass die Seite kein reCAPTCHA zeigt, das das Skript erkennt. Öffnen Sie sie dann in einem echten Browser und prüfen Sie, was geladen wird. Lädt sie dort problemlos, geben Sie browser.open(page_url).status_code aus: Ein 403 bedeutet, dass die Website den Standard-User-Agent abgelehnt hat. Ein CapSkipError stammt aus dem Lösen selbst, und dafür gilt die Fehlertabelle unten.
Viele Anmeldungen gleichzeitig und der Löser auf einem anderen Rechner
MechanicalSoup ist synchron, und der hier verwendete CapSkip-Client ebenfalls. Muss ein MechanicalSoup-Captcha-Job also viele Anmeldungen abarbeiten, sind Threads der einfachste Weg, sie parallel auszuführen. Verwenden Sie einen StatefulBrowser pro Thread, was login bereits tut, und begrenzen Sie den Pool auf zehn, den Standardwert von Max. Threads für reCAPTCHA in CapSkip. Eine Aufgabe, die darüber hinaus in die Warteschlange kommt, wartet auf einen freien Thread, und eine lange Warteschlange zehrt an den 300 Sekunden, in denen das SDK pollt. Erhöhen Sie Max. Threads in den reCAPTCHA-Einstellungen von CapSkip, bevor Sie max_workers erhöhen. Speichern Sie das vollständige Beispiel als login.py und führen Sie Folgendes aus einer zweiten Datei daneben aus:
# pool.py: save next to login.py, the full example, and run this file
from concurrent.futures import ThreadPoolExecutor
from login import login
accounts = [("YOUR_EMAIL_1", "YOUR_PASSWORD_1"), ("YOUR_EMAIL_2", "YOUR_PASSWORD_2")]
def attempt(account):
# One failed login must not stop the others.
try:
return login(*account)
except Exception as exc:
return f"{type(exc).__name__}: {exc}"
# Ten at a time, to match CapSkip's default Max. Threads for reCAPTCHA.
with ThreadPoolExecutor(max_workers=10) as pool:
for account, outcome in zip(accounts, pool.map(attempt, accounts)):
print(account[0], outcome)pool.map gibt die Ergebnisse in der Reihenfolge zurück, in der die Konten übergeben wurden, und attempt macht aus einem Fehler einen Wert, sodass eine fehlgeschlagene Anmeldung die übrigen nicht stoppt. Bei fünfundzwanzig Anmeldungen über diesen Pool waren gegen einen Mock-Löser nie mehr als zehn Lösevorgänge gleichzeitig in Arbeit.
127.0.0.1 ist richtig, solange das Skript und CapSkip auf demselben Windows-PC laufen. Verschieben Sie das Skript auf einen VPS, in einen geplanten Job auf einer anderen Maschine oder in einen Container, zeigt Loopback auf den falschen Rechner, und der erste Lösevorgang löst eine NetworkException aus. Schalten Sie CapSkip in den Server-Modus, dann lauscht es auf Ihrer Netzwerkadresse oder öffentlichen IP, sodass jede dieser Umgebungen es über dieselbe API aufrufen kann. Verwenden Sie eine statische öffentliche IP, wenn der Weg über das Internet führt. CapSkip öffnet seinen Port für jede Adresse, sobald der Server-Modus startet. Damit nur die Rechner zugreifen, die Sie erwarten, fügen Sie für alle anderen eine Blockierungsregel in der Windows Firewall hinzu. Es bleibt Ihr eigener Rechner, und abgerechnet wird pro Lösung weiterhin nichts. Der Client liest keine Umgebungsvariablen von selbst, deshalb liest das vollständige Beispiel CAPSKIP_HOST aus und übergibt den Wert.
Häufige Fehler und was sie bedeuten
| Was Sie sehen | Ursache | Beheben |
|---|---|---|
| LinkNotFoundError: No valid element named g-recaptcha-response | Das Skript des Widgets ist nie gelaufen, daher fehlt das Feld, das es erzeugt, im geparsten Formular | Fügen Sie es vor dem Absenden mit new_control hinzu |
| Das Token wird mit dem Suchformular abgesendet, oder select_form löst einen LinkNotFoundError aus | Die Seite hat vorher ein anderes Formular, oder das Widget sitzt außerhalb seines Formulars | Wählen Sie das Formular um das Widget herum aus oder das Login-Formular über seine id |
| 403 oder ein CSRF-Fehler mit einem frischen Token | Das Absenden lief über eine neue Session, über requests.post oder über Browser.submit, daher fehlte das Cookie oder der Referer | Füllen Sie das Formular auf dem Browser-Objekt aus, das die Seite geöffnet hat, und senden Sie es mit submit_selected ab |
| Die Website meldet ein gescheitertes Captcha, obwohl CapSkip ein Token zurückgegeben hat | Ein unsichtbares Widget wurde als Checkbox gelöst, eine v3-action stimmt nicht mit der action der Seite überein, eine Enterprise-Seite wurde ohne enterprise=1 gelöst, oder das Token war älter als zwei Minuten | Übergeben Sie invisible=1, enterprise=1 oder die action der Seite, und lösen Sie direkt vor dem Absenden |
| TypeError: capskip.solver.CapSkip.solve() got multiple values for keyword argument timeout | Ein timeout wurde pro Aufruf an recaptcha übergeben | Setzen Sie stattdessen recaptchaTimeout im Konstruktor von CapSkip |
| ApiException mit ERROR_CAPTCHA_UNSOLVABLE | CapSkip hat die Aufgabe wegen seines eigenen Zeitlimits scheitern lassen, oder Sitekey und Seiten-URL gehören nicht zusammen | Senden Sie browser.url und den eigenen Sitekey der Seite, und halten Sie höchstens zehn Lösevorgänge gleichzeitig in Arbeit |
| TimeoutException nach 300 Sekunden | Die Wartezeit auf einen freien Thread hat zusammen mit dem Lösen das Limit des SDK überschritten | Begrenzen Sie den Pool auf Max. Threads, oder erhöhen Sie recaptchaTimeout im Konstruktor |
| Kein reCAPTCHA gefunden, auf einer Seite, die in einem echten Browser problemlos lädt | Die Website hat mit einem 403 geantwortet, weil sie den Standard-User-Agent ablehnt, der python-requests und MechanicalSoup nennt | Übergeben Sie beim Erzeugen des StatefulBrowser den User-Agent eines echten Browsers als user_agent |
| NetworkException bei der ersten Lösung | CapSkip läuft nicht, Host oder Port sind falsch, oder das Skript läuft auf einem anderen Rechner | Starten Sie CapSkip und prüfen Sie dann, ob es im Local-Modus oder im Server-Modus laufen soll |
FAQ
Warum MechanicalSoup statt requests allein verwenden?
Es erspart Ihnen den Nachbau des Formulars. Mit requests sammeln Sie die versteckten Felder selbst, halten die Session, lösen die action des Formulars auf und setzen den Referer, und wenn Sie auch nur eines davon vergessen, wird ein gutes Token abgelehnt. MechanicalSoup erledigt alle vier Punkte anhand der geparsten Seite, sodass Sie für das Captcha nur dieses eine Feld hinzufügen. Für Seiten, die ihre Formulare per JavaScript aufbauen, reicht keins von beiden, und ein Browser-Tool passt besser.
Bleibt die Session nach dem Captcha angemeldet?
Ja. Der StatefulBrowser behält die Cookies, die die Website nach der Anmeldung setzt, sodass die Seiten, die Sie danach mit demselben Browser-Objekt öffnen, angemeldet sind. Das vollständige Beispiel schließt sein Browser-Objekt, sobald login zurückkehrt. Verschieben Sie Ihre Folgeanfragen also in diesen with-Block. Oder erzeugen Sie das Browser-Objekt ohne with, geben Sie es zurück und rufen Sie browser.close() auf, wenn Sie fertig sind.
Kann ein MechanicalSoup-Skript auf einem VPS CapSkip auf meinem Windows-PC nutzen?
Ja. Schalten Sie CapSkip in den Verbindungseinstellungen in den Server-Modus, damit es auf einer Netzwerkadresse statt auf Loopback lauscht. Setzen Sie dann CAPSKIP_HOST dort, wo das Skript läuft; das vollständige Beispiel liest die Variable aus. Ein VPS, ein Cronjob und ein Container verbinden sich alle über dieselbe HTTP-API. Verwenden Sie eine statische öffentliche IP, wenn der Weg über das Internet führt, und fügen Sie in der Windows Firewall eine Blockierungsregel für Adressen hinzu, die den Port nicht erreichen sollen. Der Löser bleibt auf Hardware, die Ihnen gehört, und mehr Anmeldungen ändern nie etwas an dem, was Sie zahlen.
Die Kurzfassung
Die Lösung für ein MechanicalSoup-Captcha läuft auf ein einziges fehlendes Feld hinaus, denn MechanicalSoup kann das Widget nicht ausführen, das es schreibt. Öffnen Sie die Seite mit einem StatefulBrowser und wählen Sie das Formular um das Widget herum aus. Lesen Sie den Sitekey aus dem g-recaptcha-Element aus oder auf einer v3-Seite den render-Schlüssel und die action aus den Skripten der Seite, und lösen Sie mit CapSkip und browser.url. Fügen Sie das Feld g-recaptcha-response mit new_control hinzu, füllen Sie den Rest aus und rufen Sie submit_selected auf demselben Browser-Objekt auf, damit die Cookies, das CSRF-Feld und der Referer mitkommen. Lösen Sie direkt vor dem Absenden, halten Sie höchstens zehn Anmeldungen gleichzeitig in Arbeit, und schalten Sie CapSkip in den Server-Modus, wenn das Skript anderswo läuft.
- Wie reCAPTCHA v2 funktioniert und was der Solver abdeckt: die reCAPTCHA-v2-Solver-Seite.
- Alle Captcha-Typen, die das Python-Paket verarbeitet, finden Sie auf der Python-Captcha-Solver-Seite.
MechanicalSoup hält einen Scraper schlank, weil es nie einen Browser startet. Kombinieren Sie es mit einem Captcha-Löser auf Ihrem eigenen Windows-Rechner, dann bleibt die schwere Arbeit auf Hardware, die Sie selbst kontrollieren, ganz ohne Gebühren pro Lösung.
