Zoekresultaten scrapen voorbij een captcha (Python-gids, 2026)
Zoekresultaatpagina's zijn een van de eerste plekken waar een scraper een captcha tegenkomt. De site ziet een uitbarsting van requests vanaf één adres, toont een uitdaging en geeft geen resultaten totdat die is doorstaan. Om zoekresultaten te scrapen voorbij een captcha, heeft je script drie stappen nodig: de uitdaging detecteren voordat je parseert, hem oplossen met één API-aanroep, en het token indienen zodat de site de echte resultatenpagina teruggeeft.
Deze gids laat die flow zien in Python met requests en BeautifulSoup. Hij behandelt pagina's die een PopularCaptcha-widget tonen, het type dat Scarnox vandaag oplost (proxyless). Begin met gratis proefkrediet, zonder creditcard en draai de code eerst tegen een pagina die van jou is.
Hoe de uitdaging eruitziet
Zoekpagina's zetten de widget meestal binnen een formulier. De widgetcontainer heeft een attribuut data-sitekey, en het formulier stuurt terug naar de site:
<form action="/search/verify" method="post">
<input type="hidden" name="q" value="web scraping">
<input type="hidden" name="lang" value="en">
<div class="h-captcha" data-sitekey="YOUR-SITEKEY-UUID"></div>
<button type="submit">Continue</button>
</form>
Je code heeft drie dingen uit deze opmaak nodig: de sitekey, de action-URL van het formulier, en de verborgen velden die samen met het token terug moeten worden gestuurd. De onderstaande helper leest alle drie, dus je hoeft ze niet vast in de code te zetten.
Als de widget op jouw doel een ander captchatype is, zie typen captcha. Ondersteuning van reCAPTCHA en Cloudflare Turnstile komt binnenkort beschikbaar, dus de code hier behandelt alleen PopularCaptcha.
De solve-aanroep
Het solve-verzoek gaat naar de Scarnox-API met je sleutel in een Bearer-header. De aanroep blokkeert tot het token klaar is, dus er is geen pollinglus nodig:
import time
from urllib.parse import urljoin
import requests
from bs4 import BeautifulSoup
API_KEY = "scarnox_YOURAPIKEY"
SEARCH_URL = "https://search.example.com/search"
session = requests.Session()
session.headers["User-Agent"] = (
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0 Safari/537.36"
)
def solve(sitekey: str, page_url: str) -> str:
r = requests.post(
"https://api.scarnox.com/api/tasks/create",
headers={"Authorization": f"Bearer {API_KEY}"},
json={
"captcha_type": "PopularCaptchaTaskProxyless",
"site_key": sitekey,
"site_url": page_url,
},
timeout=185, # de aanroep keert terug wanneer 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"]
Een mislukte solve geeft een errorId terug die niet 0 is, en daarvoor betaal je niets. Retries worden behandeld in captcha solver-foutafhandeling.
De pagina ophalen en de captcha passeren
De fetch-functie controleert eerst op de widget. Als die er niet is, geeft hij de HTML ongewijzigd terug. Als die er wel is, lost hij de uitdaging op, bouwt het formulier opnieuw op met dezelfde verborgen velden, en verstuurt het:
def fetch_results(query: str, page: int = 1) -> str:
resp = session.get(SEARCH_URL, params={"q": query, "page": page}, timeout=30)
soup = BeautifulSoup(resp.text, "html.parser")
widget = soup.select_one("[data-sitekey]")
if widget is None:
return resp.text # geen uitdaging op deze pagina
form = widget.find_parent("form")
if form is None:
raise RuntimeError("Captcha widget is not inside a form")
token = solve(widget["data-sitekey"], resp.url)
payload = {
field["name"]: field.get("value", "")
for field in form.select("input[name]")
}
payload["h-captcha-response"] = token
payload["g-recaptcha-response"] = token
action = urljoin(resp.url, form.get("action") or resp.url)
verified = session.post(action, data=payload, timeout=30)
if "data-sitekey" in verified.text:
raise RuntimeError("Challenge still shown after submit; token may be rejected")
return verified.text
Twee details zijn hier belangrijk. De site_url is de URL waarop de captcha daadwerkelijk wordt getoond (resp.url, dat eventuele redirects bevat), en niet het kale zoekeindpunt. En de sessie houdt cookies vast tussen de GET en de POST, wat veel sites nodig hebben om het token te accepteren.
Tokens verlopen na ongeveer twee minuten, daarom gebeurt de solve direct nadat het formulier is gelezen, en niet eerder. Voor meer over dat tijdvenster, zie verlopen captchatokens.
De resultaten parseren
Zodra de echte resultatenpagina is teruggegeven, is parsen gewoon HTML-werk. De onderstaande selectors zijn placeholders; open de pagina in DevTools en vervang ze door de echte:
def parse_results(html: str) -> list[dict]:
soup = BeautifulSoup(html, "html.parser")
rows = []
for item in soup.select("div.result"):
link = item.select_one("a[href]")
heading = item.select_one("h3")
if link and heading:
rows.append({
"title": heading.get_text(strip=True),
"url": link["href"],
})
return rows
if __name__ == "__main__":
for page in range(1, 4):
html = fetch_results("web scraping", page)
for row in parse_results(html):
print(row)
time.sleep(5) # stem verzoeken af tussen pagina's
Houd de uitvoer van elke pagina apart. Als een pagina nul rijen teruggeeft, controleer dan eerst op de widget voordat je aanneemt dat de zoekopdracht geen resultaten had.
Scrape verantwoord
Een captcha is een signaal, geen achterdeur. Voordat je dit tegen een zoeksite draait:
- Lees robots.txt en de gebruiksvoorwaarden. Veel zoeksites verbieden geautomatiseerde zoekopdrachten. Als de voorwaarden het verbieden, draai het script dan niet.
- Beperk de snelheid. De
time.sleep(5)hierboven is een ondergrens, geen doel. Vertraag verder als je meer captchas ziet. - Beperk wat je bewaart. Zoekresultaten kunnen namen, e-mailadressen en andere persoonsgegevens bevatten. Bewaar alleen de velden die je nodig hebt, en verwijder de rest.
- Geef de voorkeur aan een officiële API wanneer de site er een aanbiedt. Die is betrouwbaarder en meestal goedkoper dan HTML scrapen.
Voor de juridische kant, zie is webscraping legaal. Om blokkades in de eerste plaats te voorkomen, zie scrapen zonder geblokkeerd te worden.
Prijzen
Scarnox rekent per geslaagde solve, en mislukte solves zijn gratis. Zie actuele prijzen voor de tarieven. Begin met gratis proefkrediet, zonder creditcard.