So lösen Sie Captchas in SeleniumBase-Tests (Python)

seleniumbase captcha - How to Solve CAPTCHA in SeleniumBase Tests (Python)

SeleniumBase steuert den Browser. Es liest das Captcha nie für Sie aus. Ein Captcha-Schritt in SeleniumBase besteht deshalb aus drei Zügen: den Sitekey aus der Seite holen, bei einem Löser einen Token anfordern und diesen Token dann zurück in das Formular schreiben und absenden. Das Lösen passiert außerhalb des Browsers, weshalb derselbe Code in einem Lauf mit Fenster, in einem Headless-Lauf und in einem CI-Job funktioniert. Hier steht er in beiden Syntaxformaten, die Sie am ehesten verwenden.

Was Sie brauchen

  • Python 3.10 oder neuer, mit installiertem SeleniumBase und CapSkip-SDK.
  • Eine Seite, die tatsächlich ein Widget rendert. Die eigenen reCAPTCHA-Testschlüssel von Google werden immer akzeptiert und beweisen daher nichts über Ihren Code.
  • CapSkip läuft, entweder im Local-Modus auf der Loopback-Adresse oder im Server-Modus auf einer Maschine, die Ihr Test-Runner erreichen kann. Beides finden Sie unter Verbindungseinstellungen, und welchen Sie brauchen, hängt davon ab, wo Ihre Tests ausgeführt werden.
# Both packages, one command.
pip install seleniumbase capskip

Was UC mode leistet und was nicht

SeleniumBase bringt UC mode mit, und es lohnt sich, dessen Aufgabe genau zu benennen, denn viele greifen danach und erwarten einen Löser. UC mode reduziert die Automatisierungssignale, die ein Browser preisgibt, und seine GUI-Helfer können auf einer Cloudflare-Zwischenseite ein Kontrollkästchen anklicken. Das ist ein anderes Problem, als einen reCAPTCHA-Token zu erzeugen. Nichts an UC mode liest ein verzerrtes Bild oder wählt die Kacheln mit einem Bus darauf aus.

# pip install seleniumbase
from seleniumbase import SB

with SB(uc=True) as sb:
    # Disconnects the driver briefly so the page loads unobserved.
    sb.uc_open_with_reconnect("https://example.com/protected", reconnect_time=3)

    # Drives the real mouse pointer, so it needs a desktop session
    # or a virtual display. It clicks a checkbox; it solves nothing.
    sb.uc_gui_click_captcha()

Daraus folgen zwei Dinge. Die GUI-Helfer brauchen eine echte Anzeige, was sie für die meisten Headless-CI-Umgebungen ausschließt. Und für reCAPTCHA und GeeTest brauchen Sie weiterhin einen Token, worum es im Rest dieses Beitrags geht. Nutzen Sie beides zusammen: UC mode, damit Sie seltener eine Challenge bekommen, und einen Löser für die Challenges, die trotzdem eintreffen.

Schritt 1: den Sitekey von der Seite lesen

Der Sitekey ist öffentlich. Er steht als Attribut am Widget-Element, und SeleniumBase kann ihn mit einem einzigen Aufruf lesen, sodass Sie ihn nie pro Umgebung fest im Code hinterlegen müssen.

# pip install seleniumbase
from seleniumbase import SB

PAGE = "https://example.com/page-with-recaptcha"

with SB() as sb:
    sb.open(PAGE)

    # Present on the widget div for v2, and on the script tag for v3.
    sitekey = sb.get_attribute("[data-sitekey]", "data-sitekey")

    print(sitekey)      # this plus the page URL is all the solver needs

Schritt 2: einen Token vom Löser holen

Dieser Teil hat nichts mit SeleniumBase zu tun. Es ist ein gewöhnlicher Python-Aufruf an einen Dienst auf Ihrer eigenen Maschine, und er liefert genau die Token-Zeichenkette zurück, die die Seite erzeugt hätte, wenn ein Mensch das Widget bestanden hätte.

# pip install capskip
from capskip import CapSkip

solver = CapSkip(host="127.0.0.1", port=8080)

# v2 checkbox is the default shape. Pass invisible=1 or
# enterprise=1 for those variants, or version="v3" for v3.
result = solver.recaptcha(
    sitekey=sitekey,
    url=PAGE,
)

token = result["code"]      # the value the form is waiting for

Derselbe Client bedient die anderen Typen, die Ihnen in einer Test-Suite begegnen dürften. Turnstile ist solver.turnstile mit Sitekey und Seiten-URL, GeeTest ist solver.geetest mit den Werten gt und challenge, und ein Bild-Captcha ist solver.normal mit einem Dateipfad, einer URL oder einem base64-Data-URI. Die Python-Captcha-Solver-Seite deckt den vollen Funktionsumfang ab.

Schritt 3: den Token einfügen und absenden

Google legt eine versteckte textarea in das Formular und erwartet den Token in deren Wert. Schreiben Sie mit einem Skript-Aufruf hinein und senden Sie das Formular dann so ab, wie es die eigene Oberfläche der Seite tun würde.

# The response field is hidden, so a normal type() will not reach it.
sb.execute_script(
    "document.getElementById('g-recaptcha-response').value = arguments[0];",
    token,
)

sb.click("button[type='submit']")
sb.assert_element(".signup-success")

Wenn die Seite dem Widget eine eigene Callback-Funktion zuweist, statt das Feld beim Absenden zu lesen, rufen Sie diese Funktion mit dem Token auf, anstatt zu klicken. Was Sie brauchen, sehen Sie am Markup des Widgets: Ein data-callback-Attribut benennt die Funktion, und sein Fehlen bedeutet, dass das Formular das Feld liest.

Der vollständige Test

Hier ist das Ganze als BaseCase-Klasse, also in dem Format, das ohne zusätzliche Verkabelung in eine bestehende pytest-Suite passt.

# pip install seleniumbase capskip
from seleniumbase import BaseCase
from capskip import CapSkip

BaseCase.main(__name__, __file__)

PAGE = "https://example.com/page-with-recaptcha"


class RecaptchaTest(BaseCase):
    def test_signup_form(self):
        self.open(PAGE)
        sitekey = self.get_attribute("[data-sitekey]", "data-sitekey")

        solver = CapSkip(host="127.0.0.1", port=8080)
        token = solver.recaptcha(sitekey=sitekey, url=PAGE)["code"]

        self.execute_script(
            "document.getElementById('g-recaptcha-response').value = arguments[0];",
            token,
        )
        self.click("button[type='submit']")
        self.assert_element(".signup-success")

Lösen Sie spät. Ein reCAPTCHA-Token ist etwa zwei Minuten lang gültig; einen am Anfang eines Tests zu holen und ihn nach vier weiteren Schritten zu verwenden, ist ein Wettlauf, den Sie sporadisch verlieren. Setzen Sie das Lösen unmittelbar vor das Absenden, und genau dorthin gehört auch ein Retry.

Den Löser auf einem Server betreiben

Test-Runner sind selten die Maschine, an der Sie sitzen. CapSkip deckt das mit einem zweiten Verbindungsmodus ab, und das Einzige, was sich in Ihrem Code ändert, ist der Host.

ModusLauscht aufSinnvoll, wenn
Lokal127.0.0.1, nur dieses GerätTests und Löser laufen auf einer Maschine
ServerIhre Netzwerkadresse oder öffentliche IPEin CI-Runner, ein VPS oder ein Test-Grid greift von außen darauf zu
# pip install capskip
import os
from capskip import CapSkip

# The SDK reads these names from the environment too, so one test
# file runs unchanged on a laptop and on a shared runner.
solver = CapSkip(
    host=os.environ.get("CAPSKIP_HOST", "127.0.0.1"),
    port=int(os.environ.get("CAPSKIP_PORT", "8080")),
)

Eine statische öffentliche IP wird empfohlen, wenn die Aufrufer außerhalb Ihres eigenen Netzwerks sitzen. Der Server-Modus bleibt Ihre eigene Hardware und bleibt ohne Verbrauchsabrechnung: Er ändert, wo der Löser läuft, niemals, wem er gehört oder was eine einzelne Lösung kostet. Alle Optionen finden Sie unter Verbindungseinstellungen.

Häufige Fehler

Was Sie sehenUrsacheBeheben
NetworkExceptionAuf diesem Host und Port lauscht nichtsStarten Sie CapSkip, oder richten Sie den Host auf den Server aus, auf dem es läuft
TimeoutExceptionDas Polling lief über recaptchaTimeout hinausErhöhen Sie den Timeout, oder prüfen Sie, ob der Sitekey der echte ist
ERROR_GOOGLEKEYDer Sitekey kam nie an oder ist fehlerhaftGeben Sie aus, was get_attribute zurückgegeben hat, bevor Sie es senden
ERROR_PAGEURLDie Seiten-URL fehlt oder ist keine vollständige URLSenden Sie dieselbe absolute URL, auf der der Browser steht
Das Formular weist einen gültigen Token abDie Seite nutzt einen Callback statt des FeldesRufen Sie die data-callback-Funktion mit dem Token auf

Alle Codes, die die API zurückgeben kann, finden Sie unter der CapSkip-API-Dokumentation, samt dem jeweiligen Auslöser.

FAQ

Löst UC mode reCAPTCHA von allein?

Nein. UC mode lässt den Browser weniger automatisiert wirken und kann über die GUI-Helfer ein Kontrollkästchen auf einer Cloudflare-Zwischenseite anklicken. Keines von beidem erzeugt einen reCAPTCHA-Token. Sie holen den Token weiterhin von einem Löser und schreiben ihn in das Formular, genau wie oben gezeigt. Nutzen Sie beides: UC mode senkt, wie oft Sie eine Challenge bekommen, der Löser räumt die Challenges weg, die Sie bekommen.

Kann ich das headless in CI betreiben?

Ja, denn das Lösen berührt den Browser nie. Übergeben Sie headless an SB oder ergänzen Sie das Flag in der pytest-Kommandozeile, und die drei Schritte bleiben unverändert. Was einen Headless-Runner nicht überlebt, ist das GUI-Klicken von UC mode, das einen echten Mauszeiger bewegt und eine Desktop-Sitzung oder eine virtuelle Anzeige braucht.

Meine Tests laufen auf einem Linux-Runner. Wohin kommt der Löser?

Auf eine Windows-Maschine, die Sie kontrollieren und die im Server-Modus erreichbar ist. CapSkip ist eine Windows-Anwendung, das Muster lautet daher: eine Löser-Instanz auf einem Windows-Rechner und beliebig viele Runner, die über die API darauf zeigen. Setzen Sie CAPSKIP_HOST auf dem Runner, und sonst ändert sich am Test nichts.

Brauche ich einen Proxy, damit die Lösung zu meinem Browser passt?

Manchmal, und für reCAPTCHA, Turnstile und GeeTest wird es unterstützt. Übergeben Sie ein Proxy-Dictionary mit einem Typ und einer URI, dann läuft das Lösen darüber, was zählt, sobald die Website Wert darauf legt, dass Token und Sitzung von derselben Adresse stammen. Bild-Captchas nehmen keinen Proxy an, weil nichts an ihnen an einen Netzwerkpfad gebunden ist.

Die Kurzfassung

Lesen Sie den Sitekey mit get_attribute, holen Sie den Token, schreiben Sie ihn in das versteckte Antwortfeld, senden Sie ab. Halten Sie das Lösen im selben Schritt wie das Absenden, damit der Token frisch ist, und halten Sie den Host in einer Umgebungsvariablen, damit die Suite überall gleich läuft. Für den weiteren Selenium-Kontext siehe die Selenium-Captcha-Solver-Seite, und für die v2-Besonderheiten siehe die reCAPTCHA-v2-Solver-Seite. Bleiben noch die Kosten, und genau da tut eine Suite, die bei jedem Commit läuft, am meisten weh: lokaler Captcha-Löser bedeutet, dass jede Lösung auf Hardware stattfindet, die Ihnen bereits gehört, ohne Verbrauchsabrechnung, so oft die Tests auch feuern.