Como raspar resultados de busca passando por um captcha (guia em Python, 2026)
Páginas de resultados de busca estão entre os primeiros lugares onde um scraper encontra um captcha. O site percebe uma rajada de requisições de um mesmo endereço, exibe um desafio e não devolve resultados até que o desafio seja superado. Para raspar resultados de busca passando por um captcha, o seu script precisa de três etapas: detectar o desafio antes de fazer o parse, resolvê-lo com uma chamada de API e enviar o token para que o site retorne a página de resultados de verdade.
Este guia mostra esse fluxo em Python com requests e BeautifulSoup. Ele cobre páginas que exibem um widget PopularCaptcha, o tipo que a Scarnox resolve hoje (proxyless). Comece com crédito de teste grátis, sem cartão e rode o código em uma página sua antes de tudo.
Como é o desafio
As páginas de busca costumam colocar o widget dentro de um formulário. O contêiner do widget carrega um atributo data-sitekey, e o formulário envia os dados de volta para o 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>
Seu código precisa de três coisas desse marcador: o sitekey, a URL de action do formulário e os campos ocultos que devem ser enviados de volta junto com o token. O helper abaixo lê as três, então funciona sem fixar esses valores no código.
Se o widget do seu alvo for de outro tipo de captcha, veja tipos de captcha. O suporte ao reCAPTCHA e ao Cloudflare Turnstile está em breve, então o código aqui cobre apenas o PopularCaptcha.
A chamada ao solver
A requisição de resolução vai para a API da Scarnox com a sua chave em um cabeçalho Bearer. A chamada bloqueia até o token estar pronto, então não há loop de 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, # a chamada retorna quando o token estiver pronto
)
data = r.json()
if data.get("errorId") != 0:
raise RuntimeError(f"{data.get('errorCode')}: {data.get('errorDescription')}")
return data["solution"]["token"]
Uma resolução que falha devolve errorId diferente de 0, e você não é cobrado por ela. As novas tentativas estão em tratamento de erros de solver de captcha.
Busque a página e passe pelo captcha
A função de busca verifica primeiro se há um widget. Se não houver, devolve o HTML sem alterações. Se houver, resolve o desafio, monta o formulário de novo com os mesmos campos ocultos e o envia:
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 # sem desafio nesta página
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
Dois detalhes importam aqui. O site_url é a URL em que o captcha é de fato servido (resp.url, que inclui eventuais redirecionamentos), e não o endpoint de busca simples. E a sessão mantém os cookies entre o GET e o POST, o que muitos sites exigem para aceitar o token.
Os tokens expiram em cerca de dois minutos, por isso a resolução acontece logo depois que o formulário é lido, e não antes. Para mais detalhes sobre essa janela, veja expiração de token de captcha.
Faça o parse dos resultados
Quando a página de resultados de verdade é devolvida, o parse é trabalho comum de HTML. Os seletores abaixo são exemplos; abra a página no DevTools e troque-os pelos seletores reais:
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) # controle o ritmo entre as páginas
Mantenha a saída de cada página separada. Se uma página devolver zero linhas, verifique a presença do widget antes de presumir que a consulta não teve resultados.
Faça scraping com responsabilidade
Um captcha é um sinal, não uma brecha. Antes de rodar isto em qualquer site de busca:
- Leia o robots.txt e os termos de serviço. Muitos sites de busca proíbem consultas automatizadas. Se os termos proibirem, não rode o script.
- Limite a taxa. O
time.sleep(5)acima é um piso, não uma meta. Diminua ainda mais se vir mais desafios. - Limite o que você guarda. Resultados de busca podem conter nomes, e-mails e outros dados pessoais. Guarde apenas os campos de que precisa e apague o resto.
- Prefira uma API oficial quando o site oferecer uma. Ela é mais confiável e geralmente mais barata do que raspar HTML.
Para o lado jurídico, veja a web scraping é legal. Para evitar bloqueios para começar, veja como não ser bloqueado no scraping.
Preços
A Scarnox cobra por resolução bem-sucedida, e as resoluções que falham são gratuitas. Veja a página de preços para as tarifas. Comece com crédito de teste grátis, sem cartão.