Современные сайты научились распознавать автоматизированные браузеры (Selenium, Puppeteer, Playwright) и блокируют их запросы. Headless-браузеры оставляют десятки цифровых следов, по которым антибот-системы вычисляют автоматизацию за миллисекунды. В этом руководстве разберём все методы маскировки headless-браузеров с примерами кода на Python и JavaScript, чтобы ваши парсеры работали стабильно без блокировок.
Статья предназначена для разработчиков, которые занимаются веб-скрапингом, автоматизацией тестирования или сбором данных с защищённых сайтов. Мы рассмотрим технические детали детектирования и практические решения для обхода защиты.
Как сайты детектируют headless-браузеры: основные методы
Антибот-системы используют многоуровневую проверку браузера, анализируя сотни параметров. Headless-браузеры отличаются от обычных по множеству признаков, которые невозможно скрыть простой заменой User-Agent. Понимание методов детектирования — первый шаг к эффективной маскировке.
JavaScript-маркеры автоматизации
Самый распространённый метод — проверка JavaScript-свойств, которые появляются только в автоматизированных браузерах:
navigator.webdriver— возвращаетtrueв Selenium и Puppeteerwindow.chrome— отсутствует в headless Chromenavigator.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.permissionsAPI - Маскировка
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возвращаетundefinedwindow.chromeсуществует и содержит объектruntimenavigator.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) и адаптируйте настройки под изменения в антибот-системах. Парсинг — это постоянная гонка вооружений между разработчиками ботов и создателями защиты, поэтому конфигурация, которая работает сегодня, может потребовать обновления через несколько месяцев.