So lösen Sie Captchas in Huey-Tasks mit SQLite unter Windows

huey captcha - How to Solve CAPTCHAs in Huey Tasks With SQLite on Windows

Um Captchas in Huey zu lösen, halten Sie die Queue in einer SqliteHuey-Datei neben Ihrem Task-Modul, rufen CapSkip in einem Task mit Wiederholungen auf und starten den Consumer mit Thread-Workern. Unter Windows ist das schon der ganze Stack: eine SQLite-Datei, kein Redis-Server und der Solver auf demselben PC. Dieses Huey-Captcha-Setup hat vier Fallen, und dieser Leitfaden behandelt jede davon. Der Consumer ist eine exe, nicht huey_consumer.py. Die Option timeout von Huey bewirkt bei Thread-Workern nichts, sodass ein einziger hängender Lösevorgang einen Worker für immer blockiert. Mit den Standardwerten meldet das Lesen eines Ergebnisses den ersten gescheiterten Versuch, während Huey es noch erneut versucht. Und CancelExecution nimmt retry als erstes Argument, sodass der naheliegende Weg, die Wiederholungen zu stoppen, stattdessen zu endlosen Wiederholungen führen kann.

Was Sie brauchen

  • CapSkip auf einem Windows-Rechner, mit eingeschaltetem reCAPTCHA-Löser.
  • Python 3.10 oder neuer, die Mindestversion des CapSkip SDK. Huey 3.4.0 gibt selbst keine Mindestversion an. Alles Folgende wurde unter Python 3.13.7 und 3.14.0 mit huey 3.4.0 und capskip 1.3.0 ausgeführt.
  • Kein Redis. SqliteHuey hält Queue, Zeitplan, Locks und Ergebnisse in einer einzigen SQLite-Datei.
  • Eine Adresse für den Solver. 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 Consumer auf einem anderen Rechner es über die API aufrufen kann. Eingestellt werden beide Modi unter Verbindungseinstellungen.

Schritt 1: Huey und das CapSkip SDK installieren

Erstellen Sie in Ihrem Projektordner eine virtuelle Umgebung und installieren Sie drei Pakete. Das Paket requests wird vom vollständigen Beispiel verwendet, um die Login-Seite abzurufen und abzusenden, und vom Timeout-Fix weiter unten.

# Windows, in PowerShell or cmd: a virtual environment in your project folder
python -m venv .venv
.venv\Scripts\python -m pip install huey capskip requests

Schritt 2: Die Queue und den Solver-Client erstellen

Alles, was Huey braucht, steht in tasks.py. Der Consumer importiert die Datei, um die Tasks zu finden, und jedes Skript, das Arbeit in die Queue stellt, importiert sie ebenfalls.

# tasks.py
import os
from pathlib import Path

from capskip import CapSkip
from huey import SqliteHuey

# The whole queue is one SQLite file. Keep it next to this module, so the
# consumer and every script that imports tasks use the same file, whatever
# folder they are started from.
huey = SqliteHuey(
    "captcha",
    filename=str(Path(__file__).with_name("huey.db")),
    store_intermediate_errors=False,  # get() waits for the last retry
)

# Local mode: CapSkip on this PC. Server mode: set CAPSKIP_HOST (and
# CAPSKIP_API_KEY if key validation is on) to reach it on another machine.
solver = CapSkip(
    apiKey=os.environ.get("CAPSKIP_API_KEY", "capskip"),
    host=os.environ.get("CAPSKIP_HOST", "127.0.0.1"),
    port=int(os.environ.get("CAPSKIP_PORT", "8080")),
)

Geben Sie die Datenbankdatei wie oben mit einem vollständigen Pfad an. Der Standarddateiname ist huey.db in dem Ordner, aus dem der jeweilige Prozess gestartet wurde. Ein Skript, das aus einem anderen Ordner gestartet wird, legt dann seine eigene leere huey.db an, stellt seinen Task dort in die Queue und wartet auf ein Ergebnis, das nie kommt. Wenn Sie die Datei per festem Pfad neben tasks.py legen, bekommt jeder Prozess dieselbe Queue. Während der Consumer läuft, liegen zwei Hilfsdateien daneben, huey.db-wal und huey.db-shm. Sie gehören zum Write-Ahead-Log von SQLite, sind normal und können nach einem harten Stopp liegen bleiben.

SQLite hat die Last ohne Fehler bewältigt. Zwölf Worker und drei Skripte, die zusammen 600 Tasks in die Queue stellten, bekamen keinen Fehler, dass die Datenbank gesperrt sei. Innerhalb jedes Prozesses teilt Huey eine Verbindung hinter einem Lock, und zwischen Prozessen wartet SQLite bis zu 5 Sekunden auf die Datei. Eines sollten Sie über die Datei wissen: Task-Argumente und Ergebnisse werden per Pickle als einfache Bytes darin abgelegt. Ein als Argument übergebenes Passwort war in der Tabelle lesbar, halten Sie Geheimnisse also in der Umgebung oder in tasks.py, nicht in Task-Argumenten.

Schritt 3: Den Huey-Captcha-Task schreiben und festlegen, was wiederholt wird

Jede Exception, die ein Huey-Task auslöst, verbraucht eine Wiederholung. Bei manchen Fehlschlägen von CapSkip ist das richtig, bei anderen Verschwendung, deshalb sortiert der Task sie.

from capskip import ApiException, ValidationException
from huey import CancelExecution


# Any exception uses one of the two retries, 15 s apart: NetworkException
# when CapSkip is not running, TimeoutException, an unsolvable task.
@huey.task(retries=2, retry_delay=15)
def solve_recaptcha(sitekey, page_url):
    try:
        return solver.recaptcha(sitekey=sitekey, url=page_url)["code"]
    except (ApiException, ValidationException) as exc:
        if "ERROR_CAPTCHA_UNSOLVABLE" in str(exc):
            raise  # this attempt failed; a fresh one may not
        # ERROR_GOOGLEKEY, ERROR_PAGEURL, ERROR_BAD_PARAMETERS and
        # ERROR_KEY_DOES_NOT_EXIST fail the same way on every attempt.
        # CancelExecution's first argument is retry: False ends the task now.
        raise CancelExecution(False, f"{type(exc).__name__}: {exc}") from exc

Drei Fehlschläge sind einen weiteren Versuch wert: eine NetworkException, wenn CapSkip noch nicht läuft, eine TimeoutException und ERROR_CAPTCHA_UNSOLVABLE, womit CapSkip einen Lösevorgang meldet, der diesmal nicht geklappt hat. Der Consumer loggt den Fehler mit seinem Traceback und dann eine Zeile, laut der er den Task erneut in die Queue stellt, samt der Zahl der verbleibenden Wiederholungen. Mit einem retry_delay von 15 Sekunden startete der nächste Versuch in unseren Durchläufen 16,1 Sekunden nach dem Fehlschlag.

Ein fehlerhafter sitekey, eine Seiten-URL ohne http oder https oder ein falscher API-Schlüssel (bei aktivierter Schlüsselvalidierung) wird abgelehnt, bevor ein Lösevorgang beginnt, mit ERROR_GOOGLEKEY, ERROR_PAGEURL oder ERROR_KEY_DOES_NOT_EXIST. Ein leerer sitekey oder eine leere Seiten-URL ergibt ERROR_BAD_PARAMETERS. Diese Fehler treten jedes Mal gleich auf, eine Wiederholung verschwendet also nur Zeit. CancelExecution von Huey beendet einen Task nur dann ohne Wiederholung, wenn retry False ist. Lösen Sie sie ohne Argumente aus, wird der Task trotzdem wiederholt, solange er noch Wiederholungen übrig hat. Lesen Sie auch die Signatur: Das erste Positionsargument ist retry, nicht die Meldung. Schreiben Sie CancelExecution nur mit einer Meldung, wird die Meldung zum Wert von retry und gilt als wahr. Das Log meldet dann, dass der Task wiederholt wird, und der Task läuft wieder und wieder, bis Sie den Consumer stoppen. Die Meldung zusammen mit retry=False zu übergeben löst einen TypeError aus, weil retry dann zwei Werte bekäme. Übergeben Sie False zuerst, wie es der Code tut. Der Consumer loggt dann ein WARNING, dass der Task CancelExecution ausgelöst hat und nicht wiederholt wird.

Diese Sortierung hat eine Grenze: Ein sitekey, den Google selbst ablehnt, kommt trotzdem als ERROR_CAPTCHA_UNSOLVABLE bei Ihnen an, weil CapSkip jeden gescheiterten Lösevorgang so meldet. Dieser Fall kostet drei Versuche, bevor er scheitert. Wenn eine Seite jedes Mal scheitert, prüfen Sie zuerst ihren sitekey.

Schritt 4: Den Consumer unter Windows starten

Starten Sie den Consumer aus dem Ordner, der tasks.py enthält. Eine Aktivierung der virtuellen Umgebung ist nicht nötig, wenn Sie die exe über ihren Pfad aufrufen.

# In the folder that holds tasks.py, in PowerShell or cmd
.venv\Scripts\huey_consumer tasks.huey -w 4

Ältere Anleitungen, und auch der Nutzungshinweis des Consumers selbst, nennen huey_consumer.py. Huey 3 installiert nur huey_consumer.exe, daher scheitert dieser Name in PowerShell und in cmd mit der Meldung, er werde nicht erkannt. Der Befehl oben funktioniert in beiden, ebenso .venv\Scripts\python -m huey.bin.huey_consumer tasks.huey. Aus einem anderen Ordner gestartet, bricht der Consumer mit Error importing tasks.huey und ModuleNotFoundError: No module named tasks ab, weil er nur den aktuellen Ordner zu seinem Suchpfad hinzufügt.

Thread-Worker sind unter Windows die einzige echte Wahl. Mit -k process beendet sich der Consumer sofort mit der Fehlermeldung huey cannot be run in process-mode on Windows, weil Prozess-Worker fork() brauchen, das es unter Windows nicht gibt. Greenlet-Worker setzen ein installiertes gevent voraus. Threads passen ohnehin zu dieser Aufgabe, denn ein Worker verbringt fast den gesamten Lösevorgang damit, auf CapSkip zu warten.

Jeder Worker sendet seine eigene Anfrage, daher ist die Zahl der Worker auch die Zahl der Lösevorgänge, die CapSkip gleichzeitig erhält. Mit 12 Workern und 12 Tasks in der Queue erreichten alle 12 Anfragen CapSkip innerhalb von 30 Millisekunden. Der reCAPTCHA-Bereich von CapSkip führt standardmäßig 10 Lösevorgänge gleichzeitig aus, festgelegt über Max. Threads, und stellt den Rest in eine Warteschlange. Vier Worker reichen für ein paar Logins. Erhöhen Sie -w in Richtung dieses Limits, wenn die Queue lang ist.

Schritt 5: Einen Lösevorgang in die Queue stellen und das Ergebnis einmal lesen

Der Aufruf des Tasks führt ihn nicht aus. Er schreibt den Task in die Queue und gibt ein Result-Handle zurück, auf das Sie warten können.

# enqueue.py, next to tasks.py: .venv\Scripts\python enqueue.py
from huey.exceptions import ResultTimeout, TaskException

from tasks import huey, solve_recaptcha

result = solve_recaptcha("YOUR_SITEKEY", "https://example.com/login")
try:
    # Three attempts of up to about 5.5 minutes each, plus two 15 s delays.
    token = result.get(blocking=True, timeout=1100)
except TaskException as exc:
    print("solve failed:", exc)
except ResultTimeout:
    print("no result yet: is the consumer running?")
else:
    print("token:", token[:30], "...")
    # Reading the result deleted it from huey.db. This handle keeps its
    # copy, but a new handle for the same task id finds nothing.
    print("read again:", huey.result(result.id))  # None

Das Warten von 1100 Sekunden deckt den schlimmsten Fall ab: drei Versuche, die jeweils bis zum Limit des SDK laufen, plus zwei Wartezeiten zwischen den Wiederholungen. Ein gescheiterter Task löst TaskException aus, deren Meldung der letzte Fehler ist und deren Metadaten die verbleibenden Wiederholungen und den Traceback enthalten. Antwortet nichts rechtzeitig, erhalten Sie ResultTimeout, was meist bedeutet, dass der Consumer nicht läuft oder eine andere Datenbankdatei verwendet.

Hier zählt die Zeile mit store_intermediate_errors aus Schritt 2. Mit dem Standardwert von Huey wird jeder gescheiterte Versuch als Ergebnis gespeichert. In unserem Test löste get() nach dem ersten Fehlschlag TaskException aus, während Huey die Wiederholung ausführte. Das Ergebnis der Wiederholung war erst sichtbar, nachdem reset() auf dem Handle aufgerufen wurde. Ist die Option ausgeschaltet, wartet get() auf den letzten Versuch.

Ein Ergebnis lässt sich nur einmal lesen. Das Lesen löscht es aus der Datei, so wie CapSkip es mit seinen eigenen Antworten tut. Das Handle, das es gelesen hat, behält seine Kopie, aber huey.result() mit derselben Task-ID gibt danach None zurück. Übergeben Sie preserve=True an get(), wenn zwei Teile Ihres Codes den Wert brauchen. Ein Task, der None zurückgibt, speichert überhaupt nichts, das Warten darauf endet also in ResultTimeout.

Warum ein hängender Lösevorgang ein eigenes Timeout braucht

Huey 3 akzeptiert eine Option timeout an einem Task, aber bei Thread-Workern bewirkt sie nichts. Ein Thread lässt sich nicht von außen unterbrechen, daher prüft Huey das Timeout nur, wenn der Task selbst task.check_timeout() aufruft. Ein Task mit timeout=2, der fünf Sekunden schlief, endete ganz normal. Prozess-Worker, bei denen sich ein Timeout durchsetzen ließe, werden unter Windows abgelehnt.

Das ist wichtig, weil das CapSkip SDK für seine Anfragen kein HTTP-Timeout setzt. Wenn CapSkip eine Verbindung annimmt und nie antwortet, wartet der Aufruf für immer. In unserem Test hingen zwei Worker in diesem Zustand fest, ohne eine einzige Logzeile, und die Tasks hinter ihnen in der Queue starteten nie. Sie hingen auch 45 Sekunden, nachdem sich der Solver erholt hatte, noch fest. Die Lösung ist, den HTTP-Aufrufen des SDK ein Timeout zu geben. Setzen Sie das in tasks.py auf Modulebene, damit es vor jedem Lösevorgang läuft.

import functools
import types

import requests
import capskip.api

# capskip 1.3.0 calls requests.post/get with no timeout, and Huey cannot stop
# a thread worker that is stuck in one. Give every SDK call 5 s to connect
# and 30 s to answer. Module level in tasks.py, so it runs before any solve.
capskip.api.requests = types.SimpleNamespace(
    RequestException=requests.RequestException,
    post=functools.partial(requests.post, timeout=(5, 30)),
    get=functools.partial(requests.get, timeout=(5, 30)),
)

Damit löste eine hängende Übermittlung nach 30,0 Sekunden eine NetworkException aus, und der Task wurde wiederholt. Eine hängende Statusabfrage dauert länger, weil das SDK eine gescheiterte Abfrage als noch nicht bereit behandelt und weiter pollt, bis zu seinem eigenen recaptchaTimeout von 300 Sekunden. Dieser Versuch endete nach etwa 333 Sekunden mit TimeoutException, und die Wiederholung war erfolgreich. So oder so ist der Worker wieder frei. Der Fix ersetzt die Referenz auf requests innerhalb von capskip.api, einem internen Teil des SDK, prüfen Sie ihn also erneut, wenn Sie capskip aktualisieren.

Ein geplanter Lösevorgang, der sich nicht selbst überlappen kann

Ein periodischer Task kann einen Lösevorgang zeitgesteuert wiederholen, zum Beispiel um einen Login frisch zu halten. Das vollständige Beispiel unten führt den eigentlichen Login aus. Periodische Tasks nehmen keine Argumente, daher stehen sitekey und URL in der Funktion.

from huey import crontab


# Every 15 minutes. The lock goes under periodic_task: when a run is due
# while the previous one is still solving, another worker skips it instead
# of starting a second solve for the same login.
@huey.periodic_task(crontab(minute="*/15"))
@huey.lock_task("login")
def refresh_login():
    result = solver.recaptcha(sitekey="YOUR_SITEKEY", url="https://example.com/login")
    print("new token:", result["code"][:30], "...")

Der Consumer prüft seine periodischen Tasks einmal beim Start und dann alle 60 Sekunden, gezählt ab diesem Start. Ein Durchlauf, der in der aktuellen Minute fällig ist, startet also sofort. Dauert ein Lösevorgang länger als das Intervall, stellt Huey den nächsten Durchlauf trotzdem in die Queue. Mit mehreren Workern würde ein freier Worker ihn starten, während der erste Durchlauf noch löst. Der Lock verhindert das: Der neue Durchlauf loggt ein WARNING, dass er nicht ausgeführt wurde, weil er den Lock nicht erhalten konnte, und wird übersprungen. Mit einem einzigen Worker überlappt sich nichts, aber verspätete Durchläufe stauen sich in der Queue und laufen dann nacheinander ab. Fügen Sie dem Decorator periodic_task ein Argument expires in Sekunden hinzu, etwa expires=600, um Durchläufe zu verwerfen, die zu lange gewartet haben.

Locks liegen in der Datenbankdatei, sie überstehen also einen Absturz. Nach einem harten Stopp sorgt ein zurückgebliebener Lock dafür, dass jeder spätere Durchlauf übersprungen wird. Starten Sie den Consumer mit -f, um die Locks der Decorators zu löschen, und er loggt, dass er verwaiste Locks gefunden hat. Huey 3.4 hat außerdem eine Option ttl für lock_task eingeführt, aber SqliteHuey unterstützt sie nicht und löst NotImplementedError aus, wenn der Lock gesetzt wird.

Die vollständige Datei für den Huey-Captcha-Task

Diese Version ruft die Login-Seite ab, liest den sitekey und den CSRF-Token aus, löst das Captcha direkt vor dem Absenden und sendet das Formular mit einem Referer-Header ab. Sie bietet den Login auf Abruf als log_in und nach Zeitplan als scheduled_login an. Beide teilen sich einen Lock, daher lösen sie nie gleichzeitig für dasselbe Konto. Ersetzen Sie die URL und die Zugangsdaten und starten Sie dann den Consumer mit -f.

# tasks.py: pip install huey capskip requests
# Start: .venv\Scripts\huey_consumer tasks.huey -w 4 -f
import functools
import logging
import os
import re
import types
from pathlib import Path

import requests
import capskip.api
from capskip import ApiException, CapSkip, ValidationException
from huey import CancelExecution, SqliteHuey, crontab

LOGIN_URL = "https://example.com/login"
USERNAME, PASSWORD = "YOUR_USERNAME", "YOUR_PASSWORD"

huey = SqliteHuey(
    "captcha",
    filename=str(Path(__file__).with_name("huey.db")),  # same file from any folder
    store_intermediate_errors=False,  # get() waits for the last retry
)
log = logging.getLogger("huey.captcha")  # printed by the consumer

# The SDK's HTTP calls have no timeout; give them one so a stuck CapSkip
# cannot hold a worker thread forever.
capskip.api.requests = types.SimpleNamespace(
    RequestException=requests.RequestException,
    post=functools.partial(requests.post, timeout=(5, 30)),
    get=functools.partial(requests.get, timeout=(5, 30)),
)
solver = CapSkip(
    apiKey=os.environ.get("CAPSKIP_API_KEY", "capskip"),
    host=os.environ.get("CAPSKIP_HOST", "127.0.0.1"),
    port=int(os.environ.get("CAPSKIP_PORT", "8080")),
)


def solve(sitekey, page_url):
    try:
        return solver.recaptcha(sitekey=sitekey, url=page_url)["code"]
    except (ApiException, ValidationException) as exc:
        if "ERROR_CAPTCHA_UNSOLVABLE" in str(exc):
            raise  # worth another attempt
        # A refused sitekey, page URL or API key fails the same way every time.
        log.error("not retrying: %s: %s", type(exc).__name__, exc)
        raise CancelExecution(False, f"{type(exc).__name__}: {exc}") from exc


def field(html, pattern):
    match = re.search(pattern, html)
    if not match:
        raise ValueError(f"not found on the login page: {pattern}")
    return match.group(1)


def login():
    with requests.Session() as session:
        page = session.get(LOGIN_URL, timeout=30)
        sitekey = field(page.text, r'data-sitekey="([^"]+)"')
        csrf = field(page.text, r'name="csrfmiddlewaretoken" value="([^"]+)"')
        token = solve(sitekey, LOGIN_URL)  # solve right before the post
        reply = session.post(LOGIN_URL, timeout=30, headers={"Referer": LOGIN_URL}, data={
            "csrfmiddlewaretoken": csrf,
            "username": USERNAME,
            "password": PASSWORD,
            "g-recaptcha-response": token,
        })
        reply.raise_for_status()  # a refused login fails the attempt
        log.info("logged in (HTTP %s)", reply.status_code)
        return reply.status_code


# Both tasks take the same lock, so an on-demand login never runs while a
# scheduled one is solving, and the other way round.

# On demand: from tasks import log_in; log_in().get(blocking=True, timeout=1100)
@huey.task(retries=2, retry_delay=15)  # a locked attempt uses a retry
@huey.lock_task("login")
def log_in():
    return login()


# On a schedule; a run that comes due while the lock is held is skipped.
@huey.periodic_task(crontab(minute="*/15"))
@huey.lock_task("login")
def scheduled_login():
    login()

Der Lösevorgang findet im selben Versuch statt wie das Absenden, daher ruft eine Wiederholung immer eine frische Seite ab und löst erneut. Ein Token wird nie aus einem gescheiterten Versuch übernommen, und das ist wichtig, weil Google einen reCAPTCHA-Token zwei Minuten lang gelten lässt. Ein abgelehnter Login, der einen HTTP-Fehler zurückgibt, etwa einen 403, lässt den Versuch über raise_for_status() scheitern und wird zweimal wiederholt, jedes Mal mit einer neuen Seite und einem neuen Lösevorgang. CapSkip rechnet nicht pro Lösung ab, das kostet also nur Zeit. Viele Websites beantworten ein falsches Passwort mit 200 und erneut dem Formular, fügen Sie also eine Prüfung für Ihre Website hinzu, etwa die URL nach dem Absenden. Ein log_in auf Abruf, das den Lock im Besitz des geplanten Durchlaufs vorfindet, wird mit demselben WARNING übersprungen, und dieser Versuch verbraucht eine Wiederholung. Zwei Wiederholungen im Abstand von 15 Sekunden geben ihm insgesamt etwa 30 Sekunden Wartezeit. Dauert der geplante Lösevorgang länger, werden alle drei Versuche übersprungen, und get() löst TaskException aus, mit TaskLockedException in der Meldung. Erhöhen Sie retry_delay bei log_in, wenn Ihre Lösevorgänge normalerweise länger dauern.

Um sich aus einem anderen Skript auf Abruf anzumelden, importieren Sie log_in aus tasks, rufen es auf und warten mit get(blocking=True, timeout=1100) auf das Ergebnis, wie in Schritt 5. Der Rückgabewert ist der finale HTTP-Status nach dem Absenden. Logzeilen aus dem Task erscheinen im Fenster des Consumers neben denen von Huey selbst.

Den Consumer sauber stoppen

Drücken Sie im Fenster des Consumers einmal Ctrl+C. Huey nimmt keine neuen Tasks mehr an, wartet auf die laufenden und beendet sich. In unseren Durchläufen beendete sich ein Consumer, der mitten in einem Lösevorgang gestoppt wurde, 18 Sekunden später, sobald dieser Lösevorgang fertig war. Ein Consumer im Leerlauf beendete sich in weniger als einer Zehntelsekunde. Ein zweites Ctrl+C verhält sich je nach Version unterschiedlich: Python 3.14 beendet sich sofort, während 3.13 weiterhin auf den laufenden Task wartet, sodass sich ein hängender Lösevorgang ohne das Timeout von oben nur noch gewaltsam beenden ließe.

Alles, was schneller ist als ein geordnetes Beenden, verliert den laufenden Task. Dazu gehören ein zweites Ctrl+C unter 3.14, Ctrl+Break und die Option -t, die das geordnete Warten begrenzt: Mit -t 5 beendete sich der Consumer etwa 5,1 Sekunden nach Ctrl+C. Der verlorene Task hatte die Queue bereits verlassen, er läuft also nicht erneut, und sein Lock bleibt zurück. Deshalb startet das vollständige Beispiel mit -f.

CapSkip auf einem anderen Rechner betreiben

Der Consumer und der Solver 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. Ihre tasks.py liest die Adresse bereits aus der Umgebung, setzen Sie also die Variablen, bevor Sie den Consumer starten.

# PowerShell, before starting the consumer: CapSkip runs in Server mode
# on another Windows machine on your network
$env:CAPSKIP_HOST = "192.168.1.50"
$env:CAPSKIP_API_KEY = "YOUR_API_KEY"
# $env:CAPSKIP_PORT = "8080"   only if you changed the port
.venv\Scripts\huey_consumer tasks.huey -w 4 -f

Verwenden Sie eine statische öffentliche IP, wenn der Weg über das Internet führt, und aktivieren Sie die Validierung des API-Schlüssels, mit demselben Schlüssel in CAPSKIP_API_KEY. 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. Der Consumer kann dann auf jedem Rechner laufen, der den Port erreicht, und der Solver bleibt Ihr eigener Windows-Rechner, ohne Abrechnung pro Lösung.

Häufige Captcha-Fehler mit Huey

Was Sie sehenUrsacheBeheben
huey_consumer.py wird nicht erkanntHuey 3 installiert nur huey_consumer.exeStarten Sie .venv\Scripts\huey_consumer
huey cannot be run in process-mode on WindowsProzess-Worker brauchen fork()Verwenden Sie die standardmäßigen Thread-Worker
ModuleNotFoundError: No module named tasksDer Consumer wurde außerhalb des Ordners gestartet, der tasks.py enthältStarten Sie ihn aus diesem Ordner
ResultTimeout, obwohl der Consumer läuftSkript und Consumer verwenden verschiedene huey.db-DateienGeben Sie SqliteHuey einen vollständigen Pfad zur Datei
get() meldet einen Fehlschlag, danach ist der Task erfolgreichstore_intermediate_errors ist eingeschaltet, daher wird jeder gescheiterte Versuch gespeichertSetzen Sie store_intermediate_errors=False
Ein Task, der aufhören soll, versucht es immer wiederCancelExecution hat die Meldung als Argument retry bekommenLösen Sie CancelExecution(False, message) aus
Worker nehmen keine Tasks mehr an und loggen nichtsEin Lösevorgang hängt in einem HTTP-Aufruf ohne Timeout festGeben Sie den Anfragen des SDK ein Timeout
Jeder geplante Durchlauf wird übersprungenEin verwaister Lock eines gewaltsam beendeten ConsumersStarten Sie den Consumer mit -f neu

FAQ

Kann ich RedisHuey statt SqliteHuey verwenden?

Ja. Die Tasks, Wiederholungen und Locks in diesem Leitfaden funktionieren genauso. Installieren Sie zusätzlich das Paket redis und erstellen Sie die Queue dann mit RedisHuey statt mit SqliteHuey. RedisHuey braucht allerdings einen Redis-Server, und Windows bringt keinen mit. Auf dem PC, auf dem CapSkip ohnehin läuft, erspart Ihnen die SQLite-Datei einen zweiten Dienst. Für eine Queue, die über viele Rechner verteilt ist, passt Redis besser, wie in diesem Leitfaden zum Lösen von Captchas in Celery-Tasks.

Warum startet der erste Task nach einer Leerlaufphase verspätet?

Ein Consumer im Leerlauf prüft die Datenbank immer seltener, standardmäßig bis hin zu nur noch alle 10 Sekunden. Mit vier Workern wartete der erste Task nach einer Leerlaufphase in unseren Durchläufen 3 bis 7 Sekunden. Starten Sie den Consumer mit -m 1, um dieses Warten auf eine Sekunde zu begrenzen; danach blieb es unter einer Sekunde.

Funktioniert das in einem Django-Projekt?

Ja, über huey.contrib.djhuey. Setzen Sie in der Einstellung HUEY huey_class auf huey.SqliteHuey, geben Sie einen filename an und starten Sie den Consumer mit manage.py run_huey. Ein Standardwert überrascht viele: Bei eingeschaltetem DEBUG führt djhuey Tasks sofort innerhalb des Requests aus, sodass ein Lösevorgang die Seite blockiert, bis CapSkip antwortet. Setzen Sie immediate in der Einstellung HUEY auf False, um die echte Queue zu testen.

Kann der Consumer auf einem anderen Rechner laufen als CapSkip?

Ja. Schalten Sie CapSkip in den Verbindungseinstellungen in den Server-Modus, damit es auf einer Netzwerkadresse statt auf Loopback lauscht, und setzen Sie CAPSKIP_HOST auf diese Adresse, bevor der Consumer startet. Die Tasks rufen es dann über dieselbe HTTP-API von jedem Rechner aus auf, der den Port erreichen kann. Fügen Sie in der Windows Firewall eine Blockierregel für die Adressen hinzu, die es nicht erreichen sollen. Der Solver läuft weiterhin auf Hardware, die Ihnen gehört, und kein Lösevorgang läuft über einen Drittanbieterdienst.

Die Kurzfassung

Für einen Huey-Captcha-Task unter Windows legen Sie die SqliteHuey-Datei per festem Pfad neben tasks.py, schalten store_intermediate_errors aus und wiederholen nur die Fehlschläge von CapSkip, die ein neuer Versuch beheben kann, während Sie den Rest mit CancelExecution(False, message) beenden. Starten Sie .venv\Scripts\huey_consumer mit Thread-Workern aus dem Ordner, der tasks.py enthält, geben Sie den HTTP-Aufrufen des SDK ein Timeout, rechnen Sie damit, jedes Ergebnis nur einmal lesen zu können, und versehen Sie geplante Lösevorgänge mit einem Lock. Die Huey-Dokumentation zum Consumer listet jede hier verwendete Consumer-Option auf.

Eine Queue in einer einzigen Datei und ein Solver auf demselben PC halten die gesamte Arbeit auf Hardware, die Sie kontrollieren. Genau darin liegt der Sinn einer lokalen Captcha-Umgehung: Jede Wiederholung ist kostenlos, also kann es sich der Task leisten, es erneut zu versuchen.