Как решить DataDome CAPTCHA на Python (requests)

datadome captcha in python - How to Solve DataDome CAPTCHA in Python (requests)

Чтобы решить DataDome CAPTCHA на Python, вы читаете страницу блокировки с кодом 403, собираете по ней адрес проверки и отправляете этот адрес в решатель CapSkip для DataDome через datadome() и повторяете запрос с возвращённым cookie. Ответом служит cookie, а не токен для формы, и в документации CapSkip сказано, что оно работает только с тем User-Agent и IP-адресом, с которыми было получено. В этом руководстве весь цикл собран с CapSkip Python SDK и requests примерно в 90 строк. CapSkip работает на вашем собственном ПК с Windows без платы за каждое решение, поэтому повтор ничего не стоит.

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

  • CapSkip 3.2.0 или новее, запущенный на ПК с Windows. В этом выпуске появилось решение DataDome для ползунка и проверки устройства. Если вы запускаете CapSkip впервые, руководство по настройке проведёт вас через первый запуск.
  • Python 3.10 или новее и пакет capskip версии 1.5.0 или новее. В более ранних версиях нет метода datadome. Код ниже запускался на Python 3.11.7 и 3.14.0 с capskip 1.5.0 и requests 2.34.2. На Python 3.10 код здесь не проверялся.
  • Окно PowerShell. Команды написаны в синтаксисе PowerShell.
  • Страница, которой вы владеете или которую вам разрешено автоматизировать, за защитой DataDome.
  • Адрес решателя. В режиме Local CapSkip отвечает на 127.0.0.1 и только для этого устройства. В режиме Server он слушает ваш сетевой адрес или публичный IP, чтобы скрипт на другом ПК, VPS или размещённой платформе мог обращаться к нему через API. Оба режима задаются в разделе Настройки подключения.
  • Sticky-прокси, то есть прокси с постоянным IP-адресом, на каждую задачу, когда вы перейдёте от тестов к работе. Cookie привязано к IP-адресу, с которого оно получено, и это объясняет шаг 3.

Как это проверялось: код запускался на трёх имитациях серверов, которые я написал, а не на настоящем сайте с DataDome. Первая копирует проверки, перечисленные в разделе DataDome на странице CapSkip Документация API, вторая изображает защищённый сайт, который отдаёт страницу блокировки с кодом 403 и принимает только cookie, выданное CapSkip, вместе с тем User-Agent, для которого оно выдано, а третья изображает прокси-сервер. SDK был настоящим, capskip 1.5.0. Приложение CapSkip на моём ПК не было запущено, поэтому здесь нет ничего, что показывало бы, сколько длится настоящее решение или что принимает живой сайт с DataDome. Правила для cookie взяты из документации CapSkip. Собственная документация DataDome по cookie сообщает, что cookie datadome действует один год, и предупреждает не менять его Domain и Path. К чему привязано cookie, в ней не сказано.

Шаг 1: чтение страницы блокировки и сборка captcha_url

Когда DataDome сомневается в запросе, сайт отвечает статусом 403, иногда 401, и небольшой HTML-страницей вместо содержимого. На этой странице есть объект скрипта с именем dd, и она загружает проверку в iframe с geo.captcha-delivery.com. Именно адрес iframe нужен CapSkip, и в документации он называется captcha_url. Если iframe уже есть на странице, можно взять его src как есть. Большинство страниц блокировки строят iframe скриптом, поэтому функция ниже собирает такой же адрес из объекта dd.

Значение rt выбирает путь. Значение c означает ползунок под /captcha/, а i означает проверку устройства под /interstitial/. Важно и значение t: fe означает проверку, а bv означает, что DataDome заблокировал IP-адрес, с которого была загружена страница. Первый запрос уходит с обычным User-Agent от Chrome. Эта строка в CapSkip не передаётся, поэтому версия Chrome в ней не обязана ни с чем совпадать. Я не проверял, отдаёт ли живой сайт с DataDome ту же проверку при другом User-Agent, так что считайте эту часть непроверенной.

# pip install capskip requests
import json
import re
from urllib.parse import urlencode

import requests

PAGE_URL = "https://www.example.com/"  # a page behind DataDome
BROWSER = {  # for the first request only; the cookie comes with its own User-Agent
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
                  "(KHTML, like Gecko) Chrome/156.0.0.0 Safari/537.36",
}


def get(headers, via=None):
    proxies = {"http": via, "https": via} if via else None
    return requests.get(PAGE_URL, headers=headers, proxies=proxies, timeout=30)


def captcha_url_from(resp):
    """Build the challenge address from a DataDome block page, or None."""
    if resp.status_code not in (401, 403):
        return None
    found = re.search(r"var\s+dd\s*=\s*(\{[^}]*\})", resp.text)
    if not found:
        return None
    dd = json.loads(found.group(1).replace("'", '"'))
    path = "interstitial" if dd["rt"] == "i" else "captcha"
    query = {"initialCid": dd["cid"], "hash": dd["hsh"], "cid": dd["cookie"]}
    if dd.get("t"):
        query["t"] = dd["t"]  # fe is a challenge, bv means the address is banned
    query.update(referer=PAGE_URL, s=dd["s"])
    for key in ("e", "b"):
        if dd.get(key):
            query[key] = dd[key]
    query["dm"] = "cd"
    return f"https://{dd.get('host', 'geo.captcha-delivery.com')}/{path}/?{urlencode(query)}"

Функция возвращает None для любого ответа, который не является страницей блокировки, чтобы скрипт мог отличить “решать нечего” от “решено”. Каждая страница блокировки несёт новую проверку. Используйте её captcha_url сразу и запрашивайте страницу заново для следующей задачи, а не берите старый адрес.

Шаг 2: решение через datadome()

Метод SDK называется datadome(captcha_url, pageurl, userAgent, proxy) и следует порядку аргументов 2Captcha. Первые два обязательны. В CapSkip userAgent и proxy необязательны. Если User-Agent не передать, CapSkip использует User-Agent своего браузера и вернёт его в результате как userAgent. Если передать, это должен быть Chrome для Windows с той же основной версией, что и у браузера CapSkip, поэтому проще всего его не указывать. Метод ждёт ответа дольше, с тайм-аутом 300 секунд, как и для reCAPTCHA, потому что CapSkip проходит проверку в настоящем браузере. В документации API сказано, что решение обычно занимает от 3 до 20 секунд; я это время не замерял.

Прокси здесь тот же, которым будут пользоваться ваши следующие запросы. Передайте его, если скрипт выходит через прокси, и ничего не передавайте, если запросы уходят с машины, на которой работает CapSkip. Если CapSkip стоит на сервере, этой машиной будет сервер.

from capskip import CapSkip

# Local mode. In Server mode, use your server's address instead.
solver = CapSkip(host="127.0.0.1", port=8080)


def proxy_arg(proxy_url):
    if not proxy_url:
        return None  # your requests leave from the PC that runs CapSkip
    scheme, rest = proxy_url.split("://", 1)
    return {"type": scheme.upper(), "uri": rest}  # e.g. HTTP, user:pass@ip:port


def solve(captcha_url, proxy_url=None):
    # No userAgent argument: CapSkip uses its own browser's and reports it.
    return solver.datadome(captcha_url, PAGE_URL, proxy=proxy_arg(proxy_url))

Если проверку устройства пройти нельзя, DataDome переходит к ползунку. Согласно документации CapSkip, тот же вызов следует за этим переходом, так что отдельный код для двух проверок не нужен.

Шаг 3: отправка cookie с его User-Agent и IP-адресом

Результат представляет собой словарь с четырьмя важными полями. Поле code содержит cookie точно в том виде, в каком его установил DataDome, с Max-Age, Domain и другими атрибутами, а именно это нужно хранилищу cookie. Поле cookie содержит только пару имени и значения datadome, а именно это нужно заголовку Cookie. Поле userAgent содержит User-Agent, с которым cookie было получено. Поле proxy появляется только тогда, когда вы не передавали прокси и CapSkip решал через собственный список прокси для DataDome. В документации CapSkip сказано, что DataDome отвергает cookie с другого IP-адреса или с другим User-Agent, поэтому отправляйте один и тот же User-Agent каждый раз, а если в результате указан прокси, отправляйте запрос через него. Поле proxy содержит только хост и порт, без схемы и без логина. Функция добавляет http://, поэтому предполагает прокси из списка без логина. Если у вашего логин есть, добавьте его.

DataDome может также прислать сменное cookie в одном из следующих ответов. Функция сохраняет самое новое, как это делал бы браузер.

def send_with_cookie(result, proxy_url=None):
    # The cookie works only from the address and User-Agent that earned it.
    via = proxy_url
    if not via and result.get("proxy"):
        via = "http://" + result["proxy"]  # CapSkip solved through its own list
    headers = {"User-Agent": result["userAgent"], "Cookie": result["cookie"]}
    resp = get(headers, via)
    fresh = resp.cookies.get("datadome")  # DataDome may rotate the cookie
    if fresh:
        headers["Cookie"] = f"datadome={fresh}"
    return resp, headers, via

Cookie задаётся в заголовке вручную, поэтому requests им не управляет. Так сделано намеренно: явный заголовок приоритетнее хранилища сессии и держит cookie, User-Agent и прокси в одном месте. Одно наблюдение из запуска с прокси: текст после схемы уходит в CapSkip в том виде, как вы его написали, поэтому не используйте в логине прокси символы, которые требуют процентного кодирования. Для прокси socks5 requests дополнительно нужен компонент socks, который ставится командой pip install requests[socks].

Шаг 4: полный рабочий пример

Последний блок собирает весь процесс решения DataDome CAPTCHA на Python и добавляет повтор. Сохраните все четыре блока в один файл с именем datadome_demo.py, один за другим, и запустите его командой python datadome_demo.py. Каждая попытка заново запрашивает страницу, поэтому в каждой попытке новая проверка. Неудачное решение вызывает ApiException, и цикл пробует снова. Cookie, на которое всё равно пришёл 403, тоже отправляет цикл на новый круг, уже с новой страницей блокировки.

from capskip import ApiException


def fetch_protected(proxy_url=None):
    for attempt in range(1, 4):
        resp = get(BROWSER, proxy_url)  # a fresh block page, a fresh challenge
        captcha_url = captcha_url_from(resp)
        if captcha_url is None:
            return resp, BROWSER, proxy_url  # no block page on this response
        try:
            result = solve(captcha_url, proxy_url)
        except ApiException as err:
            print(f"attempt {attempt}: {err}")  # the next attempt gets a new page
            continue
        resp, headers, via = send_with_cookie(result, proxy_url)
        if resp.status_code != 200:
            print(f"attempt {attempt}: HTTP {resp.status_code} with the cookie")
            continue
        return resp, headers, via
    raise RuntimeError("DataDome not passed in 3 attempts")


if __name__ == "__main__":
    page, headers, via = fetch_protected()
    print(page.status_code, page.text[:40])
    again = get(headers, via)  # reuse the cookie while it lasts
    print(again.status_code)

SDK отказывается принимать captcha_url с t=bv, ничего не отправив, потому что решение с такого адреса пройти не может. Это ValidationException, а не ApiException, поэтому цикл его не перехватывает. Дайте ему подняться: это сигнал сменить адрес и начать заново. Блокировка, о которой вместо этого сообщает CapSkip как ERROR_PROXY_BANNED, приходит как ApiException, и цикл повторяет попытку с тем же адресом, поэтому если вы видите эту ошибку в выводе, смените прокси перед следующим запуском. Смотрите также: руководство по ротации прокси (о смене прокси) и отличие капчи от блокировки (о другом случае, когда страница вообще не является проверкой).

При запуске на имитации скрипт вёл себя так:

Настройка имитацииЧто сделал скрипт
Страница блокировки с ползунком, на каждое решение пришёл ответОдна задача, затем статус 200 с cookie и снова 200 при повторном использовании
Страница блокировки с проверкой устройстваВыполнился тот же код: captcha_url указывал на /interstitial/, и результат был тем же
Первые два решения отклоненыДве строки попыток, затем статус 200 на третьем задании
Первые три решения отклоненыТри строки попыток, затем RuntimeError
Результат указывает прокси, как это делает собственный список CapSkipЗапрос с cookie прошёл через этот прокси и получил 200
Ваш собственный прокси передан в решениеin.php получил прокси и его тип, и запросы использовали его
Страница блокировки с t=bvValidationException, и имитация CapSkip не получила ни одного запроса
Cookie отправлено с другим User-AgentИмитация сайта ответила 403
Cookie отправлено с другого IP-адресаИмитация сайта ответила 403 на cookie, которое CapSkip выдал через прокси, но отправленное без него

Последние две строки проверяют правила моей имитации, а они следуют документации CapSkip. Это не доказательство того, как ведёт себя настоящий сервис.

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

ОшибкаПричинаИсправить
ValidationExceptioncaptcha_url, который не является адресом проверки DataDome, страница блокировки с t=bv, pageurl не с http или https, или userAgent не от Chrome для Windows. Сообщение говорит, какой из этих случаев имеет местоСоберите адрес заново из свежей страницы блокировки, смените адрес, если он заблокирован, и уберите аргумент userAgent
ApiException с ERROR_WRONG_USERAGENTUser-Agent относится к Chrome для Windows, но его основная версия отличается от версии браузера CapSkipНе передавайте userAgent и используйте тот, который указан в результате
ApiException с ERROR_CAPTCHA_UNSOLVABLEПроверка не была пройденаЗапросите страницу заново и отправьте её новый captcha_url
ApiException с ERROR_PROXY_BANNEDCapSkip отказывается принимать задачу, если на странице блокировки t=bv, согласно документации APIСмените прокси и запросите страницу заново
ApiException с ERROR_PROXY_CONNECT_REFUSEDCapSkip сообщает об этом, когда не может подключиться через прокси. Воспроизвести это мне не удалосьПроверьте адрес, логин и тип прокси
TimeoutExceptionCapSkip не дал ответа за 300 секунд ожидания. Это не ApiException, поэтому цикл его не перехватываетПроверьте, что CapSkip запущен и решает, затем запустите скрипт снова
NetworkExceptionCapSkip не запущен либо неверно указаны хост или портЗапустите CapSkip и проверьте настройки подключения
Всё равно 403 при отправке cookieЗапрос ушёл с другого IP-адреса, с другим User-Agent, или cookie истеклоКаждый раз отправляйте userAgent и proxy из результата или запустите цикл заново с новой страницей блокировки

Запуск решателя на сервере

Скрипту нужны от CapSkip две вещи: хост и порт. В режиме Local это 127.0.0.1 и ваш собственный ПК. В режиме Server CapSkip слушает ваш сетевой адрес или публичный IP, поэтому скрипт на другом ПК, VPS или размещённой платформе может обращаться к нему через тот же API. Смените хост и порт в конструкторе CapSkip, и больше ничего не меняется. Для стабильного удалённого доступа рекомендуется статический публичный IP. Режим Server всё равно работает на вашем оборудовании и всё равно без счётчика. Он открывает порт для любого адреса, поэтому добавьте в Windows Firewall блокирующее правило для адресов, которым доступ к нему не нужен. Помните, что решение выполняется на этой машине, поэтому cookie получено с IP-адреса сервера. Если вы не передаёте прокси, запрос с cookie тоже должен уходить с этого сервера. Если прокси есть, передавайте один и тот же и в решение, и в свои запросы. Подробности смотрите в разделе Настройки подключения с описанием переключения режимов.

FAQ

Возвращает ли решатель DataDome токен?

Нет. Он возвращает cookie datadome. Это cookie вы отправляете в заголовке Cookie того запроса, который был заблокирован, вместе с тем же User-Agent. Поля формы для заполнения нет.

Нужен ли мне прокси для DataDome?

Для проверки нет. Для первого запуска хватит одного IP-адреса. Согласно документации CapSkip, cookie работает только с IP-адреса, с которого оно получено, поэтому когда запросов станет много, планируйте один sticky-прокси на задачу и передавайте один и тот же прокси и в решение, и в свои запросы.

Работает ли это и для ползунка, и для проверки устройства?

Да, через тот же вызов. Адрес captcha_url для ползунка находится под /captcha/, а для проверки устройства под /interstitial/. В документации CapSkip сказано, что если проверка устройства переходит в ползунок, вызов следует за ней и туда.

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

Да, через режим Server. Настройте CapSkip на прослушивание вашей сети или публичного IP, направьте конструктор CapSkip на этот хост и порт, и цикл работает так же, с одним изменением: решение теперь выполняется на сервере, поэтому давайте решению и своим запросам один и тот же прокси. Решатель остаётся на вашем оборудовании.

Коротко

Решение DataDome CAPTCHA на Python состоит из четырёх действий: прочитать страницу блокировки, собрать captcha_url, вызвать datadome() и повторить запрос с cookie вместе с User-Agent и IP-адресом, к которым оно относится. На вашей собственной машине повтор бесплатен, и это особенно важно, когда защищённый сайт раз за разом выдаёт новые проверки. Выбирайте локальный сервис распознавания капчи и повторяйте попытки столько раз, сколько нужно. Об остальных возможностях SDK читайте на странице сервиса распознавания капчи для Python.