← Все статьи

Флаг webdriver и переменные cdc_ - это отпечаток ChromeDriver, а не Selenium

4 мин чтения seleniumautomationguide

Если сайт понимает, что вы работаете через Selenium, признак, скорее всего, не в коде ваших тестов. Он приходит из ChromeDriver (или GeckoDriver) - отдельного процесса между вашим скриптом и браузером, который переводит команды протокола WebDriver в управление браузером. Этот процесс оставляет следы, до которых заплатка уровня скрипта не дотягивается, потому что ваш скрипт их и не ставил.

navigator.webdriver - часть самой спецификации WebDriver. Браузер, реализующий протокол, обязан выставлять этот флаг в true, пока находится под удалённым управлением, независимо от того, какая клиентская библиотека шлёт команды. Привязки Selenium для Python, Java или JS это свойство не ставят; его ставят ChromeDriver и GeckoDriver как соответствующие спецификации серверы WebDriver, разговаривающие с запущенным ими браузером.

Это различие важно для того, как вы пытаетесь чинить. Stealth-подход, переопределяющий navigator.webdriver фрагментом JS, внедрённым из вашего же скрипта, соревнуется со значением, которое браузер выставил ещё до запуска вашего скрипта, и ничего не делает с тем, что ещё в среде выполнения читает то же самое состояние автоматизации. Заплатка на геттер прячет одно чтение флага. Она не отменяет то состояние, о котором флаг сообщал.

Собственный след ChromeDriver

Независимо от этого флага у ChromeDriver есть свои привычки. Запуск Chrome под его переключателем автоматизации по умолчанию раньше показывал, а на части конфигураций показывает и сейчас, характерную панель, сообщающую пользователю, что браузером управляет автоматизированное тестовое ПО. Её можно подавить возможностью вроде excludeSwitches: ['enable-automation'], но это само по себе даёт экземпляр Chrome, настроенный не так, как обычная сессия, - собственный признак для всего, что сверяет флаги с состоянием среды выполнения.

Переменные cdc_

Чтобы реализовать атомы выполнения скриптов из спецификации WebDriver, ChromeDriver исторически внедрял глобальные переменные в объект window страницы, используя узнаваемый шаблон имени, начинающийся с cdc_. Их пишет не ваш тест. Их проставляет сам процесс драйвера при загрузке документа, так что скрипту детекта достаточно перечислить window в поисках этого префикса, и никакая заплатка со стороны Selenium их не убирает, потому что драйвер внедряет их заново при следующей навигации независимо от того, что ваш скрипт сделал на предыдущей.

Разные сборки ChromeDriver со временем обращались с этим по-разному, поэтому не считайте, что какое-то конкретное имя переменной всё ещё верно для вашей сборки. Постоянен именно механизм: драйвер проставляет в страницу узнаваемые метки, и эти метки живут на уровне драйвера, а не библиотеки.

Почему это проблема протокола, а не библиотеки

Архитектура Selenium маршрутизирует команды через отдельный бинарник драйвера, говорящий с браузером по проводному протоколу WebDriver. Playwright и Puppeteer подключаются напрямую по Chrome DevTools Protocol, без промежуточного процесса драйвера, реализующего ту спецификацию. Вот настоящая разница: модель Selenium требует отдельного процесса, вся работа которого - соответствовать спецификации, включающей объявление о себе. Библиотека-обёртка, латающая окружение вашего скрипта, спорит с процессом-сервером, соответствующим спецификации, а не с причудой JS.

Проверки для вашей текущей схемы

Когда переписывание честно не окупается

Честно оцените цену, прежде чем что-то по этому поводу делать. launch() в AntiBrow возвращает стандартные Page и BrowserContext из Playwright, а не экземпляр WebDriver из Selenium. Если ваш набор тестов построен на локаторах Selenium, его ожиданиях, слое page object и Selenium Grid для параллельных прогонов, переезд означает переписывание всей этой поверхности, а не замену импорта. Для короткого скрипта это вечер. Для зрелой кодовой базы с годами page object и обвязкой CI это проект.

Если конкретный пробел, за которым вы гонитесь, - действительно только флаг и переменные cdc_, поддерживаемая сообществом обёртка для вашего нынешнего драйвера, из которых самая известная для Python - undetected-chromedriver, закрывает эту более узкую дыру дешевле, чем миграция фреймворка. Сделайте эту арифметику первой и помните, что ни один из вариантов не меняет того, что о вас говорят ваш прокси и история вашего аккаунта; слой драйвера - один сигнал из нескольких.

Если настоящая проблема - отпечаток под ним

Заплатка на уровне драйвера не может тронуть личность устройства под ним: canvas, WebGL, шрифты, аудио и аппаратные свойства приходят из машины, а не из драйвера. AntiBrow генерирует этот отпечаток один раз на профиль, применяет его в движке браузера ниже слоя JS и сохраняет вместе с профилем, причём локальных профилей неограниченно на любом тарифе. Это имеет значение, только если флаг и переменные cdc_ - не вся ваша проблема. Тот же переход из других библиотек разобран в заметках по Playwright и по Puppeteer, а перед тем как решаться на переписывание, посмотрите справочник SDK.

Попробуйте на бесплатном тарифе.

Неограниченное число локальных профилей, без карты. Проверьте результат детекторами сами.