hCaptcha на Python с CapSkip SDK: один вызов, один токен

hcaptcha in python - hCaptcha in Python with the CapSkip SDK: One Call, One Token

Чтобы решить hCaptcha на Python с помощью CapSkip SDK, вызовите solver.hcaptcha() с sitekey виджета и адресом страницы, а затем отправьте свою форму с полученным токеном и тем User-Agent, который сообщает SDK. SDK отправляет задание, опрашивает результат и возвращает оба значения, поэтому скрипт состоит в основном из правил повтора и отправки формы. За вызовом стоит решатель hCaptcha в CapSkip. Он работает на вашем собственном ПК с Windows по фиксированной лицензии, поэтому заменить токен, срок которого истёк или который был отклонён, ничего не стоит.

Что понадобится

  • ПК с Windows, на котором запущен CapSkip 4.0.0 или новее. В этом выпуске от 3 ноября 2026 года появилось решение hCaptcha, включая Enterprise. Установить его поможет руководство по настройке.
  • Python 3.10 или новее (это минимум, который объявляет пакет capskip) и два пакета: capskip 1.6.0 или новее, первая версия с методом для hCaptcha, и requests. Проверено с capskip 1.6.0 и requests 2.34.2 на Python 3.11.7 и 3.14.0, на других версиях не проверялось.
  • Окно PowerShell, в котором вы выполните pip install capskip requests.
  • Страница с виджетом hCaptcha, которой вы владеете или которую вам разрешено автоматизировать, и её sitekey. Шаг 2 показывает, где его найти.
  • Адрес для CapSkip. Режим зависит от того, где запускается скрипт. Режим Local (127.0.0.1) обслуживает только тот ПК, на котором работает CapSkip. Режим Server слушает ещё и вашу сеть или публичный IP, поэтому другой ПК, VPS с Windows или размещённая платформа могут отправлять те же запросы. Режимы переключаются в разделе Настройки подключения. Код ниже рассчитан на формат CapSkip по умолчанию, поэтому оставьте любую настройку Service Emulator в значении по умолчанию.
  • По желанию: прокси. О том, куда его вставить, говорится в шаге 2.

Как это проверялось: на двух имитациях серверов, которые написал я, а не на настоящем приложении CapSkip и не на живом сайте с hCaptcha. Одна имитация повторяет проверки из раздела hCaptcha на странице CapSkip с названием Документация API. Вторая изображает форму регистрации и заодно служит прямым прокси-сервером. Здесь нигде не измерено реальное время решения, а правила сайта в имитации (одноразовый токен, совпадающий User-Agent, срок жизни две минуты) взяты из документации CapSkip, а не из hCaptcha. Настоящую проверку выполняет сайт, на который вы нацелены. Как это устроено, смотрите в документации для разработчиков hCaptcha.

Шаг 1: создание solver

Четыре блока кода ниже относятся к одному файлу и идут по порядку. Назовите его hcaptcha_demo.py. Ничего не запускается, пока не добавлен последний блок. В этих прогонах SDK был настоящим, capskip 1.6.0, а имитацией было только приложение CapSkip за ним. Аргумент apiKey конструктора по умолчанию равен строке capskip, и это работает, пока настройка API Key Validation выключена. Если вы её включили, передайте свой ключ как apiKey.

Конструктор также задаёт, как долго SDK ждёт hCaptcha. Его аргумент recaptchaTimeout, по умолчанию 300 секунд, действует потому, что виджет проходит проверку в настоящем браузере. Код снижает его до 180 секунд: это мой выбор, а не настройка CapSkip. Этот предел относится только к опросу. SDK не задаёт тайм-аут для собственных HTTP-вызовов, и на имитации ответ, удержанный 20 секунд, заставил вызов ждать все 20 секунд даже при recaptchaTimeout, равном 5. Проблемы приходят как исключения, которые вы импортируете из capskip: ValidationException, когда SDK отклоняет ваши аргументы до отправки чего-либо, ApiException для кода ошибки CapSkip, TimeoutException, когда ожидание истекло, и NetworkException, когда CapSkip недоступен.

# pip install capskip requests
import re

import requests
from capskip import ApiException, CapSkip

PAGE_URL = "https://example.com/signup"  # the page that shows the widget
SITEKEY = "YOUR_SITEKEY"  # the data-sitekey of its h-captcha div

# Local mode. In Server mode, use your server's address as the host.
# With API Key Validation on, also pass apiKey="YOUR_API_KEY".
solver = CapSkip(host="127.0.0.1", port=8080, recaptchaTimeout=180)

Шаг 2: запрос токена у CapSkip

Значение sitekey публично и одинаково для всех посетителей. На большинстве страниц оно указано в контейнере виджета как data-sitekey. Страница, которая создаёт виджет скриптом, передаёт его в параметре sitekey функции hcaptcha.render(), а в адресе iframe hCaptcha оно стоит как sitekey=. Запишите его в SITEKEY, а полный адрес страницы с виджетом запишите в PAGE_URL.

Вызов выглядит так: solver.hcaptcha(SITEKEY, PAGE_URL). Он отправляет задание и опрашивает результат, пока токен не будет готов, поэтому вам не нужно хранить ID задания и писать цикл. В документации к самому API советуют сначала подождать 10 секунд, но SDK опрашивает сразу, затем ждёт 0.25 секунды и удваивает паузу после каждого ответа «не готово», до 5 секунд. По документации решение, как правило, занимает от 10 до 30 секунд. Настоящее решение я ни разу не замерял.

Результат представляет собой словарь. Поля token и code содержат одну и ту же строку, ту, что нужна для h-captcha-response, а в userAgent лежит User-Agent, с которым она получена. Поле respKey содержит второе значение, которое hCaptcha передаёт виджету. Оно присутствует всегда и пустое, если значения нет; в документации сказано, что большинство сайтов его не читает, поэтому код его игнорирует. Каждый результат выдаётся один раз, и SDK читает его за вас, поэтому функция возвращает токен и User-Agent вместе. В документации сказано, что CapSkip использует аргумент userAgent, только если он совпадает с браузером, в котором идёт решение, поэтому пример оставляет выбор за CapSkip.

Для этого метода прокси необязателен. В документации сказано, что для проверки хватает одного адреса, а постоянный поток решений с одного IP как раз и должны ловить лимиты частоты запросов hCaptcha. При большом объёме загрузите список прокси в настройках hCaptcha или отправляйте прокси с каждой задачей. Прокси, указанный в задаче, главнее списка. Функция принимает прокси в виде полного URL, например http://user:[email protected]:8080, и отправляет схему как proxytype, а остальное в поле proxy. Прокси HTTP и HTTPS работают как есть. Для SOCKS-прокси сначала выполните pip install requests[socks]. Если вы ротируете прокси, это разбирает руководство по ротации прокси шаг за шагом.

def solve(proxy=None):
    options = {}
    if proxy:  # for example http://user:[email protected]:8080
        scheme, address = proxy.split("://", 1)
        # The SDK sends type as proxytype and uri as proxy.
        options["proxy"] = {"type": scheme.upper(), "uri": address}
    result = solver.hcaptcha(SITEKEY, PAGE_URL, **options)
    return result["token"], result["userAgent"]  # delivered once: keep both

Шаг 3: отправка формы с тем же User-Agent

В браузере виджет записывает токен в скрытое поле textarea с именем h-captcha-response, а его копию в поле g-recaptcha-response. Без браузера вы отправляете h-captcha-response сами, а также g-recaptcha-response, если такое поле есть в форме. Код ниже отправляет оба. Вместе с ними идут собственные поля вашей формы, а их имена задаёт сайт. Запрос уходит на PAGE_URL, поэтому если action формы указывает на другой адрес, замените первый аргумент вызова post, то есть PAGE_URL.

User-Agent важен. В ответе указан браузер, получивший токен, и документация просит отправлять форму с этой же строкой. Поэтому функция задаёт её в сессии до загрузки страницы, и загрузка страницы и отправка формы идут с одним и тем же User-Agent. Токены одноразовые и истекают примерно через две минуты после выдачи, поэтому отправляйте сразу и запрашивайте новый токен при каждой попытке.

Запрос также указывает страницу-источник в заголовке Referer, потому что requests сам не отправляет ни Referer, ни Origin. Некоторые фреймворки отклоняют отправку формы по HTTPS, если нет ни того, ни другого. В документации Django по CSRF, например, описана строгая проверка referer для запросов HTTPS, которые приходят без заголовка Origin.

def submit_form(session, token, user_agent, email):
    # Every request from here on carries the User-Agent the token was
    # earned with, the page load included.
    session.headers["User-Agent"] = user_agent
    session.get(PAGE_URL, timeout=30)  # picks up the page's own cookies
    return session.post(
        PAGE_URL,
        data={
            "email": email,  # your form's own fields go here
            "h-captcha-response": token,
            "g-recaptcha-response": token,  # the widget fills both
        },
        headers={"Referer": PAGE_URL},  # requests sends no Referer by itself
        timeout=30,
    )

Шаг 4: полный пример с правилами повтора

Последний блок связывает шаги в один скрипт. Добавьте его в конец hcaptcha_demo.py, убедитесь, что PAGE_URL и SITEKEY вверху ваши, и запустите python hcaptcha_demo.py из папки с этим файлом (если python нет в PATH, подойдёт py hcaptcha_demo.py). Скрипт делает до трёх попыток.

Код ошибки решает, что получит новую попытку. В документации сказано, что ERROR_CAPTCHA_UNSOLVABLE означает неудачное задание, вместо которого нужно отправить новое, поэтому это единственный код в наборе для повтора. Ещё две ошибки называют свою причину, потому что повторная отправка закончилась бы так же: sitekey, который hCaptcha отклонил для этой страницы, и прокси, до которого не удаётся достучаться. Остальные коды в таблице частых ошибок указывают на запрос или настройку. SDK помещает код внутрь сообщения ApiException, голым текстом или в небольшом JSON-конверте, поэтому error_code выбирает слово ERROR_ и работает с обоими вариантами. Всё остальное сразу останавливает скрипт, включая три исключения, которые цикл не перехватывает: ValidationException, TimeoutException и NetworkException.

Форма, которую сайт отклоняет, получает новый токен и ещё одну попытку. Если три токена не проходят одинаково, посмотрите поля формы. HTTP 400 из-за отсутствующего поля повторяется, каким бы хорошим ни был токен. Кроме того, reply.ok означает лишь, что статус ниже 400. Многие сайты отвечают 200 и показывают сообщение об ошибке на странице, поэтому замените эту проверку на поиск того, что ваш сайт показывает только при успехе.

# Only a task CapSkip could not solve gets another try. A bad sitekey, a
# dead proxy or a wrong key would fail the same way again.
RETRY = {"ERROR_CAPTCHA_UNSOLVABLE"}


def error_code(err):
    # The message holds the code as bare text or inside a JSON envelope.
    found = re.search(r"ERROR_[A-Z_]+", str(err))
    return found.group(0) if found else str(err)


def sign_up(email, proxy=None):
    session = requests.Session()
    if proxy:
        session.proxies = {"http": proxy, "https": proxy}
    for attempt in range(1, 4):
        try:
            token, user_agent = solve(proxy)
        except ApiException as err:
            code = error_code(err)
            if code not in RETRY:
                raise
            print(f"attempt {attempt}: {code}, new task")
            continue
        reply = submit_form(session, token, user_agent, email)
        if reply.ok:
            return reply
        print(f"attempt {attempt}: the site answered {reply.status_code}, new token")
    raise RuntimeError("no accepted form after 3 attempts")


if __name__ == "__main__":
    reply = sign_up("[email protected]")
    print(reply.status_code, reply.text[:60])

При запуске на имитациях скрипт вёл себя так, с одинаковыми результатами на Python 3.11.7 и 3.14.0:

Настройка имитацииЧто сделал скрипт
Токен готов при втором опросеОдна задача, затем форма принята со статусом 200
Первые два решения отклоненыДве строки попыток, затем статус 200 на третьем задании
Сайт отвечает 403 на первую отправку формыОдна строка с попыткой, вторая задача с новым токеном, затем статус 200
ERROR_PROXY_CONNECT_REFUSED при опросеИсключение после одной задачи, без повтора
Ошибки отправлены голым текстом вместо JSONТо же поведение, что и с JSON, включая повтор
URL прокси передан в sign_upin.php получил прокси с proxytype HTTP, а загрузка страницы и отправка формы прошли через имитацию прокси
Токен отправлен с User-Agent по умолчанию из requestsИмитация сайта ответила 403
Один и тот же токен отправлен дваждыВторая отправка получила 403
CapSkip так и не выдаёт результат, recaptchaTimeout снижен до 25 секундTimeoutException примерно через 28 секунд после вызова, потому что последняя пауза между опросами длится 5 секунд

Строки про сайт считайте проверкой моей собственной имитации. Её правила следуют документации CapSkip, кроме того, что я добавил проверку сессионного cookie и требование, чтобы оба поля токена совпадали. Они ничего не доказывают ни про hCaptcha, ни про настоящий сайт.

Частые ошибки

ОшибкаПричинаИсправить
ValidationExceptionSDK отклонил вызов до отправки: пустой sitekey, адрес страницы, который не является полным URL с http или https, значение invisible, отличное от 0, 1, true или false, неподдерживаемый тип прокси либо параметр, которого не принимает API hCaptcha в CapSkip, например domain из 2CaptchaПрочитайте сообщение, в котором названа проблема, и исправьте аргумент
ERROR_RECAPTCHA_INVALID_SITEKEYhCaptcha отклонил sitekey для этой страницы. Пустой sitekey досюда не доходит, потому что SDK отклоняет его раньшеЕщё раз скопируйте data-sitekey со страницы и проверьте, что PAGE_URL ведёт на страницу, где показан виджет
ERROR_CAPTCHA_UNSOLVABLEhCaptcha не пройденаОтправьте новую задачу. Цикл делает это до трёх раз
ERROR_PROXY_FORMATCapSkip не смог прочитать значение проксиПроверьте значение прокси. CapSkip читает IP:PORT, LOGIN:PASSWORD@IP:PORT и IP:PORT:LOGIN:PASSWORD, а этот скрипт разбивает URL вида http://user:pass@IP:PORT на proxytype и адрес после схемы
ERROR_PROXY_CONNECT_REFUSEDЧерез прокси не удалось связаться с целью. Я смог только имитировать этоПроверьте прокси отдельно, затем перепроверьте его тип и логин
ERROR_TASK_NOT_SUPPORTEDЭтот CapSkip не принимает данный тип задачи. Возможные причины: версия старше 4.0.0 или Service Emulator, выбранный для сервиса, который отклоняет hCaptchaОбновите CapSkip до 4.0.0 или новее и оставьте Service Emulator в значении по умолчанию
ERROR_WRONG_USER_KEY или ERROR_KEY_DOES_NOT_EXISTКлюч неверен, пока включена API Key Validation. SDK отправляет строку capskip, если вы не передаёте apiKeyПередайте свой ключ как аргумент apiKey в CapSkip()
TimeoutExceptionТокен не пришёл за 180 секунд ожидания, которые задаёт recaptchaTimeout в шаге 1, либо CapSkip перестал отвечать, пока SDK его опрашивал, а SDK считает это ответом «не готово». Это исключение не входит в набор для повтораОткройте CapSkip, посмотрите задачу в его Monitor, затем запустите скрипт снова
NetworkExceptionПо хосту и порту, которые вы передали в CapSkip(), никто не слушает, либо CapSkip ответил статусом ошибки HTTPЗапустите CapSkip, затем сравните хост и порт с настройками подключения
Сайт отклоняет токен, который выглядит вернымТокен уже использовался, был старше примерно двух минут, получен с другим User-Agent, либо в запросе не было Referer. Первые три причины взяты из документации CapSkipОтправляйте каждый токен один раз и сразу, с User-Agent из того же ответа и со страницей в качестве Referer

Если сайт всё равно отклоняет токен

Некоторые сайты, особенно с hCaptcha Enterprise, отклоняют токены, которые hCaptcha оценила как рискованные, хотя виджет пройден. В документации CapSkip такие sitekey названы строгими. Для hCaptcha документация советует режим High score mode в разделе Settings, Captcha types, hCaptcha. Ему нужен Google Chrome, установленный на ПК с CapSkip. В списке изменений для 4.0.0 сказано, что режим включён по умолчанию, а когда он выключен или Chrome отсутствует, hCaptcha решается в CapSkip Custom Browser. Документация также просит хороший прокси и отправку формы с того же адреса и с User-Agent из ответа. Проверить это без настоящего строгого сайта я не мог, поэтому считайте это рекомендациями документации CapSkip, а не тем, что проверил я.

Использование CapSkip на сервере из вашего скрипта

От CapSkip скрипту нужны только хост и порт. В режиме Server CapSkip принимает соединения и с других устройств в вашей сети, или из интернета на VPS с Windows, а руководство по настройке рекомендует статический публичный IP для стабильного доступа. Режиму Server нужно, чтобы CapSkip был запущен от имени администратора. Передайте в CapSkip() адрес и порт сервера, и больше ничего в скрипте менять не придётся. Не меняются и владение с оплатой: машина ваша, и никакого счётчика нет. При этом порт остаётся открытым для других адресов, поэтому создайте блокирующее правило Windows Firewall для тех, кому вы не доверяете.

Теперь решение выполняется на сервере. Для строгого sitekey документация просит один и тот же адрес с обеих сторон, поэтому передайте аргументу proxy функции sign_up прокси, и он попадёт и в задачу, и в вашу сессию.

FAQ

Можно ли использовать асинхронный клиент?

Да. AsyncCapSkip принимает те же аргументы конструктора, а если дождаться его метода hcaptcha() с тем же sitekey и адресом страницы, он вернёт тот же словарь. Он использует httpx, который pip устанавливает вместе с capskip. Я запускал его только на имитации.

Можно ли обращаться к CapSkip с размещённой платформы?

Да, через режим Server. Запустите CapSkip на машине с Windows и доступным адресом, передайте этот адрес в CapSkip(), и вызовы останутся теми же. Решатель остаётся на оборудовании, которое вы контролируете, а платформа лишь отправляет ему HTTP-запросы.

А как насчёт невидимых виджетов и Enterprise?

Они используют тот же метод hcaptcha(). У невидимого виджета атрибут data-size равен invisible, поэтому передайте invisible=True. Для сайта Enterprise, который задаёт значение rqdata, передайте его как data и получайте новое для каждого задания. На имитации in.php в обоих случаях получил invisible=1 и значение data. Ни тот, ни другой вариант я не запускал на настоящем сайте.

Коротко

Решение hCaptcha на Python сводится к одному вызову hcaptcha() и отправке формы с тем User-Agent, с которым получен токен. Повторяйте попытку, только когда CapSkip не смог решить задание, а любая другая ошибка пусть останавливает скрипт. Поскольку CapSkip предоставляет локальный сервис распознавания капчи по фиксированной лицензии, повторная отправка отклонённого токена требует лишь нового запуска цикла. Вызовы SDK для других типов капчи и то, как виджет проходит проверку, смотрите на странице сервиса распознавания капчи для Python и в демо hCaptcha.