Эта статья была полезной?
Как обойти Cloudflare Turnstile с помощью Playwright
Технический специалист
Cloudflare Turnstile — это современная и более дружелюбная к приватности альтернатива классическим капчам. Вместо того чтобы просить пользователя кликать по картинкам, Turnstile незаметно анализирует поведение браузера, окружение и репутацию IP-адреса, чтобы отличить человека от бота. В большинстве случаев проверка проходит невидимо — но для скриптов автоматизации, QA-тестирования и скрапинга Turnstile всё равно может заблокировать доступ к нужной странице.
В этом туториале показано, как обойти Cloudflare Turnstile в Playwright с помощью API 2Captcha: найти виджет, получить решённый токен от 2Captcha, вставить его на страницу и продолжить автоматизацию так, будто проверку прошёл реальный пользователь.
Что понадобится
- Установленный Node.js или Python
- Установленный Playwright для вашего языка
- Аккаунт 2Captcha и API-ключ (получить здесь)
- URL целевой страницы и, если для виджета нужна привязка к конкретному IP, рабочий прокси
Есть два разных сценария:
- Обычный виджет Turnstile — капча встроена прямо в страницу (например, в форму регистрации или входа). Именно этот случай разобран в туториале.
- Cloudflare Challenge page — Cloudflare полностью заменяет страницу экраном проверки перед тем, как пропустить браузер дальше. Этот сценарий требует дополнительных параметров (cData, chlPageData, action) и разбирается в отдельном туториале.
Шаг 1. Установка зависимостей
Для Python:
bash
pip install playwright 2captcha-python
playwright install chromium
Для Node.js:
bash
npm install playwright @2captcha/captcha-solver
npx playwright install chromium
Шаг 2. Запуск браузера и открытие целевой страницы
Начните с обычной сессии Playwright. Пока вы пишете и тестируете скрипт, запускайте браузер в видимом режиме (headless=False) — так гораздо легче видеть, что происходит на странице.
python
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch(headless=False)
page = browser.new_page()
page.goto("https://2captcha.com/demo/cloudflare-turnstile")
page.wait_for_selector(".cf-turnstile")
Шаг 3. Извлечение sitekey из виджета Turnstile
Каждый виджет Turnstile хранит свой sitekey в атрибуте data-sitekey контейнера .cf-turnstile. Достать его из DOM можно через get_attribute:
python
sitekey = page.locator(".cf-turnstile").get_attribute("data-sitekey")
page_url = page.url
print(f"Sitekey: {sitekey}")
print(f"Page URL: {page_url}")
Если виджет рендерится динамически через JavaScript (turnstile.render(...)), а не как статичный div, поищите sitekey в исходном коде страницы или перехватите сетевой запрос к challenges.cloudflare.com — там sitekey передаётся как query-параметр.
Шаг 4. Отправка sitekey в 2Captcha и получение токена
Имея sitekey и URL страницы, отправьте их в 2Captcha. Python SDK сам создаёт задачу и опрашивает результат, пока капча не будет решена:
python
from twocaptcha import TwoCaptcha
solver = TwoCaptcha('YOUR_API_KEY')
result = solver.turnstile(
sitekey=sitekey,
url=page_url
)
token = result['code']
print(f"Получен токен: {token}")
Это вызывает метод turnstile официального SDK 2captcha-python, который отправляет задачу в API 2Captcha и опрашивает результат, пока капча не будет решена. Решение обычно занимает 10–20 секунд. Если виджету нужны дополнительные параметры (action, data/cData, pagedata), передайте их как дополнительные именованные аргументы — solver.turnstile(sitekey=sitekey, url=page_url, action='login', data='...').
Если вы обращаетесь напрямую к API, а не через SDK, запрос выглядит так (proxyless-режим, используется, когда виджету не нужен прокси, привязанный к конкретному IP):
bash
curl -X POST https://api.2captcha.com/createTask \
-H "Content-Type: application/json" \
-d '{
"clientKey": "YOUR_API_KEY",
"task": {
"type": "TurnstileTaskProxyless",
"websiteURL": "https://2captcha.com/demo/cloudflare-turnstile",
"websiteKey": "3x00000000000000000000FF"
}
}'
Если нужен прокси, привязанный к задаче, используйте type: "TurnstileTask" и добавьте поля proxyType, proxyAddress, proxyPort, proxyLogin, proxyPassword — подробнее в документации по API Cloudflare Turnstile.
Шаг 5. Вставка токена на страницу
Получив токен, нужно поместить его в скрытое поле, которое создаёт Turnstile — обычно это . Так как это поле не принимает ввод через клавиатуру, значение нужно выставить напрямую через page.evaluate:
python
page.evaluate(f"""
document.querySelector('input[name="cf-turnstile-response"]').value = "{token}";
""")
На некоторых страницах одного значения поля недостаточно — сайт ожидает срабатывания callback-функции виджета, поскольку именно она активирует кнопку «Отправить» или запускает дополнительную клиентскую логику. Если callback настроен, вызовите его явно — вместо простой установки значения или вместе с ней:
python
page.evaluate(f"""
if (window.turnstileCallback) {{
window.turnstileCallback("{token}");
}}
""")
Узнать имя callback-функции можно по атрибуту data-callback элемента .cf-turnstile, либо посмотрев вызов turnstile.render() в JavaScript-коде страницы.
Шаг 6. Продолжение автоматизации
На этом этапе страница считает, что проверку прошёл живой человек. Отправляйте форму или продолжайте навигацию как обычно:
python
page.click("button[type='submit']")
page.wait_for_load_state("networkidle")
browser.close()
Полный рабочий пример (Python)
python
from playwright.sync_api import sync_playwright
from twocaptcha import TwoCaptcha
API_KEY = "YOUR_API_KEY"
TARGET_URL = "https://2captcha.com/demo/cloudflare-turnstile"
solver = TwoCaptcha(API_KEY)
with sync_playwright() as p:
browser = p.chromium.launch(headless=False)
page = browser.new_page()
page.goto(TARGET_URL)
page.wait_for_selector(".cf-turnstile")
sitekey = page.locator(".cf-turnstile").get_attribute("data-sitekey")
result = solver.turnstile(sitekey=sitekey, url=TARGET_URL)
token = result['code']
page.evaluate(f"""
document.querySelector('input[name="cf-turnstile-response"]').value = "{token}";
if (window.turnstileCallback) {{
window.turnstileCallback("{token}");
}}
""")
page.click("button[type='submit']")
page.wait_for_load_state("networkidle")
browser.close()
Версия на Node.js (Playwright + JavaScript)
javascript
const { chromium } = require('playwright');
const Captcha = require('@2captcha/captcha-solver');
const solver = new Captcha.Solver('YOUR_API_KEY');
const targetUrl = 'https://2captcha.com/demo/cloudflare-turnstile';
(async () => {
const browser = await chromium.launch({ headless: false });
const page = await browser.newPage();
await page.goto(targetUrl);
await page.waitForSelector('.cf-turnstile');
const sitekey = await page.locator('.cf-turnstile').getAttribute('data-sitekey');
const result = await solver.cloudflareTurnstile({
pageurl: targetUrl,
sitekey: sitekey
});
const token = result.data;
await page.evaluate((token) => {
document.querySelector('input[name="cf-turnstile-response"]').value = token;
if (window.turnstileCallback) {
window.turnstileCallback(token);
}
}, token);
await page.click("button[type='submit']");
await page.waitForLoadState('networkidle');
await browser.close();
})();
Решение проблем
- Токен отклоняется / форма не отправляется — проверьте, что значение подставляется в поле с правильным именем, и что callback (если он есть) действительно вызывается.
- Sitekey не найден — виджет может подгружаться внутри iframe уже после загрузки страницы; дождитесь сетевого запроса к challenges.cloudflare.com/turnstile и возьмите sitekey из его query-строки вместо DOM.
- Работает с headless: False, но не работает с headless: True — Cloudflare также учитывает фингерпринт браузера при оценке риска. Комбинируйте решённый токен с прокси и реалистичным user-agent, а также рассмотрите playwright-extra со stealth-плагином, чтобы снизить признаки headless-режима.
- Токен получен, но всё равно редиректит на Challenge page — вы столкнулись со сценарием Cloudflare Challenge page, а не обычным виджетом. Для этого случая нужны дополнительные параметры cData/chlPageData/action, которые описаны в туториале по Cloudflare Challenge.