Назад в Центр знаний
Начало работы

Headless-скрейпинг: что действительно меняется

Сравните видимый Chrome, единый headless и headless shell для скрейпинга. Записывайте renderer, viewport и webdriver без выводов о решении сайта.

BotBrowser Team

Документация

Нужна структурированная документация по теме Начало работы?

Эта статья относится к редакционной библиотеке. Для пошаговой настройки, справки и постоянных обновлений переходите сразу в соответствующий раздел docs.

BotBrowser позволяет запускать Chrome с профилем для видимого или headless-скрейпинга, но профиль не заменяет renderer Chrome и не гарантирует ответ удалённого сайта. Скрипт извлечения может получить разное содержимое в видимом и headless-запуске, даже если оба открывают один URL. Сначала нужно выяснить, какая реализация браузера и какой режим запуска использовались; затем проверить, не изменились ли одновременно viewport, масштаб устройства, шрифты, хранилище или состояние fixture. Это разные причины, а не взаимозаменяемые названия видимого и невидимого браузера.

Для контролируемого сравнения скрейпинга различайте три случая: видимое окно Chrome, единый headless-режим Chrome и отдельный headless shell в Puppeteer. Chrome описывает единый headless как использование обычной реализации Chrome; shell является отдельным исполняемым файлом. Ни одно из этих описаний не обещает одинаковый вывод страницы и не предсказывает, примет ли запрос удалённый сервис. Локальная fixture показывает, какие значения изменились при объявленной конфигурации.

Chrome с интерфейсом, единый headless и headless shell используют одну локальную fixture; сравнивайте параметры запуска и поля страницы, а не решения удалённого сайта

Кратко

  • headless: false открывает видимое окно; headless: true выбирает единый режим в актуальном Puppeteer; headless: 'shell' выбирает отдельную сборку shell.
  • Зафиксируйте версию браузера, fixture, viewport, масштаб устройства, хранилище и флаги запуска, прежде чем связывать различие с режимом.
  • Записывайте window.innerWidth, screen.width, window.devicePixelRatio, navigator.webdriver и версию браузера как наблюдения, а не как оценку идентичности или принятия запроса.
  • Используйте собственную локальную fixture и меняйте по одной переменной. Различие, видимое странице, не доказывает, что сторонний детектор принял или отклонил запуск.

Содержание

Три режима запуска

«Headless» описывает способ запуска браузера, а не универсальный механизм отрисовки. Важны исполняемый файл и его версия, флаги запуска, операционная система, сервер дисплея, viewport, шрифты и профиль. В полезном сравнении указывают режим и исполняемый файл, а не сохраняют только логическое значение.

Chrome с видимым окном

В видимом режиме Chrome создаёт окно верхнего уровня на экране. Оконный менеджер и среда отображения могут влиять на внешние размеры окна, доступный размер экрана, масштаб, фокус и состояние видимости. Тестовый harness может задавать viewport страницы отдельно от внешнего окна, поэтому слово «видимый» не означает, что страница получает размеры физического монитора.

В Puppeteer значение headless: false запрашивает видимый режим. Страница может прочитать внутренний viewport через window.innerWidth и window.innerHeight; screen.width и screen.height описывают размеры экрана, доступные документу. Эти значения отвечают на разные вопросы. Фиксированный viewport может стабилизировать первую пару, пока дисплей или оконный менеджер меняет вторую.

Единый headless Chrome

Текущий headless-режим Chrome использует обычную реализацию браузера Chrome, а не прежнюю отдельную реализацию headless. В документации Chrome эта граница описана начиная с Chrome 112. Это различие реализации: оно не означает наличие видимого окна рабочего стола в headless-сессии и не требует совпадения всех доступных странице значений с видимым запуском на любом хосте.

В Puppeteer для этого единого режима используется headless: true. Headless-запуск можно запросить и через командную строку. Точный исполняемый файл и его версия всё равно важны: старый пакет автоматизации, закреплённый бинарный файл или пользовательский флаг могут изменить фактический запуск. Записывайте browser.version() или эквивалентную согласованную версию вместе с режимом, а не предполагайте, что установленный пакет выбрал ожидаемый браузер.

Headless shell

В Puppeteer также доступен headless: 'shell', выбирающий отдельный исполняемый файл chrome-headless-shell. Это не просто другое название единого headless Chrome. Документация Chrome описывает shell как отдельный headless-бинарный файл, а руководство Puppeteer документирует для него собственный селектор режима. Поэтому сравнение shell с видимым режимом может одновременно менять режим запуска и реализацию бинарного файла, если эксперимент не записывает оба параметра.

Эта опция относится к Puppeteer; другие библиотеки автоматизации могут использовать иные названия или не предоставлять shell через тот же API. Не переносите строковый параметр Puppeteer в Playwright без проверки актуальной документации этой библиотеки. Вопросы настройки браузера, а не семантики режимов, рассматриваются в руководстве по интеграции Puppeteer и заметках о развёртывании headless-сервера.

Метка запускаПараметр PuppeteerЧто записать об реализацииЧего метка не доказывает
Видимое окноheadless: falseИсполняемый файл и версия ChromeЧто результат страницы совпадает с настоящим устройством
Единый headlessheadless: trueОбычная реализация Chrome и версияЧто страница не может отличить окружение
Headless shellheadless: 'shell'Исполняемый файл и версия chrome-headless-shellЧто shell и обычный Chrome ведут себя одинаково

В Chrome 132 прежняя headless-реализация стала доступна как отдельный shell-бинарный файл. Поэтому версию нужно включать в запись: названия режимов могут оставаться похожими, хотя исполняемый файл за настройкой автоматизации уже другой. При обновлении runner обращайтесь к документации Chrome Headless и руководству Puppeteer по headless-режимам; не определяйте реализацию по флагу из старой статьи.

Что может прочитать страница

Страница не получает стандартизированное универсальное свойство isHeadless. Она читает API браузера, результаты компоновки и поведение настроенного окружения. Полезная fixture записывает несколько конкретных полей, сохраняя их значения раздельно, а не объединяя их в «headless-отпечаток».

window.innerWidth и window.innerHeight описывают компоновку viewport в CSS-пикселях. screen.width и screen.height описывают размеры экрана, доступные документу. window.devicePixelRatio связывает CSS-пиксели с пикселями устройства для текущей конфигурации дисплея. Адаптивная страница может выбрать другой компоновку при изменении viewport, даже если реализация браузера и URL остались прежними. Справочники MDN для innerWidth и devicePixelRatio определяют смысл этих API, но не утверждают, что их значения задаются только headless-режимом.

navigator.userAgent и, если доступно, navigator.userAgentData предоставляют сведения об идентификации браузера с учётом правил сокращения и доступности. Эти строки не позволяют надёжно пометить сессию как видимую или headless. User-Agent может остаться прежним, хотя viewport или отрисовка изменились; код страницы не должен трактовать строку как доказательство физического устройства.

navigator.webdriver - связанный с автоматизацией сигнал, стандартизированный WebDriver. MDN описывает условия, при которых браузер сообщает такое состояние; Chrome может показывать его в настройках автоматизации, включая headless-запуск. Это не полный классификатор режимов: значение true означает, что браузер сообщает об управлении через WebDriver, а не то, что конкретный сайт уже вынес решение. Для контракта свойства используйте справочник Navigator.webdriver на MDN. Отдельная сравнительная статья о headless-сигналах показывает, как записывать несколько доступных браузеру поверхностей, не превращая одно поле в вывод о детекторе.

Другие результаты зависят не только от режима запуска. Набор доступных шрифтов меняет переносы строк и размеры элементов. Язык и часовой пояс меняют форматированный текст. Сохранённая cookie может определить, увидит ли страница экран входа или таблицу данных. Сетевой маршрут может изменить ответ сервера или время отклика. Запуск скрейпинга, который одновременно меняет несколько таких параметров, не позволяет приписать разницу DOM headless-режиму.

Среда развёртывания - ещё одна независимая ось. В настройке headless-сервера BotBrowser описаны требования этого продукта к виртуальному дисплею и профилю. В справочнике по согласованности автоматизации описаны значения браузера на основе профиля. Это эксплуатационные сведения о BotBrowser, а не утверждения об архитектуре исходного Chrome и не обещание одинаковых значений для страницы в каждом режиме.

Контролируемое сравнение двух режимов

Используйте страницу, которой владеете или которую вправе проверять. Локальная fixture не смешивает наблюдения режима запуска с разрешением доступа, лимитами запросов, проверками, изменением содержимого, политикой сервиса или состоянием удалённого сайта. Пример ниже предполагает fixture по адресу http://127.0.0.1:4173/fixture.html; здесь он не запускался и не содержит измеренных результатов.

Запускайте по одному режиму на процесс. Используйте одну установку Puppeteer, исполняемый файл, версию браузера, локальную страницу, правила профиля, viewport, масштаб устройства, язык и состояние хранилища. Для первых двух запусков имя режима - единственная намеренно меняемая переменная. Запуск shell является отдельным сравнением, поскольку его исполняемый файл другой.

import puppeteer from 'puppeteer';

const modeName = process.argv[2];
const modes = {
  headed: false,
  headless: true,
  shell: 'shell',
};

if (!Object.hasOwn(modes, modeName)) {
  throw new Error('Choose one mode: headed, headless, or shell');
}

const fixture = 'http://127.0.0.1:4173/fixture.html';
const browser = await puppeteer.launch({
  headless: modes[modeName],
  defaultViewport: {
    width: 1365,
    height: 768,
    deviceScaleFactor: 1,
  },
});

try {
  const page = await browser.newPage();
  await page.goto(fixture, { waitUntil: 'domcontentloaded', timeout: 10000 });
  const pageFields = await page.evaluate(() => ({
    origin: location.origin,
    userAgent: navigator.userAgent,
    webdriver: navigator.webdriver,
    viewport: { width: innerWidth, height: innerHeight },
    screen: { width: screen.width, height: screen.height },
    devicePixelRatio,
    visibilityState: document.visibilityState,
  }));

  process.stdout.write(
    `${JSON.stringify(
      {
        mode: modeName,
        browserVersion: await browser.version(),
        pageFields,
      },
      null,
      2
    )}\n`
  );
} finally {
  await browser.close();
}

Сохраните этот скрипт как mode-diff.mjs и записывайте каждый запуск в отдельный файл:

node mode-diff.mjs headed > headed.json
node mode-diff.mjs headless > headless.json
node mode-diff.mjs shell > shell.json
diff -u headed.json headless.json

Код выводит запись, а не результат «прошёл/не прошёл». Он намеренно не обращается к домену третьей стороны, не сравнивает аккаунты и не утверждает, что какой-либо режим принят. Ненулевой код завершения или отсутствующий JSON-файл указывает на ошибку исполнения; различающиеся значения нужно классифицировать как наблюдения. Для строгого сравнения видимого и единого headless сравните первые два файла и проверьте совпадение browserVersion. Результат shell считайте отдельным сравнением реализации и сохраняйте его версию в заметках.

Значения viewport в примере - фиксированные входные параметры, а не свидетельство универсального viewport. defaultViewport задаёт viewport страницы в Puppeteer, но не управляет всеми размерами окон операционной системы. Скрипт записывает размеры экрана и масштаб устройства, потому что они могут показать изменение хоста. Если fixture требует определённого viewport для воспроизведения адаптивной ветви, сохраняйте его в каждом запуске и указывайте рядом с файлами JSON.

Для локальной fixture с динамическими данными сделайте тестовую страницу детерминированной до сравнения режимов. Зафиксируйте версию fixture, используйте статические локальные ресурсы, заморозьте состояние приложения и не полагайтесь на живой API, ответ которого может измениться между запусками. Если шрифт загружается по сети, различие во времени может поменять компоновку при неизменном renderer. Сохраняйте ревизию исходников тестовой страницы и набор её ресурсов вместе с записью, чтобы после обновления браузера сравнение не шло с другой fixture.

Классификация различий

Начните с записанной версии браузера и конфигурации запуска. Если они не соответствуют плану сравнения, это не контролируемое сравнение режимов. Затем проверьте viewport, размеры экрана и масштаб устройства. Другое значение innerWidth может выбрать иной CSS breakpoint; другой devicePixelRatio может изменить растеризацию и разрешение canvas. Такие результаты объясняются конкретными входными параметрами, а не общей меткой headless.

Разделяйте три класса результатов:

КлассПримерСледующая проверка
Объявленное различие входовShell сообщает другую версию или исполняемый файлСохраните это как отдельное сравнение бинарных файлов; не связывайте результат только с видимостью
Различие из-за средыИзменились размеры экрана, шрифты, язык или хранилищеВыровняйте входные параметры либо запишите отличие как часть среды
Различие, наблюдаемое страницейЛокальная fixture читает другое navigator.webdriver или выбирает другой компоновкуПроверьте контракт API и настройки браузера; не делайте вывод о решении удалённого детектора

Например, изменение navigator.webdriver нужно связывать с управлением автоматизацией и конфигурацией запуска браузера. Свойство сообщает о состоянии WebDriver; оно не говорит, разрешён ли скрейпинг и принял ли сайт сессию. Перед исследованием CSS сравните новое innerWidth с настроенным viewport страницы. Изменение screen.width указывает на открытую странице конфигурацию экрана, а не на идентичность физического устройства.

Сравнение режимов также не изолирует поведение сети и сервера. Два запуска могут получить разметку разного вида, если изменились fixture-сервер, cookies, время запроса или кэшированное содержимое. Перед тем как связывать различие DOM с Chrome, сравните ответ fixture и состояние её исходников. Для удалённого сайта соблюдайте опубликованные правила доступа и рассматривайте серверные ответы отдельно от чтения API браузера; здесь не даются инструкции по прохождению проверок доступа.

Если задача - понять, как конкретный детектор группирует наблюдения, это не то же самое, что выбрать режим запуска для скрейпинга. В материале о слоях headless-детектирования CreepJS разобраны открытые категории сигналов, а согласованность профиля рассматривает значения, заданные профилем. Ни один локальный diff не становится предсказанием для сервиса, который не измерялся.

Когда сравнение не даёт вывода

Сравнение не даёт надёжного вывода, если неизвестен реальный исполняемый файл, отличаются версии браузера, изменилась fixture или два запуска использовали разные viewport и состояние. То же относится к ситуации, когда сохранился только скриншот без метаданных запуска: по пикселям нельзя определить, какой бинарный файл, флаги, профиль, шрифты, язык или настройки дисплея их создали.

Одно наблюдение не доказывает, что поведение характерно для всех headless-браузеров. Версии меняются. Puppeteer и Chrome могут обновляться по разным графикам. Настройка headless: true имеет смысл только для версии библиотеки, которая её интерпретирует, и исполняемого файла, который она запускает. Закрепите зависимости для воспроизводимого сравнения и повторите тест собственной fixture после запланированного обновления.

Не меняйте режим, viewport, профиль, язык и сеть одновременно в одном тесте «до/после». Когда изменилось несколько переменных, у результата страницы появляется несколько возможных причин. Составьте небольшую матрицу: видимый режим и единый headless с одним исполняемым файлом, версией и viewport; отдельная строка shell с фактическим исполняемым файлом; при необходимости - второе сравнение viewport при неизменном режиме. Каждая пара должна отвечать на один ограниченный вопрос.

Документированное поведение профиля BotBrowser относится к такой матрице как конфигурация продукта, а не как режим исходного Chrome. Профиль может задавать документированные значения браузера, а страница о серверном запуске описывает ограничения развёртывания. Это не означает изменение архитектуры renderer, равенство всех сигналов хоста или одинаковое содержимое ответа сайта. Граница между согласованностью профиля и видимым/headless-выводом помогает не смешивать эти вопросы.

Вопросы и ответы

Headless Chrome всегда можно обнаружить?

Универсального поддерживаемого булева значения для любой страницы и версии браузера нет. Страница может читать API и результаты компоновку, а настройки автоматизации могут открывать состояние WebDriver. Конкретное значение - свидетельство о данном запуске, а не гарантия, что все сайты используют те же поля и приходят к одному заключению. Надёжная запись включает версию браузера, режим запуска, входные параметры, fixture и наблюдаемые поля.

Headless Chrome создаёт ту же страницу, что и видимый Chrome?

Единый headless Chrome использует обычную реализацию Chrome, но общая реализация не делает все параметры среды одинаковыми. Видимое окно, виртуальный дисплей, шрифты, графический стек, viewport, масштаб устройства и профиль могут отличаться. Перед тем как связывать изменение с режимом запуска, сравните одну и ту же версию исполняемого файла на контролируемой fixture. Не обещайте идентичные пиксели или DOM между операционными системами.

headless: 'shell' - это просто другая запись для headless: true?

Нет. Puppeteer описывает shell-селектор как отдельный headless-shell исполняемый файл, а true выбирает единый headless Chrome. Доступность бинарного файла зависит от версии пакета и вариантов установки. Записывайте версию браузера каждого запуска; не объединяйте shell с базовой линией единого headless без фиксации изменения реализации.

Стоит ли переходить на видимый режим, если данные отличаются?

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

Источники

Практический вывод

Выберите режим запуска для развёртывания и нагрузки, затем запишите, что действительно запускалось. Сравните видимый и единый headless на одной локальной fixture с одной версией, viewport, политикой профиля и состоянием; shell считайте отдельным исполняемым файлом. Полученный diff объясняет наблюдаемые страницей различия в этой конфигурации. Он не подтверждает идентичность, разрешение доступа или ответ сайта.

#Headless Browser#веб-скрейпинг#Chrome

Эта статья входит в руководство: Начало работы с автоматизацией браузера: карта

Выполните ту же проверку в BotBrowser

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