Как парсить результаты поиска через капчу на Python (2026)
Страницы результатов поиска — одно из первых мест, где парсер сталкивается с капчей. Сайт видит всплеск запросов с одного адреса, показывает проверку и не возвращает результаты, пока проверка не пройдена. Чтобы парсить результаты поиска через капчу, вашему скрипту нужны три шага: обнаружить проверку до разбора, решить ее одним вызовом API и отправить токен, чтобы сайт вернул настоящую страницу результатов.
Это руководство показывает такой поток на Python с requests и BeautifulSoup. Оно охватывает страницы с виджетом PopularCaptcha, который Scarnox решает сегодня (без прокси). Начните с бесплатного пробного баланса, карта не нужна и сначала запустите код на странице, которой вы владеете.
Как выглядит проверка
Страницы поиска обычно помещают виджет внутрь формы. Контейнер виджета несет атрибут data-sitekey, а форма отправляет данные обратно на сайт:
<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>
Вашему коду из этой разметки нужны три вещи: sitekey, URL действия формы и скрытые поля, которые надо отправить обратно вместе с токеном. Функция ниже читает все три, поэтому ничего не нужно прописывать вручную.
Если виджет на вашей цели — другой тип капчи, см. типы капчи. Поддержка reCAPTCHA и Cloudflare Turnstile скоро появится, поэтому код здесь охватывает только PopularCaptcha.
Вызов решателя
Запрос на решение уходит в API Scarnox с вашим ключом в заголовке Bearer. Вызов блокируется, пока токен не будет готов, поэтому цикла опроса нет:
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, # вызов возвращается, когда токен готов
)
data = r.json()
if data.get("errorId") != 0:
raise RuntimeError(f"{data.get('errorCode')}: {data.get('errorDescription')}")
return data["solution"]["token"]
Неудачное решение возвращает errorId, отличный от 0, и с вас за него не берут плату. Повторы описаны в обработке ошибок решателя капчи.
Загрузка страницы и прохождение капчи
Функция загрузки сначала проверяет наличие виджета. Если его нет, она возвращает HTML без изменений. Если он есть, она решает проверку, собирает форму с теми же скрытыми полями и отправляет ее:
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 # на этой странице нет проверки
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
Здесь важны две детали. site_url — это URL, на котором реально отдается капча (resp.url, включая все редиректы), а не голый адрес поиска. И сессия сохраняет cookie между GET и POST, которые многим сайтам нужны, чтобы принять токен.
Токены истекают примерно через две минуты, поэтому решение происходит сразу после чтения формы, а не заранее. Подробнее об этом окне — в сроке действия токена капчи.
Разбор результатов
Когда возвращается настоящая страница результатов, разбор — обычная работа с HTML. Селекторы ниже — заглушки; откройте страницу в DevTools и замените их на настоящие:
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) # пауза между страницами
Храните вывод каждой страницы отдельно. Если страница возвращает ноль строк, сначала проверьте наличие виджета, прежде чем считать, что по запросу нет результатов.
Ответственный парсинг
Капча — сигнал, а не лазейка. Прежде чем запускать это на любом поисковом сайте:
- Прочитайте robots.txt и условия использования. Многие поисковые сайты запрещают автоматические запросы. Если условия это запрещают, не запускайте скрипт.
- Ограничивайте частоту.
time.sleep(5)выше — это минимум, а не цель. Замедляйтесь еще, если видите больше проверок. - Ограничивайте то, что храните. Результаты поиска могут содержать имена, адреса электронной почты и другие персональные данные. Храните только нужные поля и удаляйте остальное.
- Предпочитайте официальный API, когда сайт его предлагает. Он надежнее и обычно дешевле парсинга HTML.
Юридическую сторону см. в законности парсинга. Как не получить блок в первую очередь — в парсинге без блокировок.
Цены
Scarnox списывает оплату за каждое успешное решение, а неудачные решения бесплатны. Тарифы — на странице цен. Начните с бесплатного пробного баланса, карта не нужна.