Captchatoken verlopen: oorzaken en hoe je het oplost
Een captchatoken is een kortlevend bewijs dat een uitdaging is gehaald. De PopularCaptcha-tokens van Scarnox verlopen ongeveer twee minuten na uitgifte. Als je een token na dat venster verzendt, wijst de site het formulier af, vaak met een melding dat de captcha is verlopen of ongeldig is. De oplossing gaat bijna altijd over timing: los de captcha zo dicht mogelijk bij het verzenden op, gebruik elk token één keer, en probeer alleen opnieuw wanneer je weet dat de eerste poging niet is geaccepteerd.
Waarom tokens verlopen
Een token is geen permanente referentie. De site wil weten dat een persoon (of een solver die namens een persoon handelt) de uitdaging recent heeft voltooid, dus geeft hij het token een korte levensduur. De klok start bij uitgifte van het token, niet wanneer je voor het eerst de API aanroept. Alles wat tussen de oplossing en het verzenden gebeurt, eet aan die twee minuten.
Veelvoorkomende oorzaken
- Te vroeg oplossen. Een batch tokens vooraf oplossen en bewaren voor later is de meest voorkomende oorzaak. Tegen de tijd dat een token wordt gebruikt, is het verouderd.
- Traagte code tussen oplossing en verzenden. Een lange databaseschrijfactie, een wachtrij, of een
time.sleepvóór het posten van het formulier kan het token over zijn venster heen duwen. - Een token hergebruiken. Een token dat al is geaccepteerd, of dat een mislukte poging al heeft gebruikt, werkt niet meer. Behandel elk token als eenmalig.
- De pagina opnieuw laden. Als de pagina na de oplossing wordt ververst, kan het formulier een nieuwe uitdaging uitgeven en het token dat je hebt ongeldig maken.
De oplossing: los op vlak voor het verzenden
Het eenvoudigste betrouwbare patroon is om de oplossing en het verzenden één stap te maken. Vraag een token aan, zet het in het formulier, post het formulier, en doe er niets traags tussenin:
import requests
API_KEY = "scarnox_YOURAPIKEY"
CREATE_URL = "https://api.scarnox.com/api/tasks/create"
def fresh_token(sitekey: str, page_url: str) -> str:
r = requests.post(
CREATE_URL,
headers={"Authorization": f"Bearer {API_KEY}"},
json={
"captcha_type": "PopularCaptchaTaskProxyless",
"site_key": sitekey,
"site_url": page_url,
},
timeout=185, # de aanroep blokkeert totdat het token klaar is
)
data = r.json()
if data.get("errorId") != 0:
raise RuntimeError(f"{data.get('errorCode')}: {data.get('errorDescription')}")
return data["solution"]["token"]
def submit_form(page_url: str, sitekey: str, form: dict) -> requests.Response:
token = fresh_token(sitekey, page_url) # los op vlak voor het posten
return requests.post(
page_url,
data={**form, "h-captcha-response": token, "g-recaptcha-response": token},
timeout=30,
)
Let op dat submit_form geen token in een cache of wachtrij bewaart. Hij vraagt er één aan, post hem, en gaat verder. Mislukte oplossingen worden niet in rekening gebracht, dus een mislukt verzoek kost je aan de oplossingskant niets.
Veilig opnieuw proberen
Sommige sites wijzen een verlopen token af met een duidelijk antwoord. Andere geven een normale pagina terug met een foutmelding erin, dus een statuscode alleen vertelt je niet genoeg. Bekijk het antwoord dat de site daadwerkelijk teruggeeft voor een verlopen token en bouw je controle daarop. Een lus voor opnieuw proberen kan er zo uitzien:
def submit_with_retry(page_url: str, sitekey: str, form: dict, attempts: int = 2):
for _ in range(attempts):
resp = submit_form(page_url, sitekey, form)
if resp.status_code < 400 and "captcha" not in resp.text.lower():
return resp # geaccepteerd, voor zover het antwoord van deze site dat laat zien
raise RuntimeError("captcha token rejected after retries")
Wees voorzichtig met nieuwe pogingen bij formulieren die iets aanmaken. Als de eerste poging wel is geaccepteerd maar je code het antwoord verkeerd heeft gelezen, kan een nieuwe poging een dubbel account, bestelling of bericht aanmaken. Probeer alleen opnieuw wanneer je zeker weet dat de eerste post niet is geaccepteerd, en maak de controle specifiek voor de site waarmee je werkt.
Houd de timing eerlijk
- Los niet in een lus van tokens die je later wilt gebruiken. Los per verzending op.
- Houd het werk tussen de oplossing en het posten tot een minimum.
- Gebruik een time-out van ongeveer 185 seconden op het oplossingsverzoek, omdat de aanroep blokkeert totdat het token klaar is. Een aparte timer op je eigen formulierpost moet veel korter zijn dan de levensduur van het token.
- Heb je een snellere pipeline nodig, zie dan captcha snel oplossen, dat parallelle oplossingen behandelt zonder tokens op te slaan.
Veelgemaakte fouten
- Tokens cachen in een dictionary of Redis voor later. De cache lijkt onschuldig totdat een token wordt gebruikt na zijn venster en elke verzending faalt.
- Eén keer per sessie oplossen. Een langlopende sessie heeft nog steeds voor elke formulierpost een nieuw token nodig, omdat elke post zijn eigen uitdagingscontrole kan uitvoeren.
- Slapen tussen oplossing en verzending. Een snelheidsbegrenzer of een bewuste pauze na de oplossing verbruikt de levensduur van het token. Plaats elke vertraging in plaats daarvan vóór de oplossing.
- Elke fout hetzelfde behandelen. Een time-out aan jouw kant, een afgewezen token en een fout van de site vragen om verschillende afhandeling. Log ze apart.
Log genoeg om het te diagnosticeren
Wanneer een verzending mislukt, registreer dan het tijdstip waarop het token is uitgegeven, het tijdstip waarop het formulier is gepost, de HTTP-status en de eerste paar honderd tekens van de respons. Met die vier waarden zie je in één oogopslag of het token is verlopen of dat de site het om een andere reden heeft afgewezen. Log het volledige token niet in gedeelde logs, want een geldig token is een referentie voor die ene uitdaging.
Wat je moet controleren als het nog steeds faalt
- Print de tijd tussen het moment dat het token wordt teruggegeven en het posten van het formulier.
- Bevestig dat
site_urlovereenkomt met de pagina die de captcha toont. - Bevestig dat de sitekey is gelezen van de huidige pagina, niet van een oude kopie.
- Bevestig de veldnamen van het formulier. PopularCaptcha-tokens gaan in
h-captcha-response, en vaak ook ing-recaptcha-response.
Voor de volledige lijst met foutantwoorden en hoe je elk afhandelt, zie foutafhandeling van de captcha-solver.