Назад к блогу

Как обойти Headless Browser Detection при парсинге: маскировка Selenium и Puppeteer

Узнайте, как обойти защиту от headless-браузеров при парсинге: техники маскировки Selenium, Puppeteer и Playwright с примерами кода и настройкой прокси.

📅10 января 2026 г.

Современные сайты научились распознавать автоматизированные браузеры (Selenium, Puppeteer, Playwright) и блокируют их запросы. Headless-браузеры оставляют десятки цифровых следов, по которым антибот-системы вычисляют автоматизацию за миллисекунды. В этом руководстве разберём все методы маскировки headless-браузеров с примерами кода на Python и JavaScript, чтобы ваши парсеры работали стабильно без блокировок.

Статья предназначена для разработчиков, которые занимаются веб-скрапингом, автоматизацией тестирования или сбором данных с защищённых сайтов. Мы рассмотрим технические детали детектирования и практические решения для обхода защиты.

Как сайты детектируют headless-браузеры: основные методы

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

JavaScript-маркеры автоматизации

Самый распространённый метод — проверка JavaScript-свойств, которые появляются только в автоматизированных браузерах:

  • navigator.webdriver — возвращает true в Selenium и Puppeteer
  • window.chrome — отсутствует в headless Chrome
  • navigator.plugins.length — равен 0 в headless-режиме
  • navigator.languages — часто пустой массив или содержит только "en-US"
  • navigator.permissions — API работает по-разному в headless-режиме

Анализ Chrome DevTools Protocol

Puppeteer и Playwright управляют браузером через Chrome DevTools Protocol (CDP). Наличие CDP-соединения можно обнаружить через специальные JavaScript-проверки, которые анализируют объекты window.cdc_ или проверяют аномалии в поведении событий мыши и клавиатуры.

Canvas и WebGL Fingerprinting

Headless-браузеры генерируют одинаковые Canvas и WebGL отпечатки, так как используют программный рендеринг вместо аппаратного. Антибот-системы создают невидимый Canvas-элемент, рисуют на нём текст или фигуры и вычисляют хеш изображения. Если тысячи пользователей имеют идентичный хеш — это признак ботов.

Поведенческий анализ

Современные системы (DataDome, PerimeterX, Cloudflare Bot Management) анализируют движения мыши, скорость прокрутки, паттерны кликов. Headless-браузеры выполняют действия мгновенно и без естественных задержек, что выдаёт автоматизацию. Также анализируются события: в обычном браузере перед кликом всегда происходит событие mousemove, а боты часто кликают без предварительного движения мыши.

Важно: Современные антибот-системы используют машинное обучение для анализа сотен параметров одновременно. Маскировка только одного параметра (например, User-Agent) не защитит от блокировки — нужен комплексный подход.

Удаление navigator.webdriver и других JavaScript-маркеров

Свойство navigator.webdriver — самый простой способ детектирования Selenium и других WebDriver-инструментов. В обычном браузере это свойство возвращает undefined, а в автоматизированном — true. Удалить его можно через выполнение JavaScript-кода перед загрузкой страницы.

Selenium (Python): удаление webdriver через CDP

Для Selenium нужно использовать Chrome DevTools Protocol для выполнения JavaScript до загрузки любых страниц:

from selenium import webdriver
from selenium.webdriver.chrome.options import Options

options = Options()
options.add_argument('--disable-blink-features=AutomationControlled')

driver = webdriver.Chrome(options=options)

# Удаление navigator.webdriver через CDP
driver.execute_cdp_cmd('Page.addScriptToEvaluateOnNewDocument', {
    'source': '''
        Object.defineProperty(navigator, 'webdriver', {
            get: () => undefined
        });
    '''
})

driver.get('https://example.com')

Опция --disable-blink-features=AutomationControlled отключает флаг, который Chrome добавляет в режиме автоматизации. Это базовая защита, которую нужно комбинировать с другими методами.

Puppeteer (Node.js): маскировка через Page.evaluateOnNewDocument

В Puppeteer используется метод page.evaluateOnNewDocument() для выполнения кода перед загрузкой страницы:

const puppeteer = require('puppeteer');

(async () => {
    const browser = await puppeteer.launch({
        headless: true,
        args: ['--disable-blink-features=AutomationControlled']
    });
    
    const page = await browser.newPage();
    
    // Удаление webdriver и других маркеров
    await page.evaluateOnNewDocument(() => {
        Object.defineProperty(navigator, 'webdriver', {
            get: () => undefined
        });
        
        // Добавление chrome объекта
        window.chrome = {
            runtime: {}
        };
        
        // Эмуляция плагинов
        Object.defineProperty(navigator, 'plugins', {
            get: () => [1, 2, 3, 4, 5]
        });
    });
    
    await page.goto('https://example.com');
})();

Playwright: встроенные опции маскировки

Playwright имеет более продвинутую маскировку из коробки, но дополнительная настройка всё равно необходима:

const { chromium } = require('playwright');

(async () => {
    const browser = await chromium.launch({
        headless: true,
        args: ['--disable-blink-features=AutomationControlled']
    });
    
    const context = await browser.newContext({
        userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
    });
    
    const page = await context.newPage();
    
    await page.addInitScript(() => {
        Object.defineProperty(navigator, 'webdriver', {
            get: () => undefined
        });
    });
    
    await page.goto('https://example.com');
})();

Маскировка Chrome DevTools Protocol

Puppeteer и Playwright оставляют следы CDP-соединения, которые можно обнаружить через анализ объектов window. Антибот-системы ищут переменные с префиксом cdc_, $cdc_ или __webdriver_, которые создаются Chrome при подключении через DevTools Protocol.

Удаление CDP-переменных

Следующий скрипт удаляет все переменные, связанные с автоматизацией, из объекта window:

await page.evaluateOnNewDocument(() => {
    // Удаление всех переменных с префиксом cdc_
    const cdcProps = Object.keys(window).filter(prop => 
        prop.includes('cdc_') || 
        prop.includes('$cdc_') || 
        prop.includes('__webdriver_')
    );
    
    cdcProps.forEach(prop => {
        delete window[prop];
    });
    
    // Переопределение document.__proto__ для скрытия CDP
    const originalQuery = document.querySelector;
    document.querySelector = function(selector) {
        if (selector.includes('cdc_')) return null;
        return originalQuery.call(this, selector);
    };
});

Использование патченных версий Chromium

Существуют модифицированные сборки Chromium, которые не оставляют CDP-следов. Например, библиотека puppeteer-extra с плагином puppeteer-extra-plugin-stealth автоматически применяет десятки патчей для маскировки CDP.

Настройка User-Agent и HTTP-заголовков

Headless-браузеры используют устаревшие или нереалистичные User-Agent строки. Например, Puppeteer по умолчанию добавляет слово "HeadlessChrome" в User-Agent. Кроме того, нужно настроить дополнительные заголовки, которые присутствуют в запросах обычных браузеров.

Актуальные User-Agent для маскировки

Используйте свежие User-Agent реальных браузеров. Вот примеры для 2024 года:

# Chrome 120 на Windows 10
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36

# Chrome 120 на macOS
Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36

# Firefox 121 на Windows
Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:121.0) Gecko/20100101 Firefox/121.0

Настройка заголовков в Selenium

from selenium.webdriver.chrome.options import Options

options = Options()
options.add_argument('user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36')

# Дополнительные заголовки через CDP
driver.execute_cdp_cmd('Network.setUserAgentOverride', {
    "userAgent": 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
    "platform": 'Win32',
    "acceptLanguage": 'en-US,en;q=0.9'
})

Настройка заголовков в Puppeteer

await page.setUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36');

await page.setExtraHTTPHeaders({
    'Accept-Language': 'en-US,en;q=0.9',
    'Accept-Encoding': 'gzip, deflate, br',
    'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
    'Sec-Fetch-Site': 'none',
    'Sec-Fetch-Mode': 'navigate',
    'Sec-Fetch-User': '?1',
    'Sec-Fetch-Dest': 'document'
});

Заголовки Sec-Fetch-* критически важны — они появились в Chrome 76+ и их отсутствие выдаёт старые версии браузеров или ботов.

Эмуляция Canvas и WebGL Fingerprint

Canvas и WebGL fingerprinting — мощный метод детектирования, так как headless-браузеры генерируют идентичные отпечатки. Антибот-системы создают невидимый Canvas, рисуют на нём текст и вычисляют хеш пикселей. Если тысячи запросов имеют одинаковый хеш — это боты.

Добавление шума в Canvas

Следующий скрипт добавляет случайный шум в Canvas-отпечаток, делая каждый запрос уникальным:

await page.evaluateOnNewDocument(() => {
    const originalToDataURL = HTMLCanvasElement.prototype.toDataURL;
    const originalToBlob = HTMLCanvasElement.prototype.toBlob;
    const originalGetImageData = CanvasRenderingContext2D.prototype.getImageData;
    
    // Функция добавления шума
    const addNoise = (canvas, context) => {
        const imageData = originalGetImageData.call(context, 0, 0, canvas.width, canvas.height);
        for (let i = 0; i < imageData.data.length; i += 4) {
            imageData.data[i] += Math.floor(Math.random() * 10) - 5;
            imageData.data[i + 1] += Math.floor(Math.random() * 10) - 5;
            imageData.data[i + 2] += Math.floor(Math.random() * 10) - 5;
        }
        context.putImageData(imageData, 0, 0);
    };
    
    HTMLCanvasElement.prototype.toDataURL = function() {
        addNoise(this, this.getContext('2d'));
        return originalToDataURL.apply(this, arguments);
    };
});

Эмуляция WebGL параметров

WebGL раскрывает информацию о видеокарте и драйверах. В headless-режиме эти параметры выдают программный рендеринг:

await page.evaluateOnNewDocument(() => {
    const getParameter = WebGLRenderingContext.prototype.getParameter;
    WebGLRenderingContext.prototype.getParameter = function(parameter) {
        // Эмуляция реальной видеокарты
        if (parameter === 37445) {
            return 'Intel Inc.';
        }
        if (parameter === 37446) {
            return 'Intel Iris OpenGL Engine';
        }
        return getParameter.call(this, parameter);
    };
});

Параметр 37445 — это UNMASKED_VENDOR_WEBGL, а 37446 — UNMASKED_RENDERER_WEBGL. В headless-режиме они возвращают "Google SwiftShader", что выдаёт автоматизацию.

Selenium Stealth: готовые решения для Python

Библиотека selenium-stealth автоматически применяет десятки патчей для маскировки Selenium. Это самое простое решение для Python-разработчиков, которое не требует ручной настройки каждого параметра.

Установка и базовая настройка

pip install selenium-stealth
from selenium import webdriver
from selenium_stealth import stealth
from selenium.webdriver.chrome.options import Options

options = Options()
options.add_argument("--headless")
options.add_argument("--no-sandbox")
options.add_argument("--disable-dev-shm-usage")

driver = webdriver.Chrome(options=options)

# Применение stealth-патчей
stealth(driver,
    languages=["en-US", "en"],
    vendor="Google Inc.",
    platform="Win32",
    webgl_vendor="Intel Inc.",
    renderer="Intel Iris OpenGL Engine",
    fix_hairline=True,
)

driver.get("https://bot.sannysoft.com")
driver.save_screenshot("test.png")

Библиотека автоматически удаляет navigator.webdriver, добавляет window.chrome, эмулирует плагины, маскирует WebGL и применяет ещё 20+ патчей. Это покрывает 80% случаев детектирования.

Продвинутая настройка с прокси

Для полной маскировки комбинируйте selenium-stealth с резидентными прокси — они обеспечивают реальные IP-адреса домашних пользователей, что критически важно для обхода продвинутых антибот-систем:

from selenium.webdriver.common.proxy import Proxy, ProxyType

proxy = Proxy()
proxy.proxy_type = ProxyType.MANUAL
proxy.http_proxy = "ip:port"
proxy.ssl_proxy = "ip:port"

capabilities = webdriver.DesiredCapabilities.CHROME
proxy.add_to_capabilities(capabilities)

driver = webdriver.Chrome(desired_capabilities=capabilities, options=options)
stealth(driver, languages=["en-US", "en"], vendor="Google Inc.", platform="Win32")

Puppeteer Extra Stealth Plugin для Node.js

Для Puppeteer существует плагин puppeteer-extra-plugin-stealth, который является самым продвинутым решением для маскировки headless-браузеров в JavaScript-экосистеме. Он содержит 23 независимых модуля, каждый из которых маскирует определённый аспект автоматизации.

Установка и базовое использование

npm install puppeteer-extra puppeteer-extra-plugin-stealth
const puppeteer = require('puppeteer-extra');
const StealthPlugin = require('puppeteer-extra-plugin-stealth');

puppeteer.use(StealthPlugin());

(async () => {
    const browser = await puppeteer.launch({ 
        headless: true,
        args: ['--no-sandbox', '--disable-setuid-sandbox']
    });
    
    const page = await browser.newPage();
    await page.goto('https://bot.sannysoft.com');
    await page.screenshot({ path: 'test.png' });
    await browser.close();
})();

Что маскирует Stealth Plugin

Плагин автоматически применяет следующие патчи:

  • Удаление navigator.webdriver
  • Добавление window.chrome объекта
  • Эмуляция navigator.permissions API
  • Маскировка navigator.plugins и navigator.mimeTypes
  • Эмуляция Canvas и WebGL fingerprint
  • Маскировка User-Agent и языков
  • Исправление аномалий в iframe contentWindow
  • Эмуляция Battery API, Media Devices, WebRTC

Настройка с прокси и дополнительными параметрами

const puppeteer = require('puppeteer-extra');
const StealthPlugin = require('puppeteer-extra-plugin-stealth');
const AdblockerPlugin = require('puppeteer-extra-plugin-adblocker');

puppeteer.use(StealthPlugin());
puppeteer.use(AdblockerPlugin({ blockTrackers: true }));

(async () => {
    const browser = await puppeteer.launch({
        headless: true,
        args: [
            '--proxy-server=http://your-proxy:port',
            '--disable-web-security',
            '--disable-features=IsolateOrigins,site-per-process'
        ]
    });
    
    const page = await browser.newPage();
    
    // Аутентификация прокси
    await page.authenticate({
        username: 'your-username',
        password: 'your-password'
    });
    
    await page.setViewport({ width: 1920, height: 1080 });
    await page.goto('https://example.com', { waitUntil: 'networkidle2' });
})();

Playwright: настройка антидетекта

Playwright имеет более совершенную архитектуру по сравнению с Puppeteer и оставляет меньше следов автоматизации. Однако для обхода продвинутых антибот-систем нужна дополнительная настройка. Для Playwright существует порт stealth-плагина — playwright-extra.

Установка playwright-extra

npm install playwright-extra playwright-extra-plugin-stealth playwright
const { chromium } = require('playwright-extra');
const stealth = require('playwright-extra-plugin-stealth');

chromium.use(stealth());

(async () => {
    const browser = await chromium.launch({ headless: true });
    const context = await browser.newContext({
        userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
        viewport: { width: 1920, height: 1080 },
        locale: 'en-US',
        timezoneId: 'America/New_York'
    });
    
    const page = await context.newPage();
    await page.goto('https://bot.sannysoft.com');
    await page.screenshot({ path: 'test.png' });
    await browser.close();
})();

Настройка контекста браузера для максимальной маскировки

Playwright позволяет создавать изолированные контексты браузера с индивидуальными настройками. Это полезно для мультиаккаунтинга или параллельного парсинга:

const context = await browser.newContext({
    userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36',
    viewport: { width: 1440, height: 900 },
    locale: 'en-US',
    timezoneId: 'America/Los_Angeles',
    permissions: ['geolocation'],
    geolocation: { latitude: 37.7749, longitude: -122.4194 },
    colorScheme: 'light',
    deviceScaleFactor: 2,
    isMobile: false,
    hasTouch: false,
    // Настройка прокси для контекста
    proxy: {
        server: 'http://your-proxy:port',
        username: 'user',
        password: 'pass'
    }
});

Параметры geolocation и timezoneId должны соответствовать IP-адресу прокси, иначе антибот-системы обнаружат несоответствие (например, IP из Калифорнии, но timezone Нью-Йорка).

Ротация прокси для снижения риска блокировки

Даже идеально замаскированный headless-браузер может быть заблокирован, если использует один IP-адрес для сотен запросов. Современные антибот-системы анализируют частоту запросов с одного IP и блокируют подозрительную активность. Ротация прокси — обязательный элемент защиты при парсинге.

Типы прокси для парсинга: сравнение

Тип прокси Скорость Trust Score Лучше для
Datacenter Очень высокая (50-200 мс) Низкий Простые сайты, массовый парсинг
Резидентные Средняя (300-1000 мс) Высокий Защищённые сайты, соцсети
Мобильные Низкая (500-2000 мс) Очень высокий Мобильные приложения, Instagram, TikTok

Для парсинга защищённых сайтов (маркетплейсы, соцсети, рекламные платформы) рекомендуются резидентные прокси, так как они имеют реальные IP домашних пользователей и не попадают в чёрные списки антибот-систем.

Реализация ротации прокси в Puppeteer

const puppeteer = require('puppeteer-extra');
const StealthPlugin = require('puppeteer-extra-plugin-stealth');

puppeteer.use(StealthPlugin());

const proxyList = [
    'http://user1:pass1@proxy1:port',
    'http://user2:pass2@proxy2:port',
    'http://user3:pass3@proxy3:port'
];

async function scrapeWithRotation(urls) {
    for (let i = 0; i < urls.length; i++) {
        const proxy = proxyList[i % proxyList.length];
        
        const browser = await puppeteer.launch({
            headless: true,
            args: [`--proxy-server=${proxy}`]
        });
        
        const page = await browser.newPage();
        
        try {
            await page.goto(urls[i], { waitUntil: 'networkidle2' });
            const data = await page.evaluate(() => document.body.innerText);
            console.log(data);
        } catch (error) {
            console.error(`Error on ${urls[i]}:`, error);
        } finally {
            await browser.close();
        }
        
        // Задержка между запросами
        await new Promise(resolve => setTimeout(resolve, 2000));
    }
}

scrapeWithRotation([
    'https://example1.com',
    'https://example2.com',
    'https://example3.com'
]);

Ротация через Session-based прокси

Некоторые провайдеры прокси (включая ProxyCove) предлагают session-based ротацию — каждый запрос автоматически получает новый IP без необходимости перезапуска браузера. Это реализуется через специальный формат URL прокси:

// Формат: username-session-RANDOM:password@gateway:port
const generateSessionProxy = () => {
    const sessionId = Math.random().toString(36).substring(7);
    return `http://username-session-${sessionId}:password@gateway.proxycove.com:12321`;
};

const browser = await puppeteer.launch({
    args: [`--proxy-server=${generateSessionProxy()}`]
});

Как проверить качество маскировки: инструменты тестирования

После настройки маскировки необходимо проверить, насколько хорошо ваш headless-браузер имитирует обычного пользователя. Существует несколько специализированных сайтов, которые анализируют десятки параметров браузера и показывают, какие следы автоматизации остались.

Основные инструменты тестирования

  • bot.sannysoft.com — проверяет 15+ параметров детектирования, включая webdriver, Chrome объект, плагины, Canvas
  • arh.antoinevastel.com/bots/areyouheadless — специализируется на детектировании headless Chrome
  • pixelscan.net — продвинутый анализ fingerprint с визуализацией всех параметров
  • abrahamjuliot.github.io/creepjs — самый детальный анализ (200+ параметров), показывает уровень доверия браузера
  • iphey.com — проверка IP-адреса на принадлежность к прокси и VPN

Автоматизация тестирования

Создайте скрипт для автоматической проверки маскировки после каждого изменения настроек:

const puppeteer = require('puppeteer-extra');
const StealthPlugin = require('puppeteer-extra-plugin-stealth');

puppeteer.use(StealthPlugin());

async function testStealth() {
    const browser = await puppeteer.launch({ headless: true });
    const page = await browser.newPage();
    
    // Тест 1: Sannysoft
    await page.goto('https://bot.sannysoft.com');
    await page.screenshot({ path: 'test-sannysoft.png', fullPage: true });
    
    // Тест 2: Are you headless
    await page.goto('https://arh.antoinevastel.com/bots/areyouheadless');
    const headlessDetected = await page.evaluate(() => {
        return document.body.innerText.includes('You are not Chrome headless');
    });
    console.log('Headless detected:', !headlessDetected);
    
    // Тест 3: Webdriver property
    const webdriverPresent = await page.evaluate(() => navigator.webdriver);
    console.log('navigator.webdriver:', webdriverPresent);
    
    // Тест 4: Chrome object
    const chromePresent = await page.evaluate(() => !!window.chrome);
    console.log('window.chrome present:', chromePresent);
    
    await browser.close();
}

testStealth();

Чек-лист успешной маскировки

Ваш headless-браузер правильно замаскирован, если:

  • navigator.webdriver возвращает undefined
  • window.chrome существует и содержит объект runtime
  • navigator.plugins.length больше 0
  • WebGL vendor и renderer показывают реальную видеокарту, а не SwiftShader
  • Canvas fingerprint уникален для каждой сессии
  • User-Agent соответствует актуальной версии Chrome/Firefox
  • IP-адрес прокси не находится в чёрных списках (проверка через iphey.com)
  • Timezone и locale соответствуют геолокации IP-адреса

Заключение

Маскировка headless-браузеров — это комплексная задача, требующая внимания к десяткам параметров. Современные антибот-системы используют машинное обучение и анализируют сотни характеристик браузера одновременно, поэтому простая замена User-Agent уже не работает. Для успешного парсинга необходимо комбинировать несколько методов защиты.

Основные элементы эффективной маскировки: удаление JavaScript-маркеров автоматизации (navigator.webdriver, CDP-переменные), эмуляция Canvas и WebGL fingerprint, настройка реалистичных HTTP-заголовков и использование качественных прокси. Готовые решения (selenium-stealth для Python, puppeteer-extra-plugin-stealth для Node.js) покрывают 80% случаев, но для обхода продвинутых защит требуется дополнительная настройка.

Критически важный момент — выбор прокси. Даже идеально замаскированный браузер будет заблокирован, если использует IP-адреса из чёрных списков или делает слишком много запросов с одного IP. Для парсинга защищённых сайтов рекомендуем использовать резидентные прокси с автоматической ротацией — они обеспечивают высокий trust score и минимальный риск блокировок, так как используют реальные IP домашних пользователей вместо серверных адресов.

Регулярно тестируйте качество маскировки через специализированные сервисы (bot.sannysoft.com, pixelscan.net) и адаптируйте настройки под изменения в антибот-системах. Парсинг — это постоянная гонка вооружений между разработчиками ботов и создателями защиты, поэтому конфигурация, которая работает сегодня, может потребовать обновления через несколько месяцев.