Come fare scraping dei risultati di ricerca superando un captcha (guida Python 2026)
Le pagine dei risultati di ricerca sono uno dei primi posti in cui uno scraper incontra un captcha. Il sito vede una raffica di richieste da un solo indirizzo, serve una sfida e non restituisce risultati finché la sfida non viene superata. Per fare scraping dei risultati di ricerca oltre un captcha, il tuo script ha bisogno di tre passaggi: rilevare la sfida prima di analizzare la pagina, risolverla con una sola chiamata API e inviare il token così che il sito restituisca la vera pagina dei risultati.
Questa guida mostra questo flusso in Python con requests e BeautifulSoup. Copre le pagine che mostrano un widget PopularCaptcha, il tipo che Scarnox risolve oggi (proxyless). Inizia con il credito di prova gratuito, senza carta ed esegui il codice su una pagina di tua proprietà prima di tutto.
Come si presenta la sfida
Le pagine di ricerca di solito inseriscono il widget in un modulo. Il contenitore del widget ha un attributo data-sitekey, e il modulo invia i dati al sito:
<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>
Dal markup il tuo codice ha bisogno di tre cose: la sitekey, l'URL di azione del modulo e i campi nascosti che devono essere rispediti insieme al token. La funzione qui sotto li legge tutti e tre, quindi funziona senza valori fissi nel codice.
Se il widget del tuo bersaglio è un tipo di captcha diverso, consulta tipi di captcha. Il supporto a reCAPTCHA e Cloudflare Turnstile è in arrivo, quindi il codice qui tratta solo PopularCaptcha.
La chiamata al solver
La richiesta di risoluzione va all'API di Scarnox con la tua chiave nell'intestazione Bearer. La chiamata si blocca finché il token è pronto, quindi non c'è alcun ciclo di polling:
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, # la chiamata restituisce il risultato quando il token è pronto
)
data = r.json()
if data.get("errorId") != 0:
raise RuntimeError(f"{data.get('errorCode')}: {data.get('errorDescription')}")
return data["solution"]["token"]
Un solve fallito restituisce un errorId diverso da 0, e non ti viene addebitato. I nuovi tentativi sono trattati in gestione degli errori del solver captcha.
Scarica la pagina e supera il captcha
La funzione di recupero controlla prima il widget. Se non c'è, restituisce l'HTML invariato. Se c'è, risolve la sfida, ricostruisce il modulo con gli stessi campi nascosti e lo invia:
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 # nessuna sfida su questa 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
Due dettagli contano qui. Il site_url è l'URL su cui viene servito il captcha (resp.url, che include eventuali reindirizzamenti), non il semplice endpoint di ricerca. E la sessione mantiene i cookie tra la GET e la POST, cosa che molti siti richiedono per accettare il token.
I token scadono in circa due minuti, ed è per questo che la risoluzione avviene subito dopo la lettura del modulo, non prima. Per approfondire quella finestra, vedi scadenza del token captcha.
Analizza i risultati
Una volta restituita la vera pagina dei risultati, l'analisi è il normale lavoro sull'HTML. I selettori qui sotto sono segnaposto; apri la pagina in DevTools e sostituiscili con quelli reali:
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) # rallenta le richieste tra una pagina e l'altra
Tieni separato l'output di ogni pagina. Se una pagina restituisce zero righe, verifica la presenza del widget prima di presumere che la query non abbia risultati.
Scraping responsabile
Un captcha è un segnale, non una scappatoia. Prima di eseguire questo script su un sito di ricerca:
- Leggi robots.txt e i termini di servizio. Molti siti di ricerca vietano le query automatizzate. Se i termini lo proibiscono, non eseguire lo script.
- Limita la frequenza. Il
time.sleep(5)mostrato sopra è un minimo, non un obiettivo. Rallenta ulteriormente se vedi più sfide. - Limita ciò che conservi. I risultati di ricerca possono contenere nomi, indirizzi email e altri dati personali. Memorizza solo i campi che ti servono, ed elimina il resto.
- Preferisci un'API ufficiale quando il sito ne offre una. È più affidabile e di solito più economica dello scraping dell'HTML.
Per il lato legale, vedi il web scraping è legale?. Per evitare i blocchi fin dall'inizio, vedi scraping senza farsi bloccare.
Tariffe
Scarnox addebita per ogni solve riuscito, e i solve falliti sono gratuiti. Consulta le tariffe attuali per i prezzi. Inizia con il credito di prova gratuito, senza carta.