So lösen Sie Friendly Captcha in Python anhand des Script-Tags

Um Friendly Captcha in Python zu lösen, lesen Sie das Attribut data-sitekey am Element frc-captcha aus, suchen das Script-Tag, das das Widget lädt, unterscheiden anhand dieses Skripts v1 von v2 und übergeben sitekey, Seiten-URL, Version und die vollständige Adresse des Skripts an die Methode friendly_captcha von CapSkip. Sie erhalten einen Token zurück, den Sie bei v1 in frc-captcha-solution und bei v2 in frc-captcha-response senden. Entscheidend ist das Script-Tag. Friendly Captcha liefert unter einem Namen und mit einem gemeinsamen sitekey-Format zwei voneinander unabhängige Protokolle aus, und wer das falsche löst, bekommt einen gültig aussehenden Token, der stillschweigend abgelehnt wird. CapSkip hat Friendly Captcha in Version 1.4.0 hinzugefügt. Dieser Leitfaden behandelt das Auslesen der Seite, den Aufruf, das Absenden und das gleichzeitige Ausführen vieler Lösevorgänge mit AsyncCapSkip.
Was Sie brauchen
- CapSkip 1.4.0 oder neuer auf einem Windows-Rechner. Mit diesem Release kam Friendly Captcha dazu, zusammen mit CaptchaFox und Capy Puzzle.
- Python 3.10 oder neuer und Version 1.3.0 oder neuer des Pakets capskip, das erste Release mit friendly_captcha. Die Beispiele verwenden außerdem requests, um die Seite abzurufen und das Formular zu senden.
- Die URL der Seite, die das Widget anzeigt. Der sitekey, die Skriptadresse und der Feldname stammen alle aus dem HTML dieser Seite, und Schritt 1 zeigt, wo sie stehen.
- 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 Skript auf einem anderen Rechner ihn über die API aufrufen kann. Beide finden Sie unter Verbindungseinstellungen, und Schritt 4 erklärt, wann Sie wechseln sollten.
# pip install capskip requests pip install "capskip>=1.3.0" requests
Behalten Sie die Anführungszeichen bei. In cmd ist ein Größer-als-Zeichen ohne Anführungszeichen eine Umleitung: pip installiert dann ohne jede Versionsprüfung, was es unter capskip findet, und schreibt seine Ausgabe in eine Datei namens 1.3.0. PowerShell reicht das Argument unverändert durch, aber die Anführungszeichen funktionieren in jeder Shell.
Schritt 1: Das Widget und sein Script-Tag auslesen
Beide Versionen rendern dasselbe Element, ein div mit der Klasse frc-captcha und einem Attribut data-sitekey. Das Element liefert Ihnen also den sitekey, aber nichts über das Protokoll. Die Version steckt im Script-Tag, das das Widget lädt, denn v1 und v2 sind verschiedene Pakete mit unterschiedlichen Dateinamen:
<!-- v2: the @friendlycaptcha/sdk package --> <div class="frc-captcha" data-sitekey="YOUR_SITEKEY"></div> <script type="module" src="https://cdn.jsdelivr.net/npm/@friendlycaptcha/sdk/site.min.js" async defer></script> <script nomodule src="https://cdn.jsdelivr.net/npm/@friendlycaptcha/sdk/site.compat.min.js" async defer></script> <!-- v1: the friendly-challenge package --> <div class="frc-captcha" data-sitekey="YOUR_SITEKEY"></div> <script type="module" src="https://cdn.jsdelivr.net/npm/friendly-challenge/widget.module.min.js" async defer></script> <script nomodule src="https://cdn.jsdelivr.net/npm/friendly-challenge/widget.min.js" async defer></script>
Die meisten Websites legen in diesem Pfad eine Versionsnummer fest, was hier nichts ändert. Die Standardbibliothek von Python reicht aus, um all das auszulesen. Dieser Parser behält die Attribute des Widget-Elements und die Adresse jedes Skripts, das kein nomodule-Fallback ist, aufgelöst gegen die Seiten-URL:
# pip install capskip requests
from html.parser import HTMLParser
from urllib.parse import urljoin
import requests
class FriendlyPage(HTMLParser):
"""Collects the frc-captcha element and the page's script tags."""
def __init__(self, page_url):
super().__init__()
self.page_url = page_url
self.widget = {}
self.scripts = []
def handle_starttag(self, tag, attrs):
a = dict(attrs)
if "frc-captcha" in (a.get("class") or "").split():
self.widget = a
# Skip nomodule fallbacks; keep full addresses, never relative.
if tag == "script" and a.get("src") and "nomodule" not in a:
self.scripts.append(urljoin(self.page_url, a["src"]))
session = requests.Session()
page = FriendlyPage("https://example.com/signup")
page.feed(session.get(page.page_url, timeout=30).text)Der Aufruf von urljoin hat seinen Grund. Ein selbst gehostetes Widget kann ein relatives src haben, etwa /js/site.min.js, und CapSkip braucht eine Adresse, die es abrufen kann: Bei v2 lädt es genau dieses Skript in seinem eigenen Browser, um das Lösen auszuführen. Übergeben Sie nur den nackten Pfad, lädt das Widget nie.
Schritt 2: Die Version wählen und friendly_captcha aufrufen
CapSkip bestimmt die Version in einer festen Reihenfolge und hört bei der ersten Antwort auf: zuerst die Version, die Sie übergeben, dann die Skriptadresse, die Sie als module_script übergeben, dann der Standardwert v1. Dieser letzte Schritt ist die Falle. Ein Skript, aus dem CapSkip keine Version ablesen kann, etwa ein gebündeltes /assets/app.4f2a.js, wird kommentarlos als v1 gelöst, und eine v2-Website lehnt dann jeden Token ab.
Bestimmen Sie die Version also in Ihrem eigenen Code, wo Sie sich weigern können zu raten. Der Paketname in einer CDN-Adresse ist eindeutig. Ein selbst gehosteter Build lässt ihn oft weg, behält aber den Dateinamen, so wie es das offizielle WordPress-Plugin macht: site.min.js ist v2, und widget.module.min.js oder widget.min.js ist v1. Die eigenen Attribute des Widgets sind der letzte Ausweg, denn die beiden Versionen benennen ihre Optionen unterschiedlich:
from urllib.parse import urlparse
V2_FILES = {"site.min.js", "site.compat.min.js"}
V1_FILES = {"widget.module.min.js", "widget.min.js", "widget.polyfilled.min.js"}
def friendly_version(page):
# Package names are unambiguous, so check them first.
for src in page.scripts:
if "@friendlycaptcha/sdk" in src:
return "v2", src
if "friendly-challenge" in src:
return "v1", src
# Self-hosted builds usually keep the file name. Themes ship their own
# site.min.js too, so only trust a path that says friendly.
for src in [s for s in page.scripts if "friendly" in s.lower()]:
name = urlparse(src).path.rsplit("/", 1)[-1]
if name in V2_FILES:
return "v2", src
if name in V1_FILES:
return "v1", src
# Last resort: v2 and v1 name their widget options differently.
if {"data-api-endpoint", "data-form-field-name"} & page.widget.keys():
return "v2", None
if {"data-puzzle-endpoint", "data-solution-field-name"} & page.widget.keys():
return "v1", None
raise RuntimeError("v1 or v2? Read the page and set it by hand.")Die Prüfung des Dateinamens vertraut nur Pfaden, die friendly enthalten, denn ein Theme kann ein eigenes site.min.js laden, das mit dem Widget nichts zu tun hat.
Dann folgt der Aufruf. Senden Sie die gefundene Version und, falls vorhanden, die Skriptadresse. Die explizite Version hat Vorrang, und bei v2 sagt die Adresse CapSkip, dass es mit genau dem Build lösen soll, den die Website lädt. Ein Wert None wird einfach aus der Anfrage weggelassen.
from capskip import CapSkip
solver = CapSkip(host="127.0.0.1", port=8080)
version, script = friendly_version(page)
result = solver.friendly_captcha(
page.widget["data-sitekey"],
"https://example.com/signup",
version=version,
module_script=script,
# data-api-endpoint="eu" (v2) or data-puzzle-endpoint (v1).
api_server=page.widget.get("data-api-endpoint")
or page.widget.get("data-puzzle-endpoint"),
)
print(result["token"][:40]) # v2 tokens start with AQQA.Die Zeile mit api_server deckt Websites am EU-Endpunkt von Friendly Captcha ab. Auch der sitekey einer EU-Website bekommt vom globalen Endpunkt einen Token, ein Lösen dort scheitert also erst bei der Verifizierung durch die Website selbst, dasselbe stille Scheitern wie bei der Version. Ist keines der beiden Attribute vorhanden, ist der Wert None, und CapSkip verwendet den globalen Endpunkt.
Das Ergebnis ist ein einfaches dict. result["token"] ist die Zeichenfolge zum Absenden, result["code"] enthält dieselbe Zeichenfolge für Skripte, die von einem anderen Solver portiert wurden, und result["captchaId"] ist die Referenz von CapSkip für den Auftrag. Eine Version außer v1, v2, 1 oder 2, ein leerer sitekey oder eine Option, die die Methode nicht annimmt, löst eine ValidationException aus, bevor überhaupt etwas gesendet wird.
Schritt 3: Den Token in das Feld senden, das das Widget verwendet
Der Feldname ist der zweite Unterschied zwischen den Versionen, und eine Website kann ihn am Widget-Element umbenennen:
import requests
# A renamed field wins; otherwise the default for the version.
field = (
page.widget.get("data-form-field-name") # v2 rename
or page.widget.get("data-solution-field-name") # v1 rename
or ("frc-captcha-response" if version == "v2" else "frc-captcha-solution")
)
# session is the requests.Session that fetched the page.
resp = session.post(
"https://example.com/signup",
data={"email": "YOUR_EMAIL", field: result["token"]},
# Browsers send the page as Referer; some servers refuse a post without it.
headers={"Referer": page.page_url},
timeout=30,
)
print(resp.status_code)Ein Sonderfall bei v1: Ist data-solution-field-name auf einen einzelnen Bindestrich gesetzt, schreibt das Widget überhaupt kein verstecktes Feld, und das eigene Skript der Website sendet den Token auf anderem Weg. Bilden Sie diese Anfrage nach, wie unten beschrieben.
Senden Sie an die Adresse, auf die das Attribut action des Formulars zeigt, und nehmen Sie die übrigen Felder des Formulars mit, auch versteckte wie etwa einen CSRF-Token. Wenn Sie die Seite über eine einzige requests.Session abrufen und das Formular darüber senden, bleiben die Cookies der Website erhalten, von denen dieser CSRF-Token meist abhängt. Senden Sie die Seiten-URL außerdem als Referer: requests sendet weder einen Referer- noch einen Origin-Header, und manche Frameworks, darunter Django, lehnen einen HTTPS-Formular-POST ab, der keinen von beiden hat, egal wie korrekt Cookie und Token sind.
Senden Sie den Token mit data= im Body, niemals mit params= in der URL. Ein v2-Token ist rund sechs Kilobyte groß, genug, um das Limit eines Servers für die URL-Länge zu sprengen, während ein v1-Token aus vier durch Punkte getrennten Teilen und einigen hundert Zeichen besteht. Reichen Sie ihn unverändert weiter, ohne Kürzen oder Neukodieren. Er gilt für genau ein Absenden: Die Verifizierung von Friendly Captcha lehnt eine Antwort ab, die bereits verwendet wurde oder abgelaufen ist, lösen Sie also für jedes Formular neu.
Manche Websites senden das Formular per JavaScript mit einem JSON-Body statt als Formular-POST. Wenn das Absenden trotz korrektem Feldnamen scheitert, öffnen Sie die DevTools, senden einmal von Hand ab und übernehmen genau das, was die Seite sendet.
Schritt 4: Viele Formulare gleichzeitig und wo der Solver läuft
AsyncCapSkip ist in Python ein echter asynchroner Client auf Basis von httpx und kein Alias des blockierenden Clients, sodass ein einziger Event Loop viele Lösevorgänge gleichzeitig in Arbeit halten kann. Begrenzen Sie sie mit einem Semaphor, damit nie mehr laufen, als CapSkip gleichzeitig bearbeitet. Die Einstellung Max. Threads im Friendly-Captcha-Bereich von CapSkip steht standardmäßig auf 10.
import asyncio
from capskip import AsyncCapSkip
solver = AsyncCapSkip(host="127.0.0.1", port=8080)
limit = asyncio.Semaphore(10) # match Friendly Captcha Max. Threads
async def solve(sitekey, url, version, script):
async with limit:
r = await solver.friendly_captcha(
sitekey, url, version=version, module_script=script)
return r["token"]
async def main(jobs):
# jobs: (sitekey, url, version, script) tuples from Step 2
return await asyncio.gather(*(solve(*j) for j in jobs))Rechnen Sie mit schwankenden Lösezeiten. Friendly Captcha legt die Arbeitsmenge pro Anfrage fest und erhöht sie für Adressen, die es schon oft gesehen hat, und CapSkip löst jedes v2-Widget in einem echten Browser. Deshalb pollt die Methode mit recaptchaTimeout, standardmäßig 300 Sekunden, statt mit dem defaultTimeout von 120 Sekunden. CapSkip hat außerdem eigene Limits. In seinen Friendly-Captcha-Einstellungen darf eine Aufgabe 250 Sekunden auf einen freien Thread warten und dann 120 Sekunden mit dem Lösen verbringen, bevor CapSkip sie als gescheitert abbricht. Ein langsamer Lösevorgang braucht also ein höheres Row Timeout, und das eigene Timeout des SDK, standardmäßig 300 Sekunden oder ein timeout= pro Aufruf, muss diesen Wert plus jede Wartezeit der Aufgabe auf einen Thread abdecken. Der Semaphor oben hält diese Wartezeit nahe bei null. Wenn das Lösen über einen langen Durchlauf hinweg langsamer wird, ist meist die steigende Schwierigkeit für eine Adresse die Ursache. Übergeben Sie einen Proxy pro Anfrage als dict mit den Schlüsseln type und uri, oder konfigurieren Sie in CapSkip einen Proxy-Pool, damit nicht jeder Lösevorgang von derselben Adresse kommt.
Die Beispiele verwenden 127.0.0.1, weil das richtig ist, wenn Ihr Skript und der Solver auf demselben Rechner laufen. Sobald das Skript irgendwo anders läuft, etwa auf einem VPS, in einem Container oder auf einem CI-Runner, zeigt Loopback auf den falschen Rechner, und der erste Aufruf 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 erreichen kann. Verwenden Sie eine statische öffentliche IP, wenn der Weg über das Internet führt, zusammen mit einer Firewall-Regel für die Adressen, die Sie erwarten. Es bleibt Ihre eigene Hardware, und es bleibt ohne Abrechnung pro Lösung. Der Client liest Umgebungsvariablen nicht von selbst, lesen Sie CAPSKIP_HOST also in Ihrem Code aus und übergeben Sie den Wert, wie es das vollständige Beispiel unten tut.
Vollständiges lauffähiges Beispiel
# pip install capskip requests
import os
from html.parser import HTMLParser
from urllib.parse import urljoin, urlparse
import requests
from capskip import CapSkip
from capskip.exceptions import CapSkipError, ValidationException
PAGE_URL = "https://example.com/signup"
V2_FILES = {"site.min.js", "site.compat.min.js"}
V1_FILES = {"widget.module.min.js", "widget.min.js", "widget.polyfilled.min.js"}
class FriendlyPage(HTMLParser):
def __init__(self, page_url):
super().__init__()
self.page_url, self.widget, self.scripts = page_url, {}, []
def handle_starttag(self, tag, attrs):
a = dict(attrs)
if "frc-captcha" in (a.get("class") or "").split():
self.widget = a
if tag == "script" and a.get("src") and "nomodule" not in a:
self.scripts.append(urljoin(self.page_url, a["src"]))
def friendly_version(page):
for src in page.scripts:
if "@friendlycaptcha/sdk" in src:
return "v2", src
if "friendly-challenge" in src:
return "v1", src
for src in [s for s in page.scripts if "friendly" in s.lower()]:
name = urlparse(src).path.rsplit("/", 1)[-1]
if name in V2_FILES or name in V1_FILES:
return ("v2" if name in V2_FILES else "v1"), src
keys = page.widget.keys()
if {"data-api-endpoint", "data-form-field-name"} & keys:
return "v2", None
if {"data-puzzle-endpoint", "data-solution-field-name"} & keys:
return "v1", None
raise SystemExit("v1 or v2? Read the page and set it by hand.")
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")),
)
session = requests.Session()
page = FriendlyPage(PAGE_URL)
page.feed(session.get(PAGE_URL, timeout=30).text)
if not page.widget.get("data-sitekey"):
raise SystemExit("No frc-captcha widget in the HTML; it may be built by JS.")
version, script = friendly_version(page)
try:
result = solver.friendly_captcha(
page.widget["data-sitekey"], PAGE_URL,
version=version, module_script=script,
api_server=page.widget.get("data-api-endpoint")
or page.widget.get("data-puzzle-endpoint"),
)
except ValidationException as exc:
raise SystemExit(f"not sent: {exc}")
except CapSkipError as exc:
raise SystemExit(f"solve failed: {exc!r}")
field = (page.widget.get("data-form-field-name")
or page.widget.get("data-solution-field-name")
or ("frc-captcha-response" if version == "v2" else "frc-captcha-solution"))
# Post wherever the form's action points, with its other fields.
# Browsers send the page as Referer; some servers refuse a post without it.
resp = session.post(PAGE_URL, data={"email": "YOUR_EMAIL", field: result["token"]},
headers={"Referer": PAGE_URL}, timeout=30)
print(resp.status_code, version, field)Die Version wird einmal bestimmt und zweimal verwendet, für das Lösen und für den Feldnamen, sodass beides nie auseinanderlaufen kann. Findet der Parser kein Widget, erzeugt die Seite es meist per JavaScript, und Sie brauchen den sitekey aus der gerenderten Seite oder aus dem Skript, das es erzeugt. Alle Parameter, die der rohe Endpunkt annimmt, finden Sie in der API-Referenz zu Friendly Captcha.
Häufige Fehler und was sie bedeuten
| Was Sie sehen | Ursache | Beheben |
|---|---|---|
| Die Website lehnt einen Token ab, den CapSkip ohne Fehler zurückgegeben hat | Die falsche Version wurde gelöst, oft weil v1 stillschweigend als Standard griff | Bestimmen Sie die Version wie in Schritt 2 im Code und übergeben Sie sie |
| Abgelehnt auf einer Website, deren Widget data-api-endpoint (v2) oder data-puzzle-endpoint (v1) hat | Der Token kam vom globalen Endpunkt, und die Website nutzt den EU-Endpunkt | Übergeben Sie den Wert des Attributs als api_server |
| Der Token stimmt, aber das Absenden scheitert trotzdem | Er ist im falschen Feld gelandet, das Absenden erfolgte ohne Referer, oder die Website sendet JSON | Prüfen Sie die Attribute zum Umbenennen und senden Sie die Seiten-URL als Referer, dann bilden Sie die Anfrage mit den DevTools nach |
| Ein v2-Lösevorgang scheitert auf einer Website, die das Widget selbst hostet | module_script war ein relativer Pfad, daher wurde das Widget nie geladen | Lösen Sie src vor dem Übergeben mit urljoin auf |
| ApiException mit ERROR_CAPTCHA_UNSOLVABLE innerhalb von Sekunden, jedes Mal | Friendly Captcha lehnt den sitekey oder den Origin der Seite ab, oder im Konto des Schlüssels ist weder v2 noch der EU-Endpunkt aktiviert | Prüfen Sie sitekey, Seiten-URL und api_server; ein erneuter Versuch hilft nicht |
| ValidationException, bevor überhaupt etwas gesendet wird | Ein leerer sitekey, eine Version außer v1, v2, 1 oder 2 oder eine unbekannte Option | Prüfen Sie, ob der Parser das Widget gefunden hat, und entfernen Sie die Option, die die Meldung nennt |
| Lösevorgänge werden im Lauf eines langen Durchlaufs langsamer | Friendly Captcha erhöht die Arbeit für eine viel genutzte Adresse | Fügen Sie Proxys pro Anfrage oder einen Proxy-Pool in CapSkip hinzu |
| ApiException mit ERROR_CAPTCHA_UNSOLVABLE nach zwei Minuten oder mehr | Ein Lösevorgang lief länger als das Row Timeout von CapSkip mit 120 Sekunden oder wartete länger als das Wait Timeout von 250 Sekunden auf einen freien Thread | Halten Sie den Semaphor bei Max. Threads, fügen Sie Proxys hinzu oder erhöhen Sie Row Timeout in den Friendly-Captcha-Einstellungen |
| TimeoutException nach 300 Sekunden | Eine Aufgabe wartete auf einen freien Thread, und der Lösevorgang lief danach länger, als das SDK pollt | Halten Sie den Semaphor bei Max. Threads oder übergeben Sie ein längeres timeout= |
| NetworkException beim ersten Aufruf | CapSkip läuft nicht, oder Host und Port sind falsch | Starten Sie CapSkip und prüfen Sie dann, ob der Local-Modus oder der Server-Modus passt |
FAQ
Warum nicht nur module_script senden und CapSkip entscheiden lassen?
Das können Sie, und bei einem Skript, das von einem CDN geladen wird, funktioniert es auch, weil der Paketname in der Adresse steht. Das Risiko ist der Fallback. Nennt die Adresse keinen Build, den CapSkip erkennt, löst es v1, statt zu scheitern, und nichts im Ergebnis verrät Ihnen das. Eine Prüfung in Ihrem eigenen Code macht aus diesem stillen Standardwert eine Exception, die Sie sehen können.
Braucht das Lösen auf meiner Seite einen Browser, etwa Selenium oder Playwright?
Nein. Ihr Skript braucht nur das HTML, und das ruft requests ab. Jede Browserarbeit findet in CapSkip statt, das v2-Widgets in seinem eigenen Browser und v1-Rätsel direkt löst. Wenn Sie aus anderen Gründen ohnehin einen Browser steuern, funktioniert derselbe Aufruf; lesen Sie sitekey und Skriptadresse dann stattdessen aus der Live-Seite aus.
Kann ein Skript auf einem VPS oder in der Cloud einen Solver 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 CAPSKIP_HOST dort, wo das Skript läuft, und übergeben Sie den Wert an den Client, wie es das vollständige Beispiel tut. Ein VPS, ein Container und ein gehosteter Runner verbinden sich alle über dieselbe HTTP-API. Verwenden Sie eine statische öffentliche IP mit einer Firewall-Regel, wenn der Weg über das Internet führt. Der Solver bleibt auf Hardware, die Ihnen gehört, mehr Lösevorgänge ändern also nie etwas an dem, was Sie zahlen.
Worin unterscheidet sich das vom C#-Leitfaden?
Der rohe Endpunkt, die Optionen und die Ergebnisfelder sind in jedem CapSkip SDK gleich; nur die Schreibweise ändert sich. Der eigentliche Unterschied liegt im Code drumherum: der Parser aus der Standardbibliothek, die requests-Session, die die Cookies der Website mitführt, und AsyncCapSkip, das in Python ein eigener asynchroner Client ist, während es in .NET nur ein anderer Name für CapSkipClient ist, der ohnehin schon asynchron ist. Die .NET-Version dieses Workflows zeigt der C#-Leitfaden zu Friendly Captcha, und wie Sie in Python mehrere Lösevorgänge gleichzeitig ausführen, erklärt der Leitfaden zum parallelen Lösen.
Die Kurzfassung
Um Friendly Captcha in Python zu lösen, durchsuchen Sie die Seite nach dem Element frc-captcha und seinen Script-Tags und machen aus jedem src eine vollständige Adresse. Bestimmen Sie v1 oder v2 anhand des Paketnamens oder der eigenen Attribute des Widgets, und werfen Sie eine Exception, wenn beides nichts verrät. Rufen Sie friendly_captcha mit dem sitekey, der Seiten-URL, dieser Version und der Skriptadresse auf, bei EU-Websites zusätzlich mit api_server. Senden Sie den Token einmal ab, im Body und in dem Feld, das Version und Widget vorgeben.
- Wie der Typ funktioniert und was der Solver abdeckt: die Solver-Seite für Friendly Captcha.
- Alle anderen Captcha-Typen, die das Python-Paket löst, finden Sie auf der Python-Captcha-Solver-Seite.
Noch ein Punkt zum Volumen. Friendly Captcha verlangt für seine Schwierigkeit CPU-Zeit und kein Geld, ein arbeitsreicher Tag kostet Sie also Lösezeit auf Ihrem eigenen Rechner. Wenn ein unbegrenzter Captcha-Löser lokal läuft, setzen nur dieser Rechner und die Threads, die Sie ihm geben, Grenzen, kein Abrechnungszähler.
