Files
autoreg-skills-max/skills/anti-detection-browser/SKILL.md

15 KiB
Raw Blame History

name, description, category
name description category
anti-detection-browser Browser automation with Cloudflare Turnstile & CDP-detection bypass. DrissionPage + Turnstile patch extension + proxy auth forwarder. Use when headless browsers are blocked by Cloudflare or anti-bot detection. automation

Anti-Detection Browser Automation

Обход Cloudflare Turnstile и CDP-детекции при автоматизации браузера.

Когда использовать

  • Cloudflare блокирует headless-браузеры (Playwright, Puppeteer, Selenium)
  • Страница показывает "Attention Required! | Cloudflare" или "Sorry, you have been blocked"
  • MouseEvent.screenX/screenY = 0 детектится как CDP-браузер
  • Нужна регистрация/логин на сайтах с Turnstile (x.ai, Clerk, TokenHarbor)

Почему Playwright не работает

Playwright использует CDP (Chrome DevTools Protocol). Cloudflare Turnstile детектит CDP-браузеры через:

  • MouseEvent.screenX/screenY = 0 (в headless-режиме координаты мыши не эмулируются)
  • navigator.webdriver = true
  • Отсутствие реальных движений мыши перед кликом

Решение: DrissionPage + Turnstile Patch

DrissionPage — библиотека управления Chromium, которая не использует WebDriver и не выставляет navigator.webdriver.

Turnstile patch — Chrome extension (Manifest V3), который инжектит скрипт в MAIN world на document_start, переопределяя MouseEvent.prototype.screenX/Y случайными реалистичными значениями.

Структура расширения

turnstilePatch/
├── manifest.json
└── script.js

manifest.json:

{
  "manifest_version": 3,
  "name": "Turnstile Patch",
  "version": "1.0",
  "content_scripts": [{
    "matches": ["<all_urls>"],
    "js": ["script.js"],
    "run_at": "document_start",
    "world": "MAIN"
  }]
}

script.js:

Object.defineProperty(MouseEvent.prototype, 'screenX', {
  get: function() { return Math.floor(Math.random() * 400) + 800; }
});
Object.defineProperty(MouseEvent.prototype, 'screenY', {
  get: function() { return Math.floor(Math.random() * 200) + 400; }
});

Настройка DrissionPage

from DrissionPage import Chromium, ChromiumOptions

co = ChromiumOptions()
co.auto_port()
co.set_argument("--no-sandbox")
co.set_argument("--disable-gpu")
co.set_argument("--disable-dev-shm-usage")
co.set_argument("--disable-software-rasterizer")

# Загрузка Turnstile patch
EXTENSION_PATH = os.path.abspath("turnstilePatch")
co.add_extension(EXTENSION_PATH)

# Прокси (если без аутентификации)
co.set_proxy("http://proxy:port")

browser = Chromium(co)

Ожидание Turnstile

После навигации нужно ждать 15-30 секунд, пока Turnstile пройдёт в non-interactive режиме:

page.get("https://target.com/sign-up")
run_logger.info("Waiting for Cloudflare Turnstile...")
deadline = time.time() + 30
while time.time() < deadline:
    has_challenge = page.run_js(
        "return document.title.includes('Attention Required') || "
        "document.querySelector('#challenge-running') !== null || "
        "document.body.innerText.includes('verifying you are human')"
    )
    if not has_challenge:
        break
    time.sleep(2)

Поиск кнопок с fallback

На странице может быть разный язык. Использовать JS-поиск по тексту:

const candidates = Array.from(document.querySelectorAll('button, a, [role="button"]'));
const target = candidates.find(node => {
    const text = (node.innerText || node.textContent || '').replace(/\s+/g, '').toLowerCase();
    return text.includes('signupwithemail') ||
           text.includes('continue with email') ||
           text.includes('sign up with email') ||
           text.includes('use email') ||
           text.includes('email');
});
if (target) target.click();

Proxy Auth Forwarder

DrissionPage и nodriver НЕ поддерживают прокси с аутентификацией. Ошибка: "你似乎在设置使用账号密码的代理,暂时不支持这种代理".

Решение: локальный forward proxy, который добавляет Proxy-Authorization header.

⚠️ HTTP-based forwarder НЕ работает с Chrome

http.server.HTTPServer + BaseHTTPRequestHandler ломает CONNECT-туннели для Chrome — браузер получает chrome-error://chromewebdata/. Curl работает, Chrome — нет.

Использовать RAW TCP forwarder (см. scripts/tcp_forwarder.py):

import socket, threading, select, base64

UPSTREAM_HOST = 'residential-x.bpproxy.at'
UPSTREAM_PORT = 1000
AUTH = base64.b64encode(b'user:pass').decode()
LOCAL_PORT = 9129

def handle_client(client_sock):
    data = b''
    while b'\r\n\r\n' not in data:
        chunk = client_sock.recv(4096)
        if not chunk: return
        data += chunk
    
    request = data.decode('latin-1', errors='replace')
    first_line = request.split('\r\n')[0]
    method, target, _ = first_line.split(' ', 2)
    
    if method == 'CONNECT':
        host, port_str = target.rsplit(':', 1)
        port = int(port_str)
        remote = socket.create_connection((UPSTREAM_HOST, UPSTREAM_PORT), timeout=10)
        connect_req = f'CONNECT {host}:{port} HTTP/1.1\r\nHost: {host}:{port}\r\nProxy-Authorization: Basic {AUTH}\r\n\r\n'
        remote.send(connect_req.encode())
        resp = b''
        while b'\r\n\r\n' not in resp:
            resp += remote.recv(4096)
        if b'200' not in resp.split(b'\r\n')[0]:
            client_sock.send(b'HTTP/1.1 502 Bad Gateway\r\n\r\n')
            remote.close()
            return
        client_sock.send(b'HTTP/1.1 200 Connection Established\r\n\r\n')
        client_sock.settimeout(30)
        remote.settimeout(30)
        socks = [client_sock, remote]
        while True:
            r, _, _ = select.select(socks, [], [], 30)
            if not r: break
            for s in r:
                d = s.recv(8192)
                if not d: return
                dst = remote if s is client_sock else client_sock
                dst.sendall(d)

server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server.bind(('127.0.0.1', LOCAL_PORT))
server.listen(50)
while True:
    client, addr = server.accept()
    threading.Thread(target=handle_client, args=(client,), daemon=True).start()

Запуск:

python tcp_forwarder.py &
# Затем использовать http://127.0.0.1:9129 как browser_proxy

Geo-Blocking

Сервисы типа x.ai гео-блокируют по стране выхода прокси. Проверять перед использованием:

curl -x "http://user:pass@proxy:port" -s http://ip-api.com/json
# Проверить поле "country" — x.ai доступен из US, CA, UK, EU, но НЕ из IQ, RU, CN

Nodriver: Fallback When DrissionPage Fails

DrissionPage может быть детектирован Cloudflare даже с Turnstile patch. Симптом: PageDisconnectedError: 与页面的连接已断开 — Cloudflare активно рвёт CDP-соединение.

Nodriver (pip install nodriver) — альтернатива с встроенной антидетект-защитой. Не требует отдельного Turnstile patch.

Настройка nodriver

import nodriver as uc

browser = await uc.start(
    headless=False,
    browser_args=[
        "--window-size=1280,900",
        "--no-first-run",
        "--no-default-browser-check",
        "--disable-blink-features=AutomationControlled",
        "--proxy-server=http://127.0.0.1:9129",  # через proxy forwarder
        "--ignore-certificate-errors",
    ],
)
tab = await browser.get("https://accounts.x.ai/sign-up?redirect=grok-com")

API ключевые отличия от DrissionPage

  • Async: все методы await
  • tab.find(text) — поиск элемента по тексту (аналог page.run_js(...find text...))
  • tab.select(selector) — CSS-селектор
  • tab.evaluate(js) — выполнить JS
  • el.click(), el.send_keys(text) — взаимодействие
  • Обернуть в asyncio.wait_for() для таймаутов

Google OAuth Bypass для accounts.x.ai

Проблема: accounts.x.ai блокирует ВСЕ bpproxy residential IP (403) напрямую, но Google OAuth (accounts.google.com) принимает те же прокси (302).

Решение: использовать Google OAuth flow вместо прямой email-регистрации:

  1. Зайти на accounts.x.ai/sign-up?redirect=grok-com
  2. Нажать «Continue with Google» (не «Use email»)
  3. Google OAuth-страница загружается через прокси (Google принимает bpproxy)
  4. Ввести Gmail + пароль + TOTP (если 2FA)
  5. Пройти consent screen
  6. Редирект обратно на Grok

Требования: Gmail аккаунты с 2FA-секретами (формат как в tusk_gmail_accounts.json).

# Пример: поиск кнопки Google
clicked = await click_text(tab, [
    "Continue with Google", "Sign in with Google",
    "Sign up with Google", "Google",
], timeout=10)

Port Conflicts: Multiple Proxy Forwarders

Никогда не запускать несколько экземпляров proxy forwarder на одном порту — они конфликтуют и порт становится недоступным. При смене upstream-прокси:

  1. Убить старый forwarder: taskkill /F /PID <pid>
  2. Проверить что порт свободен: netstat -ano | grep <port>
  3. Запустить новый forwarder на уникальном порту

Рекомендация: использовать разные порты для разных upstream-прокси (9128, 9129, 9130...).

Nodriver browser.stop() не awaitable

browser.stop() в nodriver возвращает None — не использовать await:

# ❌ ОШИБКА: TypeError: object NoneType can't be used in 'await' expression
await browser.stop()

# ✅ ПРАВИЛЬНО
browser.stop()

TCP Forwarder: всегда вместо HTTP-based

HTTP-based forwarder (http.server.HTTPServer) не работает с Chrome — CONNECT-туннели ломаются. Всегда использовать RAW TCP forwarder (scripts/tcp_forwarder.py).

Pitfalls

Playwright Headless Blocked by Cloudflare (General)

Playwright's headless Chromium is detected by Cloudflare on many sites, not just those with Turnstile. Symptoms: page.goto() times out, empty page content, or Cloudflare challenge page. Use headless=False with a persistent Chrome user profile (launch_persistent_context), or connect via CDP to an existing Chrome instance (connect_over_cdp).

Playwright CDP Works, Headless Doesn't

connect_over_cdp("http://127.0.0.1:9222") to an existing Chrome with remote debugging works reliably because it uses the real Chrome profile. Headless Playwright (launch()) is detected. For automation that needs Cloudflare bypass, always prefer CDP connection over headless launch.

Nodriver Windows Asyncio Crashes (2026-08-21)

Nodriver (uc.start()) crashes on Windows with ValueError: I/O operation on closed pipe in asyncio.proactor_events. This is a Windows-specific asyncio issue. Fall back to Playwright with headless=False or CDP connection.

MCP Browser as Reliable Fallback

When Playwright and nodriver both fail, the Chrome DevTools MCP (mcp__chrome_devtools__* tools) works reliably. It uses the existing Chrome instance with the user's profile, avoiding Cloudflare detection entirely. Use evaluate_script with nativeInputSetter for fastest form interactions.

Standalone Chrome + CDP: самопроходящий Turnstile (2026-09-10)

Когда все MCP/headless-пути висят на SPA-бандле (белая страница, бандл pending, хотя curl качает 200), а firecrawl_interact рвёт сессии — поднять отдельный Chrome с --remote-debugging-port=9333 --user-data-dir=<fresh> (background=true), подключиться Playwright connect_over_cdp, заполнить форму и поллить скрытое поле cf-turnstile-response до length>10 — в реальном Chrome non-interactive Turnstile решается сам за 10-20 сек. Полный разбор (RainProxy): references/rainproxy-cdp-turnstile.md — там же pitfalls: has-text("Login") ловит "Login with Google", Gmail-алиасы с + отклоняются, UTF-7 имена папок Gmail при русской локали.

accounts.x.ai blocks ALL bpproxy residential IPs (2026-08-14)

Все 25 bpproxy residential прокси возвращают 403 при прямом обращении к accounts.x.ai. Google (accounts.google.com) принимает те же прокси. Использовать Google OAuth flow вместо прямого email signup.

DrissionPage CDP disconnection

Cloudflare может детектировать и разрывать CDP-соединение DrissionPage (PageDisconnectedError). Переключиться на nodriver.

DrissionPage proxy auth

DrissionPage не поддерживает user:pass@host:port в URL прокси. Использовать proxy forwarder.

Turnstile timing

Non-interactive Turnstile требует 5-30 секунд. Недостаточное ожидание → кнопки не найдены (страница ещё не загрузилась после challenge).

Geo-blocking bpproxy

bpproxy residential выходят из разных стран (Ирак, Канада, etc.). Всегда проверять ip-api.com/json перед использованием с гео-зависимыми сервисами.

bpproxy hardsession истекает

Hardsession-прокси могут умереть через несколько часов. Периодически перегенерировать пул через bpproxy skill.

Связанные скиллы

  • bpproxy — генерация и управление прокси
  • bulk-account-registration — пайплайн массовой регистрации

Связанные файлы

  • references/grok-registration-pipeline.md — конкретный пайплайн регистрации Grok (x.ai) с DrissionPage
  • scripts/proxy_forwarder.py — HTTP forward proxy (DEPRECATED — не работает с Chrome, используй tcp_forwarder.py)
  • scripts/tcp_forwarder.py — RAW TCP forward proxy (работает с Chrome, nodriver, DrissionPage)
  • templates/turnstile-patch/ — шаблон Chrome extension для обхода CDP-детекции (manifest.json + script.js)