So lösen Sie Captchas in Jupyter ohne Fehler bei asyncio.run

Um Captchas in einem Jupyter Notebook zu lösen, installieren Sie das Paket capskip mit %pip, damit es im eigenen Python des Kernels landet. Rufen Sie CapSkip dann aus einer Zelle heraus auf wie in einem Skript, oder verwenden Sie await mit seinem Client AsyncCapSkip direkt auf oberster Ebene einer Zelle. Was Sie nicht tun dürfen: den asynchronen Aufruf in asyncio.run() verpacken. Der Kernel betreibt bereits eine Event-Loop, daher löst asyncio.run einen RuntimeError aus, und die Sync-API von Playwright verweigert aus demselben Grund den Start. Bei Captchas in Jupyter Notebooks gibt es zwei weitere eigene Fallen: Der Stopp-Button kann einen Lösevorgang nicht immer stoppen, und unter Windows hindert ein älteres ipykernel Playwright daran, einen Browser zu starten. Dieser Leitfaden behandelt die Installation, beide Clients, Browser und Remote-Kernel, getestet mit ipykernel 7.4 und 7.3.
Was Sie brauchen
- CapSkip auf einem Windows-Rechner. Die Beispiele lösen reCAPTCHA v2, und jeder andere Typ, den das SDK unterstützt, funktioniert in einer Zelle genauso.
- Python 3.10 oder neuer und ein aktuelles Paket capskip (1.3.0 zum Zeitpunkt dieses Artikels), dazu requests und Beautiful Soup für die Seite. Playwright nur, wenn Sie einen Browser brauchen.
- Jupyter in beliebiger Form: Notebook 7, JupyterLab oder der Notebook-Editor in VS Code. Alle drei führen Ihre Zellen auf ipykernel aus, und genau daher kommt jedes Verhalten, das unten beschrieben wird.
- Eine Adresse für den Solver. Der Local-Modus antwortet auf 127.0.0.1 nur für dieses Gerät; der Server-Modus lauscht auf Ihrer Netzwerkadresse oder öffentlichen IP, damit ein Kernel auf einem anderen Rechner ihn über die API aufrufen kann. Beide finden Sie unter Verbindungseinstellungen, und der letzte Abschnitt erklärt, wann Sie den Server-Modus brauchen.
Schritt 1: Mit %pip in das Python des Kernels installieren
Führen Sie die Installation aus einer Notebook-Zelle aus, mit einem Prozentzeichen:
# pip install capskip requests beautifulsoup4, run inside the notebook: %pip install capskip requests beautifulsoup4
Das Prozentzeichen ist wichtig. Der Magic-Befehl %pip von IPython führt pip mit dem eigenen Interpreter des Kernels aus, also mit sys.executable, sodass das Paket dort landet, woher Ihre Zellen importieren. Ein Ausrufezeichen übergibt die Zeile dagegen an eine Shell, und diese führt das pip aus, das im PATH zuerst kommt. Auf einem Rechner mit mehr als einem Python sind das verschiedene Installationen. In einem Test meldete %pip im Kernel capskip 1.3.0, während das pip der Shell aus einem globalen Python 3.14 ein altes 1.0.1 ohne die neueren Captcha-Typen meldete. Prüfen Sie, welches Python und welche Version Sie bekommen haben, bevor Sie weitermachen:
import sys import capskip print(sys.executable) # the Python this kernel runs on print(capskip.__version__) # 1.3.0 or later
Wenn ein Paket beim Upgrade bereits importiert war, starten Sie den Kernel neu, damit die neue Version geladen wird. IPython gibt nach %pip denselben Hinweis aus.
Schritt 2: In einer Zelle mit dem synchronen Client lösen
Der synchrone Client braucht in einem Notebook nichts Besonderes. Rufen Sie die Seite mit einer requests.Session ab, suchen Sie das Widget und lösen Sie:
import requests
from bs4 import BeautifulSoup
from capskip import CapSkip
PAGE_URL = "https://example.com/login"
# pollingInterval caps the wait between polls. At 1 instead of the
# default 5, the stop button takes effect within about a second.
solver = CapSkip(host="127.0.0.1", port=8080, pollingInterval=1)
session = requests.Session()
page = session.get(PAGE_URL, timeout=30)
soup = BeautifulSoup(page.text, "html.parser")
widget = soup.select_one(".g-recaptcha[data-sitekey]")
form = widget.find_parent("form")
token = solver.recaptcha(sitekey=widget["data-sitekey"], url=PAGE_URL)["code"]
len(token)Senden Sie den Token dann in der nächsten Zelle ab, mit den eigenen Feldern des Formulars und derselben Session:
from urllib.parse import urljoin
def form_fields(form):
"""The fields a browser would send: hidden, text and checked boxes."""
fields = {}
for tag in form.select("input[name]"):
kind = (tag.get("type") or "text").lower()
if kind in ("submit", "button", "image", "file", "reset"):
continue
if kind in ("checkbox", "radio") and not tag.has_attr("checked"):
continue
fields[tag["name"]] = tag.get("value", "on" if kind in ("checkbox", "radio") else "")
return fields
data = form_fields(form)
data["username"] = "YOUR_USERNAME"
data["g-recaptcha-response"] = token
res = session.post(urljoin(PAGE_URL, form.get("action") or PAGE_URL), data=data,
headers={"Referer": PAGE_URL}, timeout=30)
res.status_codeDie Session trägt die Cookies der Seite bis zum Absenden, und genau das bindet das versteckte CSRF-Feld an Ihren Besuch. Der Referer-Header ersetzt den, den ein Browser sendet: requests sendet keinen, und manche Frameworks, darunter Django, lehnen einen HTTPS-Formular-POST ohne Referer oder Origin ab. Ein reCAPTCHA-Token gilt für ein einziges Absenden und etwa zwei Minuten, führen Sie die Zelle zum Absenden also direkt nach der Zelle zum Lösen aus, statt später darauf zurückzukommen.
Beim Stopp-Button unterscheidet sich ein Notebook von einem Skript. Eine Unterbrechung löst in Ihrer Zelle einen KeyboardInterrupt aus, auf einem Windows-Kernel kommt er aber erst an, wenn der aktuelle Aufruf zurückkehrt. Das SDK verbringt den größten Teil eines Lösevorgangs schlafend zwischen zwei Abfragen, daher stoppt die Zelle, wenn diese Schlafphase endet: nach bis zu fünf Sekunden beim Standardwert von pollingInterval, nach etwa einer Sekunde mit der Einstellung oben. Der Traceback sieht seltsam aus, zuerst eine NetworkException und dann der KeyboardInterrupt, der während ihrer Behandlung ausgelöst wurde. Diese NetworkException ist nur das Signal des SDK für eine noch nicht bereite Antwort.
Der schlimme Fall ist ein CapSkip, das die Verbindung annimmt und nie antwortet. Der synchrone Client setzt für seine HTTP-Aufrufe kein Timeout, unter Windows hängt die Zelle also in einem Socket-Lesevorgang fest, den keine Unterbrechung erreicht, und recaptchaTimeout hilft nicht, weil das SDK es nur zwischen zwei Abfragen prüft. Ein Neustart des Kernels befreit die Zelle, kostet aber alle Variablen. Auch das Schließen von CapSkip befreit sie und behält die Variablen, denn die abgebrochene Verbindung beendet den Lesevorgang, und die bereits gesendete Unterbrechung kommt sofort an. Eine Unterbrechung stoppt außerdem nur Ihre Zelle, nie CapSkip: Das SDK hat keinen Aufruf, um einen Auftrag abzubrechen, der verlassene Lösevorgang läuft also weiter, und sein Token wird nie gelesen.
Schritt 3: Async in einer Zelle: await auf oberster Ebene, nie asyncio.run
Der übliche Weg, asynchronen Code aus einem Skript auszuführen, scheitert in einem Notebook:
import asyncio from capskip import AsyncCapSkip asolver = AsyncCapSkip(host="127.0.0.1", port=8080, pollingInterval=1) asyncio.run(asolver.recaptcha(sitekey=widget["data-sitekey"], url=PAGE_URL)) # RuntimeError: asyncio.run() cannot be called from a running event loop
ipykernel führt jede Zelle innerhalb einer Event-Loop aus, die es bereits gestartet hat, und asyncio.run besteht darauf, eine neue zu starten. Das ältere Idiom asyncio.get_event_loop().run_until_complete() scheitert ebenfalls, mit RuntimeError: This event loop is already running. Keiner der beiden Aufrufe erreicht jemals CapSkip, es wird also kein Auftrag eingereicht. Die übergebene Coroutine läuft nie, daher gibt eine spätere Zelle womöglich eine RuntimeWarning aus, dass sie nie awaited wurde; das ist harmlos. Die Abhilfe ist einfacher als das Problem. In IPython können Sie await auf oberster Ebene einer Zelle verwenden, außerhalb jeder Funktion:
# No asyncio.run: the kernel's loop runs this await. from capskip import AsyncCapSkip asolver = AsyncCapSkip(host="127.0.0.1", port=8080, pollingInterval=1) result = await asolver.recaptcha(sitekey=widget["data-sitekey"], url=PAGE_URL) token = result["code"]
Damit ist AsyncCapSkip der bessere Client für ein Notebook, aus zwei Gründen, die über den Stil hinausgehen. Eine Unterbrechung bricht das await sofort ab, die Zelle endet also umgehend statt erst nach der nächsten Abfrage. Und seine HTTP-Aufrufe laufen über httpx, das bei einer stummen Verbindung standardmäßig nach fünf Sekunden aufgibt. Ein CapSkip, das nicht mehr antwortet, beendet den Aufruf dann mit einem SDK-Fehler statt mit einem Hänger: mit einer TimeoutException kurz nach Ablauf von recaptchaTimeout, wenn eine Abfrage hängt, oder mit einer NetworkException nach fünf Sekunden, wenn das Einreichen hängt. Der Stopp-Button funktioniert dabei durchgehend. Setzen Sie recaptchaTimeout im Konstruktor, um diese Wartezeit zu verkürzen; recaptcha() nimmt im Python-SDK kein timeout-Argument an, und wer eines übergibt, bekommt einen TypeError.
Mehrere Lösevorgänge gleichzeitig sind nur ein gather entfernt. CapSkip löst reCAPTCHA standardmäßig auf 10 Threads (Max. Threads in seinen Einstellungen), und eine Aufgabe, die dahinter in der Warteschlange steht, wartet bis zu 250 Sekunden, bevor CapSkip sie als gescheitert abbricht. Begrenzen Sie den Stapel also:
import asyncio
# Max. Threads for reCAPTCHA is 10 by default; more than that queues.
limit = asyncio.Semaphore(10)
async def solve(sitekey, url):
async with limit:
return (await asolver.recaptcha(sitekey=sitekey, url=url))["code"]
# jobs: (sitekey, url) pairs; here the Step 2 page three times
jobs = [(widget["data-sitekey"], PAGE_URL)] * 3
tokens = await asyncio.gather(*(solve(k, u) for k, u in jobs), return_exceptions=True)gather liefert alle Tokens erst am Ende zurück, senden Sie sie also zügig ab oder wechseln Sie zu asyncio.as_completed und senden Sie jeden Token ab, sobald er eintrifft. Mit return_exceptions=True kommt ein gescheiterter Lösevorgang als seine Exception in der Liste zurück, statt aus der Zelle heraus ausgelöst zu werden, senden Sie also nur die Einträge ab, die Strings sind. Die Lösevorgänge eines Stapels teilen sich einen CapSkip-Rechner, rechnen Sie also damit, dass jeder langsamer läuft als ein einzelner Lösevorgang. Der Client baut außerdem für jede Abfrage einen neuen HTTP-Client auf, was unter Windows jeweils etwa eine Zehntelsekunde CPU-Zeit des Kernels kostet, ein großer Stapel hält also einen Prozessorkern beschäftigt. Die Muster für größere Stapel finden Sie im Leitfaden zum parallelen Lösen von Captchas in Python.
Ältere Antworten auf den Fehler von asyncio.run empfehlen nest_asyncio. Lassen Sie es weg. Das Projekt wurde im März 2024 archiviert. Unter Python 3.14 legt seine Anwendung den Kernel lahm: Ab der nächsten Zelle scheitert jeder httpx-Aufruf, auch der von AsyncCapSkip, mit einem TypeError über eine schwache Referenz auf ein NoneType-Objekt, bis Sie neu starten. Top-Level-await braucht überhaupt keinen Patch.
Schritt 4: Ein Browser im Notebook
Selenium hat keine eigene Event-Loop, eine webdriver-Session funktioniert in einer Zelle also genau wie in einem Skript: Lösen Sie mit dem synchronen Client aus Schritt 2 und schreiben Sie den Token in die Seite. Die Sync-API von Playwright prüft, ob eine Loop läuft, und bricht in diesem Fall ab mit Error: It looks like you are using Playwright Sync API inside the asyncio loop. Please use the Async API instead. In einem Notebook steuern Sie Playwright also über seine Async-API, mit await auf oberster Ebene:
# pip install playwright into the kernel, then download a browser once:
%pip install playwright
import sys
!"{sys.executable}" -m playwright install chromiumfrom playwright.async_api import async_playwright
# start() instead of "async with", so the browser outlives this cell.
pw = await async_playwright().start()
browser = await pw.chromium.launch()
page = await browser.new_page()
await page.goto(PAGE_URL)
sitekey = await page.locator(".g-recaptcha").get_attribute("data-sitekey")
token = (await asolver.recaptcha(sitekey=sitekey, url=page.url))["code"]
# The widget's hidden textarea is the field the form submits.
await page.locator("#g-recaptcha-response").evaluate("(el, t) => { el.value = t; }", token)
await page.locator("form button[type=submit]").click()Wenn Sie pw und browser in Variablen behalten, können Sie in den nächsten Zellen an der Seite herumprobieren, und genau darum geht es bei einem Notebook größtenteils. Schließen Sie beide, wenn Sie fertig sind, mit await browser.close() und await pw.stop(). Der Aufruf über das eigene sys.executable des Kernels passt den Browser an das Playwright an, das Ihre Zellen importieren, aus demselben Grund wie bei %pip. Die Anführungszeichen halten einen Pfad mit Leerzeichen zusammen, und die Zeile mit dem Ausrufezeichen zeigt den Download-Fortschritt in der Zelle an. Mehr zur Steuerung des Browsers selbst finden Sie im Leitfaden zum Playwright-Captcha-Solver.
Prüfen Sie unter Windows Ihre ipykernel-Version, bevor Sie Playwright debuggen. Bis 7.3 führte ipykernel Zellen dort auf einer Selector-Event-Loop aus, die keine asyncio-Subprozesse starten kann, daher scheitert chromium.launch() mit einem nackten NotImplementedError. Version 7.4.0, Ende September 2026 erschienen, hat Windows-Zellen auf die Proactor-Loop umgestellt, und dieselbe Zelle funktioniert. Geben Sie ipykernel.__version__ aus, um zu sehen, welche Version Sie haben. Für ein Upgrade führen Sie im Notebook %pip install -U ipykernel aus und starten den Kernel neu. Wenn Sie kein Upgrade durchführen können, hilft auch ein Worker-Thread nicht, denn die Selector-Policy gilt für den gesamten Kernel-Prozess. Steuern Sie den Browser dann stattdessen mit Selenium, das chromedriver ohne asyncio startet.
Wenn der Kernel nicht auf Ihrem PC läuft: Server-Modus
Alles oben setzt voraus, dass der Kernel auf dem Rechner mit CapSkip läuft, sodass 127.0.0.1 es erreicht. Das ist das übliche Setup für Captchas in Jupyter Notebooks, aber nicht das einzige. Ein Notebook, das JupyterHub auf einem Server bereitstellt, ein Remote-Kernel in VS Code oder ein gehostetes Notebook wie Google Colab führt Ihre Zellen woanders aus, und dort bedeutet 127.0.0.1 eben diesen Rechner. Der erste Aufruf scheitert dann mit einer NetworkException und einer abgewiesenen Verbindung. Schalten Sie CapSkip in den Verbindungseinstellungen in den Server-Modus, dann lauscht es auf Ihrer Netzwerkadresse oder öffentlichen IP, und der Remote-Kernel ruft es über dieselbe API mit dieser Adresse als Host auf.
Verwenden Sie eine statische öffentliche IP, wenn der Weg über das Internet führt. CapSkip öffnet seinen Port beim Start des Server-Modus für jede Adresse. Um ihn auf die Rechner zu beschränken, die Sie erwarten, fügen Sie in der Windows Firewall eine Blockierregel für alle anderen hinzu. Das funktioniert, wenn die Adresse des Kernels feststeht, etwa auf Ihrem eigenen JupyterHub-Server. Die ausgehende Adresse eines gehosteten Notebooks kann sich zwischen Sitzungen ändern, aktivieren Sie dort daher die Validierung des API-Schlüssels in den Einstellungen von CapSkip und übergeben Sie den Schlüssel als apiKey. Es bleibt Ihre eigene Hardware, und es bleibt ohne Abrechnung pro Lösung. Der Client liest von sich aus keine Umgebungsvariablen, lesen Sie CAPSKIP_HOST also in einer Zelle mit os.getenv aus und übergeben Sie den Wert, wie es das vollständige Beispiel tut.
Vollständiges lauffähiges Beispiel
Drei Zellen, durchgehend asynchron: der kürzeste vollständige Workflow für Captchas in Jupyter Notebooks, der den Stopp-Button übersteht.
# Cell 1: pip install capskip requests beautifulsoup4, then restart the kernel. %pip install capskip requests beautifulsoup4
# Cell 2: the client and a helper. Run once per kernel.
import os
from urllib.parse import urljoin
import requests
from bs4 import BeautifulSoup
from capskip import AsyncCapSkip, CapSkipError
PAGE_URL = "https://example.com/login"
solver = AsyncCapSkip(
apiKey=os.getenv("CAPSKIP_API_KEY", "capskip"),
host=os.getenv("CAPSKIP_HOST", "127.0.0.1"),
port=int(os.getenv("CAPSKIP_PORT", "8080")),
pollingInterval=1,
)
def form_fields(form):
fields = {}
for tag in form.select("input[name]"):
kind = (tag.get("type") or "text").lower()
if kind in ("submit", "button", "image", "file", "reset"):
continue
if kind in ("checkbox", "radio") and not tag.has_attr("checked"):
continue
fields[tag["name"]] = tag.get("value", "on" if kind in ("checkbox", "radio") else "")
return fields# Cell 3: fetch, solve, post. Run it again for a fresh token.
session = requests.Session()
page = session.get(PAGE_URL, timeout=30)
soup = BeautifulSoup(page.text, "html.parser")
widget = soup.select_one(".g-recaptcha[data-sitekey]")
if widget is None:
raise RuntimeError("No reCAPTCHA widget in the HTML; the page may build it with JavaScript.")
form = widget.find_parent("form")
try:
result = await solver.recaptcha(sitekey=widget["data-sitekey"], url=PAGE_URL)
except CapSkipError as err:
raise RuntimeError(f"solve failed: {type(err).__name__}: {err}") from err
data = form_fields(form)
data["username"] = "YOUR_USERNAME"
data["g-recaptcha-response"] = result["code"]
res = session.post(urljoin(PAGE_URL, form.get("action") or PAGE_URL), data=data,
headers={"Referer": PAGE_URL}, timeout=30)
print(res.status_code)Zelle 3 ist die, die Sie erneut ausführen, und jeder Durchlauf ist ein neuer Lösevorgang mit einem neuen Token, denn ein reCAPTCHA-Token funktioniert nur einmal. Wenn die Seite ihr Widget per JavaScript erzeugt, enthält das abgerufene HTML keinen sitekey; verwenden Sie dann stattdessen die Playwright-Zellen aus Schritt 4, oder lesen Sie den sitekey aus dem Skript, das das Widget rendert.
Häufige Fehler und was sie bedeuten
| Was Sie sehen | Ursache | Beheben |
|---|---|---|
| RuntimeError: asyncio.run() cannot be called from a running event loop | Der Kernel betreibt bereits eine Event-Loop | Verwenden Sie await für den Aufruf auf oberster Ebene der Zelle |
| RuntimeError: This event loop is already running | run_until_complete wurde auf der eigenen Loop des Kernels aufgerufen | Verwenden Sie await für den Aufruf auf oberster Ebene der Zelle |
| Playwright meldet, dass Sie die Sync-API innerhalb der asyncio-Loop verwenden | Die Sync-API verweigert die Ausführung, solange eine Loop läuft | Verwenden Sie playwright.async_api mit await; ab ipykernel 7.4 funktioniert es auch, den synchronen Code in einem Worker-Thread auszuführen |
| NotImplementedError von chromium.launch() unter Windows | ipykernel vor 7.4.0 führt Zellen auf einer Selector-Loop aus, die den Browserprozess nicht starten kann | Führen Sie %pip install -U ipykernel aus und starten Sie dann den Kernel neu |
| ModuleNotFoundError für capskip oder eine ältere Version als die installierte | Das pip der Shell hat in ein anderes Python installiert als das des Kernels | Installieren Sie mit %pip und prüfen Sie sys.executable |
| Der Stopp-Button bewirkt nichts, und die Zelle läuft weiter | Der synchrone Client wartet auf ein CapSkip, das die Verbindung angenommen und nie geantwortet hat, und er setzt kein HTTP-Timeout | Schließen Sie CapSkip oder starten Sie den Kernel neu, und bevorzugen Sie in Notebooks AsyncCapSkip |
| Ein TypeError über eine schwache Referenz bei jedem Aufruf von httpx oder AsyncCapSkip, Python 3.14 | nest_asyncio wurde in einer früheren Zelle angewendet | Starten Sie den Kernel neu und verzichten Sie auf nest_asyncio; Top-Level-await braucht keinen Patch |
| NetworkException mit abgewiesener Verbindung | CapSkip läuft nicht, oder der Kernel läuft auf einem anderen Rechner | Starten Sie CapSkip, oder schalten Sie es in den Server-Modus und übergeben Sie seine Adresse |
| ApiException mit ERROR_CAPTCHA_UNSOLVABLE | CapSkip hat den Lösevorgang als gescheitert abgebrochen, zum Beispiel nach 250 Sekunden Warten auf einen freien Thread | Halten Sie Stapel bei Max. Threads, fügen Sie Proxys hinzu und versuchen Sie es dann erneut |
| Die Website lehnt einen Token ab, den CapSkip zurückgegeben hat | Das Absenden erfolgte ohne die Cookies der Seite, ihre versteckten Felder oder einen Referer, oder der Token war älter als zwei Minuten | Senden Sie direkt nach dem Lösen über dieselbe requests.Session ab, mit form_fields und einem Referer |
FAQ
Funktioniert das in JupyterLab und in Notebooks in VS Code?
Ja. JupyterLab, Notebook 7 und der Notebook-Editor in VS Code übergeben Ihre Zellen alle an ipykernel, daher verhalten sich Top-Level-await, der Fehler von asyncio.run und die Event-Loop unter Windows überall gleich. Unterschiedlich ist, welches Python der Kernel verwendet, prüfen Sie also sys.executable in dem Editor, den Sie gerade öffnen.
Kann ein gehostetes Notebook wie Google Colab den Solver auf meinem PC nutzen?
Ja, über den Server-Modus. Ein gehostetes Notebook führt seinen Kernel auf den Rechnern des Anbieters aus, 127.0.0.1 ist dort also nicht Ihr PC. Schalten Sie CapSkip in den Verbindungseinstellungen in den Server-Modus, geben Sie dem PC eine statische öffentliche IP, aktivieren Sie die Validierung des API-Schlüssels und übergeben Sie Ihre Adresse und den Schlüssel an den Client. Die ausgehende Adresse eines gehosteten Notebooks kann sich ändern, deshalb ist es der Schlüssel, der Fremde fernhält; eine Blockierregel in der Windows Firewall kann den Port trotzdem für Adressbereiche schließen, die Sie nie verwenden. Der Solver bleibt auf Hardware, die Ihnen gehört, eine lange Sitzung mit vielen erneuten Durchläufen ändert also nie etwas an dem, was Sie zahlen.
Sollte ich nest_asyncio verwenden, damit asyncio.run funktioniert?
Nein. Es patcht die laufende Loop, damit sich asyncio.run darin verschachteln lässt, und das war eine verbreitete Lösung für diesen Fehler. Das Projekt ist archiviert, und unter Python 3.14 macht der Patch jeden späteren httpx-Aufruf im Kernel kaputt, auch den von AsyncCapSkip. Top-Level-await leistet bereits, was Sie von asyncio.run wollten, ohne irgendetwas an der Loop zu ändern.
Was passiert mit dem Lösevorgang, wenn ich eine Zelle unterbreche?
Ihre Zelle stoppt, und CapSkip arbeitet weiter. Das SDK hat keinen Aufruf, um einen Auftrag abzubrechen, CapSkip schließt den Lösevorgang also ab, und niemand liest den Token. Führen Sie die Zelle erneut aus, wird ein neuer Auftrag eingereicht. Wenn Sie einen großen Stapel unterbrechen, halten die verlassenen Lösevorgänge die Threads von CapSkip beschäftigt, bis jeder einzelne fertig ist oder in die eigenen Timeouts von CapSkip läuft, was Minuten dauern kann. Ein sofort gestarteter neuer Stapel wartet daher hinter ihnen.
Die Kurzfassung
Captchas in Jupyter Notebooks zu lösen läuft auf fünf Gewohnheiten hinaus. Installieren Sie mit %pip, nie über die Shell. Der synchrone CapSkip-Client funktioniert in einer Zelle wie in einem Skript; setzen Sie pollingInterval auf 1, damit Unterbrechungen schnell ankommen. Für asynchronen Code verwenden Sie await mit AsyncCapSkip auf oberster Ebene der Zelle und verpacken den Aufruf nie in asyncio.run und greifen auch nie zu nest_asyncio. Für einen Browser verwenden Sie Selenium unverändert oder die Async-API von Playwright, und unter Windows ipykernel 7.4 oder neuer. Wenn der Kernel auf einem anderen Rechner läuft, schalten Sie CapSkip in den Server-Modus und übergeben seine Adresse.
- Alle Captcha-Typen, die das Python-Paket löst, finden Sie auf der Python-Captcha-Solver-Seite.
- Wie das Widget selbst funktioniert und was der Solver abdeckt: die reCAPTCHA-v2-Solver-Seite.
In einem Notebook entsteht ein Scraper mit jedem erneuten Durchlauf ein Stück weiter. Mit einer Captcha-Umgehung auf Ihrem eigenen Rechner ist jeder dieser Durchläufe kostenlos.
