Scarnox · Blog

Як скрапити результати пошуку через капчу (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)  # пауза між запитами сторінок

Зберігайте вивід кожної сторінки окремо. Якщо сторінка повертає нуль рядків, спершу перевірте наявність віджета, а вже потім вважайте, що запит не має результатів.

Скрапінг відповідально

Капча - це сигнал, а не лазівка. Перш ніж запускати це на будь-якому пошуковому сайті:

Про правовий бік див. чи законний веб-скрапінг. Щоб уникнути блокувань узагалі, див. скрапінг без блокувань.

Ціни

Scarnox бере плату за кожне успішне розв'язання, а невдалі розв'язання безкоштовні. Актуальні тарифи дивіться на сторінці тарифів. Почніть з безкоштовного пробного кредиту, картка не потрібна.

Що далі