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

Puppeteer и BotBrowser: ошибки запуска и профиля

Избегайте ошибок Puppeteer, нарушающих согласованность профиля BotBrowser: defaultViewport, встроенный Chromium и неверно выбранная CDP-сессия уровня браузера.

BotBrowser Team

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

Нужна поддерживаемая продуктовая документация?

У этой статьи есть соответствующая страница в центре документации. Используйте docs для каноничного сценария настройки, актуальных флагов и долгосрочной справки.

Если вам нужна самая короткая связка BotBrowser и Puppeteer, начните со страницы документации о Puppeteer. После первого удачного запуска согласованность профиля часто незаметно нарушают три ошибки интеграции; их можно распознать по симптомам и исправить, не трогая код ваших страниц.

Puppeteer является слоем управления. Он решает, какой бинарный файл запускается, какие параметры запуска применяются и какие команды отправляются по соединению DevTools. BotBrowser является браузером, который загружает профиль и показывает его страницам. Когда оба соединены правильно, ваши селекторы, логика навигации и извлечение данных работают точно так же, как раньше. Когда соединение выполнено неверно, профиль может загрузиться, но что-то вокруг него будет ему противоречить, и такое противоречие диагностировать сложнее, чем явный сбой.

Чек-лист запуска Puppeteer для BotBrowser: puppeteer-core запускает исполняемый файл BotBrowser, команды CDP идут через сессию уровня браузера, defaultViewport равен null

Кратко

  • Запускайте puppeteer-core с явным executablePath; Chromium из пакета puppeteer профиль не загружает.
  • Оставляйте defaultViewport: null; если новая страница измеряется как 800x600, перезапись viewport в Puppeteer всё ещё действует.
  • Отправляйте команды BotBrowser, например BotBrowser.setCustomHeaders, через browser.target().createCDPSession(), а не через сессию страницы.
  • Передавайте --bot-profile, --proxy-server, --bot-timezone, --bot-locale и --bot-languages в args, а затем сравните navigator.languages и screen.width с профилем.

Содержание

Три ошибки запуска, нарушающие согласованность профиля

Большинство неудачных интеграций Puppeteer сводится к одной из трёх причин. У каждой свой симптом, поэтому полезно узнавать их по тому, что вы видите, а не по тому, что вы настроили.

  • Запускается не тот бинарный файл. Обычный пакет puppeteer скачивает собственную сборку Chromium, а запуск без явного executablePath использует именно её. Браузер открывается, страницы загружаются, но профиль BotBrowser не участвует вообще.
  • Puppeteer перезаписывает viewport. Puppeteer применяет к каждой новой странице viewport по умолчанию 800x600, если вы не отключите это поведение. Профиль, описывающий больший экран, оказывается рядом со страницей, которая сообщает о маленьком окне, и два значения противоречат друг другу.
  • Команда BotBrowser уходит не в ту сессию. BotBrowser добавляет собственные команды CDP, и они доступны в сессии уровня браузера. Сессия уровня страницы их не знает, поэтому вызов завершается ошибкой «метод не найден».

Ни один из этих случаев не приводит к очевидному сбою. Первый даёт вполне обычный Chromium, второй даёт обычную страницу со странным размером окна, а третий даёт один неудавшийся вызов, который легко списать на опечатку. Поэтому они так часто проходят ревью кода.

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

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

Помните, чем этот чек-лист является и чем не является. Он охватывает конфигурацию запуска: какой бинарный файл, какие аргументы, какое поведение viewport, какая сессия. Он не меняет того, как Puppeteer управляет страницами, и ничего не утверждает о том, как конкретный сайт отнесётся к получившейся сессии.

Запуск BotBrowser через puppeteer-core

Установка компонентов

Документация BotBrowser перечисляет четыре предварительных условия для Puppeteer: установленный исполняемый файл BotBrowser, Node.js 18 или новее, пакет puppeteer-core и файл профиля. В продакшене используется зашифрованный профиль .enc, и он должен соответствовать версии BotBrowser, которую вы запускаете.

  • Исполняемый файл BotBrowser, скачанный из репозитория BotBrowser и установленный по руководству по установке.
  • Node.js 18 или новее.
  • Пакет npm puppeteer-core.
  • Файл профиля, обычно с расширением .enc.

Выбор пакета важнее, чем кажется. Полный пакет puppeteer вместе с собой скачивает браузер, а именно его вы не хотите запускать по случайности. puppeteer-core предоставляет тот же API автоматизации без встроенного браузера, поэтому он заставляет вас указать, какой исполняемый файл запускать. Это требование и есть точка интеграции: вы передаёте путь к исполняемому файлу BotBrowser, и Puppeteer его запускает.

npm init -y
npm install puppeteer-core

Минимальный запуск

Документированный минимальный запуск задаёт четыре вещи, которые обычный скрипт Puppeteer пропускает или оставляет по умолчанию.

import puppeteer from 'puppeteer-core';

const browser = await puppeteer.launch({
  browser: 'chrome',
  executablePath: process.env.BOTBROWSER_EXEC_PATH,
  headless: true,
  defaultViewport: null,
  args: [`--bot-profile=${process.env.BOT_PROFILE_PATH}`],
});

const page = await browser.newPage();
await page.goto('https://example.com');
await browser.close();

У каждого параметра своя задача.

  • browser: "chrome" выбирает запуск в режиме Chrome, чего документация BotBrowser требует для puppeteer-core. Если запуск завершается ошибкой внутри puppeteer-core, в первую очередь проверьте этот параметр и путь к исполняемому файлу.
  • executablePath указывает на исполняемый файл BotBrowser. Чтение пути из переменной окружения оставляет пути, привязанные к машине, вне скрипта, и один и тот же код работает и на ноутбуке, и на сервере.
  • headless: true запускает браузер без видимого окна. На время отладки отключите его, если хотите наблюдать за сессией.
  • defaultViewport: null отключает перезапись viewport в Puppeteer, о чём говорится в следующем разделе.
  • args несёт флаги BotBrowser. --bot-profile загружает профиль, а абсолютный путь избавляет от сюрпризов, когда рабочий каталог на разных машинах отличается.

Код страницы после вызова запуска является обычным Puppeteer. Ничто в newPage, goto, селекторах или close не специфично для BotBrowser, и в этом смысл: перенос существующего проекта должен затрагивать только блок запуска.

Почему defaultViewport должен быть null

Справочник параметров запуска Puppeteer описывает defaultViewport как настройку, которая даёт каждой странице единый viewport, со значением по умолчанию 800x600. Значение null отключает это умолчание. В обычном наборе тестов это удобство. С профилем BotBrowser оно превращается в конфликт, потому что профиль уже описывает размеры экрана и окна, о которых должна сообщать сессия.

Пока перезапись остаётся включённой, Puppeteer задаёт каждой странице 800x600 уже после того, как браузер загрузил профиль. Профиль говорит одно, а страница измеряет другое. Любой, кто сравнит размеры экрана с внутренними размерами окна, увидит сочетание, которого не даёт ни одно реальное устройство. Это самая частая причина, по которой первый тестовый запуск выглядит неверно, хотя профиль загрузился без ошибок.

Исправление занимает одну строку, а симптом легко подтвердить: если внутреннее окно новой страницы измеряется как 800x600, viewport по умолчанию всё ещё активен. Задайте defaultViewport: null, запустите заново, и страница возьмёт размеры из профиля. Оставляйте эту строку в каждом вызове запуска, который вы пишете, в том числе в скриптах, скопированных из старых примеров Puppeteer, потому что большинство таких примеров полагаются на значение по умолчанию.

Перенос существующего проекта

Существующему проекту Puppeteer нужны три правки, и все они делаются в блоке запуска. Замените зависимость puppeteer на puppeteer-core, чтобы встроенный браузер не скачивался и каждый запуск обязан был назвать свой исполняемый файл. Замените параметры запуска на минимальный запуск, показанный выше, подставив путь к исполняемому файлу BotBrowser и путь к профилю. Затем удалите любое значение defaultViewport и осторожно относитесь к явным вызовам, задающим viewport, потому что ручной размер страницы перезаписывает те же размеры из профиля.

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

Закрывайте то, что запускаете. Каждый вызов puppeteer.launch запускает отдельный процесс браузера, поэтому оберните работу в try и finally и вызывайте browser.close() в блоке finally, иначе неудачный прогон оставит после себя процесс.

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

Отправка команд CDP BotBrowser в сессии уровня браузера

Puppeteer предоставляет Chrome DevTools Protocol через объекты CDPSession. Стандартные команды протокола работают в любой сессии. Собственные команды BotBrowser, например BotBrowser.setCustomHeaders, относятся к браузеру в целом, поэтому они доступны в сессии, подключённой к цели браузера.

const cdp = await browser.target().createCDPSession();

await cdp.send('BotBrowser.setCustomHeaders', {
  headers: { 'x-client-app': 'bb' },
});

Смотреть нужно на вызов browser.target().createCDPSession(). Он создаёт сессию на самом браузере. Более привычный page.createCDPSession() создаёт сессию на одной цели страницы, и такая сессия не распознаёт команды BotBrowser. Результатом становится ошибка «метод не найден», которая выглядит как опечатка в имени команды, даже когда имя записано абсолютно верно.

Увидев эту ошибку, сначала проверьте, откуда взялась сессия, и только потом проверяйте написание. Если сессия создана от страницы, создайте новую от цели браузера и отправьте команду снова. Одной сессии уровня браузера хватает на весь скрипт, поэтому её можно создать один раз после запуска и использовать для каждой команды BotBrowser.

Понятные имена делают область действия видимой на ревью кода. Переменную browserCdp, созданную от browser.target(), трудно спутать с сессией страницы, тогда как общую переменную client, созданную внутри вспомогательной функции страницы, легко неверно использовать спустя месяцы тому, кто её не писал.

Стандартные возможности Puppeteer идут обычным путём. Только команды BotBrowser требуют сессии уровня браузера, а остальная часть скрипта продолжает пользоваться привычными методами страницы.

Прокси, часовой пояс и язык

Профиль описывает устройство. Сетевые и региональные параметры описывают, где это устройство якобы находится. Документация BotBrowser передаёт оба вида параметров через args, рядом с --bot-profile.

const browser = await puppeteer.launch({
  browser: 'chrome',
  executablePath: process.env.BOTBROWSER_EXEC_PATH,
  headless: true,
  defaultViewport: null,
  args: [
    `--bot-profile=${process.env.BOT_PROFILE_PATH}`,
    '--proxy-server=socks5://user:[email protected]:1080',
    '--bot-timezone=Asia/Tokyo',
    '--bot-locale=ja-JP',
    '--bot-languages=ja-JP,ja,en-US,en',
  ],
});

Передавайте прокси как --proxy-server в args. BotBrowser принимает учётные данные внутри URL прокси, а документация не рекомендует способы аутентификации прокси, доступные только в самом фреймворке, потому что географические значения, выведенные из адреса выхода, не совпадут, если прокси настроен вне браузера. Если часовой пояс не соответствует региону прокси, сначала посмотрите, как был настроен прокси, и только потом проверяйте остальное; формы учётных данных и синтаксиса разобраны в статье Настройка прокси.

Когда прокси задан, документированное поведение по умолчанию таково: BotBrowser выводит часовой пояс, локаль и языки из адреса выхода прокси. Явные флаги --bot-timezone, --bot-locale и --bot-languages заменяют эти выведенные значения для тех параметров, которые они определяют. Используйте их, когда нужен фиксированный регион, и не указывайте, когда значения должны следовать за прокси. Некоторые из этих флагов относятся к более высоким уровням BotBrowser, поэтому прежде чем на них полагаться, проверьте в справочнике флагов CLI, какой уровень подходит для вашей конфигурации.

Если вы запускаете несколько браузеров на одной машине, дайте каждому собственный --user-data-dir. Документация указывает конфликты между экземплярами как известную проблему, а уникальный каталог как решение, потому что он разделяет хранилище и состояние сессии.

Прокси для отдельного контекста через browser.createBrowserContext() требует внимательной проверки ваших версий. Будет ли там принят параметр прокси, зависит от версии Puppeteer и от уровня BotBrowser, поэтому относитесь к этому как к тому, что нужно проверить на своей конфигурации, а не как к возможности, которую поддерживает любое сочетание.

Проверка запуска и разбор частых сбоев

Проверять нужно со стороны страницы, потому что именно её видит сайт. Откройте страницу, прочитайте несколько значений и сравните их с загруженным профилем.

const page = await browser.newPage();
await page.goto('https://example.com');

const reported = await page.evaluate(() => ({
  userAgent: navigator.userAgent,
  languages: navigator.languages,
  screenWidth: screen.width,
  screenHeight: screen.height,
  innerWidth: window.innerWidth,
  innerHeight: window.innerHeight,
  pixelRatio: window.devicePixelRatio,
}));

console.log(reported);

Читайте вывод в три шага. User agent и список языков должны совпадать с тем, что описывают профиль и ваши региональные флаги. Размеры экрана должны совпадать с экраном профиля. Внутренние размеры окна должны согласовываться с этим экраном и не равняться ровно 800x600. Если они равны 800x600, вернитесь к блоку запуска и поищите перезапись viewport.

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

Когда что-то не так, симптом обычно прямо указывает на причину.

СимптомВероятная причина и исправление
Viewport измеряется как 800x600Активен viewport по умолчанию. Задайте defaultViewport: null.
Запуск завершается ошибкой внутри puppeteer-coreДобавьте browser: "chrome" и убедитесь, что executablePath указывает на исполняемый файл BotBrowser.
Профиль, похоже, не применяетсяВозможно, запустился не тот файл. Проверьте путь к исполняемому файлу и используйте абсолютный путь к профилю.
Команды BotBrowser возвращают «метод не найден»Сессия создана от страницы. Создайте её через browser.target().createCDPSession().
Часовой пояс или языки не совпадают с проксиПередайте прокси через --proxy-server в args, а не через параметр, доступный только во фреймворке.
Несколько экземпляров мешают друг другуДайте каждому экземпляру уникальный --user-data-dir.

Если профиль, похоже, вообще не применяется, двигайтесь снаружи внутрь; сочетания флагов из этих примеров собраны в статье Рецепты CLI. Убедитесь, что запущенный вами процесс является исполняемым файлом BotBrowser, а не обычным Chromium, убедитесь, что путь к профилю абсолютный и файл существует, и убедитесь, что пакет профиля соответствует основной версии установленного BotBrowser, поскольку документация требует совпадения версий. Меняйте что-то одно за раз и запускайте заново, чтобы знать, какая правка сыграла роль.

Последний частый вопрос: нужны ли ещё плагины, которые исправляют страницу средствами JavaScript. Они не входят в документированную конфигурацию BotBrowser. Профиль применяет исполняемый файл браузера, который вы запускаете, и приведённый выше чек-лист запуска не зависит от исправлений на уровне страницы. Их добавление даёт ещё один слой для анализа, когда запуск выглядит неверно, поэтому сначала проверьте документированную конфигурацию и добавляйте что-либо ещё только по причине, которую вы можете назвать.

Доказать, что значения пришли из профиля, а не с машины

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

Ответить на этот вопрос позволяет сравнение двух профилей. Запустите один и тот же скрипт дважды, подставляя разные профили, и выведите одни и те же поля. Всё остальное оставьте неизменным: тот же путь к исполняемому файлу, та же настройка области просмотра, та же страница.

// launch-report.mjs: один запуск на профиль, сохраняйте каждый вывод
const fields = await page.evaluate(() => ({
  ua: navigator.userAgent,
  langs: navigator.languages.join('|'),
  screen: `${screen.width}x${screen.height}`,
  inner: `${window.innerWidth}x${window.innerHeight}`,
  dpr: window.devicePixelRatio,
}));
console.log(JSON.stringify(fields));
BOT_PROFILE_PATH=/path/profile-a.enc node launch-report.mjs > a.json
BOT_PROFILE_PATH=/path/profile-b.enc node launch-report.mjs > b.json
diff a.json b.json

Три результата, три разные проблемы.

РезультатЧто означаетСледующий шаг
diff выводит различающиеся поляОба профиля загрузились, значения следуют за профилемВ запуске править нечего
diff не выводит ничегоНи один профиль не дошёл до браузераПроверьте executablePath и что запущенный процесс это двоичный файл BotBrowser
Различается только ua, а screen и inner совпадаютНастройка области просмотра всё ещё действует, либо оба профиля описывают тот же экранПроверьте defaultViewport: null, затем сравните поля экрана в профиле
ua совпадает, но отсутствует поле, специфичное для профиляФайл профиля не загрузился или не совпадает с мажорной версией двоичного файлаСверьте пакет профиля с установленной мажорной версией BotBrowser

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

Если сравнение не вывело ничего, прекращайте отладку страницы. Ни newPage, ни goto, ни селектор не заставят профиль примениться: это решение уже принято внутри вызова запуска.

Что покрывает BotBrowser, а что нет

BotBrowser документирует запуск Puppeteer через puppeteer-core с browser, равным chrome, с executablePath, --bot-profile и defaultViewport: null, чтобы размеры viewport из профиля оставались главными вместо значения 800x600 по умолчанию у Puppeteer, а команды CDP BotBrowser отправлялись в сессии уровня браузера. Для вас это означает короткий проверяемый порядок запуска: если сессия выглядит несогласованной, вы можете сверить её с этими четырьмя пунктами и найти тот, который отличается. BotBrowser не может исправить запуск, который перезаписывает Puppeteer, например defaultViewport, оставленный по умолчанию, не заменяет правильную конфигурацию запуска Puppeteer и не гарантирует, как любой целевой сайт отнесётся к сессии.

Те же ограничения действуют для соседних тем. Флаги прокси и региона зависят от прокси и от вашего уровня BotBrowser, а поддержка прокси для отдельного контекста зависит и от версии Puppeteer, и от уровня. Сравнение BotBrowser с плагинами, исправляющими страницу, лишь говорит, на каком уровне действует каждый подход: один работает через исполняемый файл браузера, другой через внедрённый JavaScript. Оно ничего не говорит о том, как отреагирует на любой из них какой-либо сайт.

О смежных конфигурациях читайте: Начало работы с Playwright, если вы используете и Playwright, Рецепты CLI для сочетаний флагов, Настройка headless-сервера для запуска на сервере и Настройка прокси для синтаксиса прокси и учётных данных.

Открытые источники

Итоги и практические выводы

Четыре решения запуска проверяются до первой строки кода страницы: executablePath указывает на исполняемый файл BotBrowser, --bot-profile ссылается на абсолютный файл .enc, соответствующий установленной основной версии, defaultViewport: null не даёт перезаписи 800x600 от Puppeteer добраться до страницы, а команды BotBrowser идут через сессию из browser.target(). Если сессия выглядит несогласованной, сверьте её с этими четырьмя пунктами, прежде чем менять селекторы или навигацию. BotBrowser настраивает запуск; он не исправляет параметр, который перезаписывает Puppeteer, и не решает, как целевой сайт отнесётся к получившейся сессии.

#Puppeteer#автоматизация#быстрый старт#руководство

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

Переведите BotBrowser из исследований в продакшн

Используйте эти руководства, чтобы понять модель, а затем перейти к кроссплатформенной валидации, изолированным контекстам и масштабируемому браузерному развертыванию.