Создание Screenshot API для рендеринга тяжелых JS-сайтов

#javascript#puppeteer#screenshot-api#nodejs#performance

TL;DR: Разработка Screenshot API для рендеринга JS-heavy сайтов — это вызов. Использование пула браузеров и кэширования позволяет улучшить производительность и стабильность.

Введение

В современном вебе множество сайтов используют тяжелые JavaScript-фреймворки, такие как React, Angular или Vue. Рендеринг таких страниц для создания скриншотов или экспорта в PDF может быть сложной задачей. Puppeteer и Playwright — популярные инструменты для автоматизации браузеров, но их использование в production может быть сопряжено с рядом проблем: от случайных ошибок загрузки шрифтов до нестабильного рендеринга контента.

Основные проблемы и их решения

1. Шрифты и иконки не загружаются

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

const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('https://example.com', { waitUntil: 'networkidle2' });
await page.screenshot({ path: 'screenshot.png' });
await browser.close();

Решение: Убедитесь, что вы используете waitUntil: 'networkidle2' для ожидания завершения всех сетевых запросов.

2. Холодные старты и производительность

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

Решение: Используйте пул браузеров для повторного использования уже запущенных экземпляров.

const browserPool = [];
const poolSize = 5;

async function getBrowser() {
  if (browserPool.length < poolSize) {
    const browser = await puppeteer.launch();
    browserPool.push(browser);
  }
  return browserPool.shift();
}

async function releaseBrowser(browser) {
  browserPool.push(browser);
}

3. Память и тяжелые страницы

Некоторые страницы могут потреблять большое количество памяти, что приводит к утечкам и сбоям.

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

let pageCount = 0;
const maxPagesPerBrowser = 10;

async function createPage() {
  if (pageCount >= maxPagesPerBrowser) {
    await browser.close();
    browser = await puppeteer.launch();
    pageCount = 0;
  }
  const page = await browser.newPage();
  pageCount++;
  return page;
}

4. Рендеринг JS-контента

Иногда страница считается загруженной, но JS-контент еще не отрендерен.

Решение: Используйте кастомные ожидания для проверки наличия ключевых элементов на странице.

await page.waitForSelector('#main-content', { timeout: 5000 });

Практическое применение

Кэширование повторяющихся рендеров

Кэширование результатов рендеринга для одинаковых URL может значительно снизить нагрузку на сервер.

const cache = new Map();

async function renderPage(url) {
  if (cache.has(url)) {
    return cache.get(url);
  }
  const browser = await getBrowser();
  const page = await browser.newPage();
  await page.goto(url, { waitUntil: 'networkidle2' });
  const screenshot = await page.screenshot();
  cache.set(url, screenshot);
  releaseBrowser(browser);
  return screenshot;
}

Заключение

Создание стабильного Screenshot API для рендеринга JS-heavy сайтов требует тщательной настройки и оптимизации. Использование пула браузеров, кэширования и кастомных ожиданий позволяет значительно улучшить производительность и стабильность системы. Несмотря на то, что Puppeteer и Playwright предоставляют мощные инструменты для автоматизации, их использование в production требует дополнительных усилий для обработки всех возможных сценариев.


Источник: https://www.reddit.com/r/javascript/comments/1thucyc/askjs_screenshot_api_that_renders_heavy_js/