Как решить DataDome CAPTCHA на 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, viaCookie задаётся в заголовке вручную, поэтому 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=bv | ValidationException, и имитация CapSkip не получила ни одного запроса |
| Cookie отправлено с другим User-Agent | Имитация сайта ответила 403 |
| Cookie отправлено с другого IP-адреса | Имитация сайта ответила 403 на cookie, которое CapSkip выдал через прокси, но отправленное без него |
Последние две строки проверяют правила моей имитации, а они следуют документации CapSkip. Это не доказательство того, как ведёт себя настоящий сервис.
Частые ошибки
| Ошибка | Причина | Исправить |
|---|---|---|
| ValidationException | captcha_url, который не является адресом проверки DataDome, страница блокировки с t=bv, pageurl не с http или https, или userAgent не от Chrome для Windows. Сообщение говорит, какой из этих случаев имеет место | Соберите адрес заново из свежей страницы блокировки, смените адрес, если он заблокирован, и уберите аргумент userAgent |
| ApiException с ERROR_WRONG_USERAGENT | User-Agent относится к Chrome для Windows, но его основная версия отличается от версии браузера CapSkip | Не передавайте userAgent и используйте тот, который указан в результате |
| ApiException с ERROR_CAPTCHA_UNSOLVABLE | Проверка не была пройдена | Запросите страницу заново и отправьте её новый captcha_url |
| ApiException с ERROR_PROXY_BANNED | CapSkip отказывается принимать задачу, если на странице блокировки t=bv, согласно документации API | Смените прокси и запросите страницу заново |
| ApiException с ERROR_PROXY_CONNECT_REFUSED | CapSkip сообщает об этом, когда не может подключиться через прокси. Воспроизвести это мне не удалось | Проверьте адрес, логин и тип прокси |
| TimeoutException | CapSkip не дал ответа за 300 секунд ожидания. Это не ApiException, поэтому цикл его не перехватывает | Проверьте, что CapSkip запущен и решает, затем запустите скрипт снова |
| NetworkException | CapSkip не запущен либо неверно указаны хост или порт | Запустите 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.
