перейти к содержанию

Как делать снимки экрана с помощью Puppeteer для эффективного парсинга веб-страниц

Puppeteer — это библиотека Node.js, предоставляющая мощный API для управления безголовым Chrome и Chromium через протокол DevTools. Одной из наиболее полезных функций является возможность программного создания снимков экрана веб-страниц и элементов.

Для парсеров возможность делать снимки экрана с помощью Puppeteer открывает множество ценных вариантов использования:

  • Визуальная отладка проблем парсинга и неудачных тестов.
  • Регистрация состояний динамических страниц и SPA.
  • Мониторинг визуальных регрессов и изменений пользовательского интерфейса.
  • Создание руководств и документации со скриншотами для контекста.
  • Создание графических ресурсов из веб-страниц.

В этом подробном руководстве мы рассмотрим, как использовать снимки экрана Puppeteer для улучшения рабочих процессов парсинга веб-страниц.

Расцвет Puppeteer для парсинга веб-страниц

Puppeteer был впервые выпущен в 2017 году и быстро был принят сообществом веб-парсеров. Вот несколько статистических данных, которые подчеркивают его популярность:

  • Более 52,000 XNUMX звезд на Github делают его одним из лучших JS-проектов.
  • Более 3 миллионов загрузок в неделю на NPM.
  • Рост количества запросов в Google по запросу «Кукловод» на 490% по сравнению с прошлым годом в 2022 году.

Так что же отличает Puppeteer для парсинга веб-страниц?

Безголовое управление браузером

Puppeteer обеспечивает полный контроль над безгласным браузером через протокол Chrome DevTools. Это позволяет копировать взаимодействия пользователей для автоматизации и очистки динамического контента.

Легкий и быстрый

Использование только headless означает, что Puppeteer пропускает весь рендеринг пользовательского интерфейса, который делает Chromium тяжеловесным. Это приводит к высокой производительности при крупномасштабном парсинге.

Активное развитие

При поддержке команды Chrome в Google Puppeteer получает частые обновления и новые функции, адаптированные для случаев автоматизации и очистки данных.

Проще, чем селен

Puppeteer фокусируется только на управлении Chromium, тогда как Selenium поддерживает несколько браузеров. API стал намного чище и идиоматичнее, что делает его простым в использовании.

По этим причинам многие веб-парсеры переходят на Puppeteer с Selenium/WebDriver для повышения скорости, надежности и возможностей.

Теперь давайте углубимся в то, как использовать мощные возможности Puppeteer по созданию снимков экрана.

Создание снимков экрана всей страницы

Самый простой способ сделать снимок экрана всей страницы — использовать page.screenshot() Метод:

// Launch browser
const browser = await puppeteer.launch();

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

// Screenshot
await page.screenshot({
  path: ‘fullpage.png‘ 
});

Это захватывает видимый в данный момент область просмотра. Чтобы сделать снимок экрана во всю высоту страницы, установите fullPage вариант true:

await page.screenshot({
  path: ‘longpage.png‘,
  fullPage: true
}); 

Указание параметров изображения

Ассоциация screenshot() метод принимает параметры для управления типом, качеством и многим другим:

  • type – PNG, JPEG или WebP. По умолчанию — png.
  • quality – Для jpeg/webp качество варьируется от 0 до 100. По умолчанию — 80.
  • omitBackground – Скрывает белый фон по умолчанию и обеспечивает прозрачность.
  • encoding – Можно выводить в формате Base64 вместо сохранения файла.

Например, чтобы сохранить JPEG высокого качества:

await page.screenshot({
  path: ‘page.jpeg‘,
  type: ‘jpeg‘,
  quality: 100
});

Функции: используйте webp для лучшего сжатия с таким же качеством. Однако у webp могут быть проблемы с совместимостью.

Работа с большими скриншотами

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

Чтобы обрабатывать большие снимки экрана, передайте параметр encoding: ‘base64‘ чтобы получить строку base64 вместо буфера. Затем сохраните с помощью fs.writeFile(), чтобы избежать буферизации изображения в памяти.

Вот пример:

const buffer = await page.screenshot({ encoding: ‘base64‘ });

fs.writeFile(‘screenshot.png‘, buffer, ‘base64‘, err => {
  // handle error 
});

Прокрутка высоких страниц для захвата всей страницы

Чтобы захватить всю высоту страниц, длиннее области просмотра, нам нужно сначала прокрутить страницу.

Вот один из подходов с использованием page.evaluate():

// Scroll to bottom  
await page.evaluate(() => {
  window.scrollTo(0, document.body.scrollHeight);
});

// Screenshot full scrollable area
await page.screenshot({ path: ‘longpage.png‘, fullPage: true });

Мы также можем поэтапно прокручивать снимки экрана, а затем объединять их в один высокий снимок экрана. Это предотвращает необходимость буферизации всего изображения в памяти.

Альтернатива: сохранить в формате PDF.

Еще один вариант захвата всего содержимого страницы — создание PDF-файла!

// Generates PDF and saves to disk 
await page.pdf({
  path: ‘page.pdf‘,
  printBackground: true
});

Плюсы PDF-файлов:

  • Обрабатывает многостраничный контент «из коробки».
  • Векторный формат обычно приводит к уменьшению размера файла.
  • Форматирование печати остается неизменным.

Минусы:

  • Менее гибок для программной обработки.
  • Ограниченные возможности оформления по сравнению с изображениями.
  • Может не захватывать динамически отображаемый контент.

Настройка размера области просмотра

По умолчанию Puppeteer использует область просмотра 800 x 600 пикселей. Чтобы получить точные скриншоты всей страницы на разных размерах настольных компьютеров и мобильных устройств, мы можем явно указать область просмотра:

// 1200px wide desktop 
await page.setViewport({
  width: 1200,
  height: 800  
});

// 400px wide mobile
await page.setViewport({
  width: 400,
  height: 1200 
});

Тогда снимки экрана будут соответствовать указанному размеру области просмотра.

Захват элементов

В дополнение к полноэкранным снимкам страниц мы можем делать снимки отдельных элементов, используя element.screenshot().

// Get reference to element
const menu = await page.$(‘.main-menu‘);

// Screenshot just that element
await menu.screenshot({path: ‘menu.png‘});

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

Некоторые варианты использования скриншотов элементов:

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

Скриншоты закадровых элементов

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

Мы можем использовать автоматическую прокрутку элементов. element.screenshot() для надежного захвата элементов в любом состоянии, даже когда они находятся вне экрана:

// Click button which hides the element 
await page.click(‘.toggle-menu‘);

// Menu is now hidden but we can still screenshot it
await menu.screenshot({path: ‘hidden-menu.png‘}); 

Это позволяет легко делать снимки экрана без сброса состояния страницы.

Ожидание загрузки динамического контента

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

Вот пример ожидания появления элемента:

// Click button to trigger ajax call
await page.click(‘.load-content‘);

// Wait for new content to load
await page.waitForSelector(‘.loaded‘);

// Screenshot after loaded
await page.screenshot({path: ‘loaded.png‘}); 

page.waitForSelector() прежде чем продолжить, ждет, пока селектор не появится в DOM.

Некоторые другие полезные ожидания включают в себя:

  • page.waitFor() – Подождите, пока данное условие станет истинным.
  • page.waitForFunction() – Дождитесь завершения асинхронных обновлений DOM.
  • page.waitUntil() – Подождите, пока произойдет навигация.

Ключевым моментом является выбор правильного условия ожидания для обновления страницы, которое вы хотите запечатлеть на снимке экрана.

Ожидание конкретных изменений DOM

Чтобы синхронизироваться с более дискретными изменениями DOM, мы можем дождаться обновления атрибутов вместо общих селекторов:

// Wait for text content to change
await page.waitForFunction(() => {
  return document.querySelector(‘.status‘).textContent === ‘Loaded‘; 
});

// Element updated  
await page.screenshot({/*...*/});

Этот подход хорошо работает для ожидания загрузки ключевых данных, а не для статических изменений DOM.

Работа с одностраничными приложениями (SPA)

Ожидание изменений DOM может оказаться затруднительным из-за сложных SPA-интерфейсов JavaScript, которые обновляют состояние без перезагрузки.

Несколько советов по обращению с ними:

  • После взаимодействия дождитесь простоя сети, чтобы завершить XHR.
  • Подождите, пока исчезнут определенные компоненты, такие как наложения, вместо общих селекторов.
  • Прокрутите до нужного раздела, чтобы принудительно выполнить рендеринг, прежде чем делать снимок экрана.
  • Используйте дополнительные ожидания вместо фиксированных таймаутов.

Ни один подход не работает идеально для всех SPA. Вам придется поэкспериментировать с рассматриваемым приложением.

Прокрутка страниц перед созданием полных снимков экрана

Для страниц, требующих прокрутки, нам нужно будет прокрутить программно, прежде чем делать полный снимок экрана с помощью fullPage: true.

Вот надежный подход:

await page.evaluate(() => {
  // Scroll to bottom
  window.scrollTo(0, document.body.scrollHeight);
}); 

// Capture full scrolled screenshot  
await page.screenshot({fullPage: true});

Это прокручивает страницу вниз до максимального положения прокрутки, прежде чем сделать снимок экрана.

Альтернативой является использование window.scrollBy() для постепенной прокрутки определенного количества за раз. Это позволяет делать непрерывные снимки экрана при прокрутке страницы на всю длину.

Обработка длинных прокручиваемых страниц

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

Хорошее решение — разбить его на разделы, прокручивать понемногу, делать снимки экрана и сшивать их вместе:

const screenshots = [];

while (hasMoreContent()) {

  await page.evaluate(scrollDown);

  screenshots.push(await page.screenshot()); 

}

// Stitch screenshots together into one tall image

Это избавляет от необходимости буферизировать всю высоту страницы в памяти.

Горизонтальная прокрутка тоже

Для страниц с горизонтальной прокруткой мы можем настроить последовательность прокрутки так, чтобы она также прокручивалась горизонтально:

await page.evaluate(() => {
  window.scrollTo(
    document.body.scrollWidth, 
    document.body.scrollHeight
  );
});

await page.screenshot({fullPage: true});

Это захватывает всю ширину и высоту страницы!

Лучшие практики для создания надежных снимков экрана

Вот несколько ключевых советов по созданию последовательных и надежных снимков экрана с помощью Puppeteer:

Подождите, пока сеть будет простаивать - использовать page.waitForNetworkIdle() после взаимодействия, чтобы гарантировать выполнение всех асинхронных запросов перед записью состояния.

Используйте соответствующие ожидания – Выбирайте условные ожидания, которые синхронизируются с желаемым состоянием страницы, а не общие тайм-ауты.

Установить размер области просмотра – Явно настройте область просмотра для получения точных снимков экрана устройства.

Защита от анимации/всплывающих окон – Наведение курсора на элементы может вызвать изменения – используйте page.evaluate() чтобы избежать побочных эффектов.

Дайте время на рендеринг – Подождите несколько сотен миллисекунд после прокрутки страниц, чтобы завершить рендеринг, прежде чем делать снимки экрана.

Стабилизация нестабильных тестов – Установите цикл повторов с ожиданием вокруг шагов скриншота для обработки хлопьев.

Сравнить с известным хорошим – Используйте инструменты визуального регрессионного тестирования, чтобы выявить непреднамеренные изменения.

Заключение

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

Некоторые ключевые темы, которые мы рассмотрели:

  • Использование page.screenshot() и element.screenshot() для создания снимков экрана
  • Параметры управления типом, качеством и форматом изображения.
  • Прокрутка страниц и ожидание динамического контента
  • Настройка размера области просмотра для адаптивных страниц
  • Рекомендации по обеспечению надежных рабочих процессов создания снимков экрана

Автоматизированные снимки экрана неоценимы для отладки парсеров, визуального тестирования и захвата динамических состояний. Добавьте их в свой набор инструментов для парсинга веб-страниц с помощью Puppeteer!

Присоединяйтесь к беседе

Ваш электронный адрес не будет опубликован. Обязательные поля помечены * *