Для всех, кто задается вопросом, в Puppeteer существует множество стратегий для рендеринга загруженных изображений или ресурсов, но не все из них работают одинаково хорошо. Небольшие подробности реализации на веб-сайте, который вы пытаетесь сделать на скриншоте, могут изменить конечный результат, поэтому, если вы хотите иметь реализацию, которая хорошо работает во многих сценариях, вам нужно будет изолировать каждый общий случай и рассмотреть его индивидуально.
Я знаю это, потому что я запускаю небольшой проект «Снимок как услуга» (https://getscreenshot.rasterwise.com/)), и мне пришлось рассматривать множество случаев отдельно. Это большая задача этого проекта, поскольку, кажется, всегда есть что-то новое, что необходимо быть обращенным к новым библиотекам и методам пользовательского интерфейса, используемым каждый день.
При этом я думаю, что есть некоторые стратегии рендеринга, которые имеют хорошее освещение. Вероятно, лучшая из них - это комбинация ожидания и прокрутки страницы, как это сделал OP, но при этом необходимо учитывать порядок операций. Вот немного измененная версия оригинального кода OP.
//Scroll and Wait Strategy
function waitFor (ms) {
return new Promise(resolve => setTimeout(() => resolve(), ms));
}
async function capturePage(browser, url) {
// Load the page that you're trying to screenshot.
const page = await browser.newPage();
await page.goto(url, {waitUntil: 'load'}); // Wait until networkidle2 could work better.
// Set the viewport before scrolling
await page.setViewport({ width: 1366, height: 768});
// Get the height of the page after navigating to it.
// This strategy to calculate height doesn't work always though.
const bodyHandle = await page.$('body');
const { height } = await bodyHandle.boundingBox();
await bodyHandle.dispose();
// Scroll viewport by viewport, allow the content to load
const calculatedVh = page.viewport().height;
let vhIncrease = 0;
while (vhIncrease + calculatedVh < height) {
// Here we pass the calculated viewport height to the context
// of the page and we scroll by that amount
await page.evaluate(_calculatedVh => {
window.scrollBy(0, _calculatedVh);
}, calculatedVh);
await waitFor(300);
vhIncrease = vhIncrease + calculatedVh;
}
// Setting the viewport to the full height might reveal extra elements
await page.setViewport({ width: 1366, height: calculatedVh});
// Wait for a little bit more
await waitFor(1000);
// Scroll back to the top of the page by using evaluate again.
await page.evaluate(_ => {
window.scrollTo(0, 0);
});
return await page.screenshot({type: 'png'});
}
Вот некоторые ключевые отличия:
- Вы хотите установить область просмотра с самого начала и работать с этим фиксированным окном просмотра.
- Вы можете изменить время ожидания и ввести произвольные ожидания для эксперимента. Иногда это приводит к раскрытию элементов, которые висят за сетевыми событиями.
- Изменение области просмотра на полную высоту страницы также может показать элементы, как если бы вы прокручивали. Вы можете проверить это в реальном браузере, используя вертикальный монитор. Однако обязательно вернитесь к исходной высоте области просмотра, поскольку область просмотра также влияет на предполагаемый рендеринг.
Здесь нужно понять одну вещь: ожидание в одиночку не обязательно приведет к загрузке ленивых активов. Прокрутка по высоте документа позволяет в окне просмотра отображать те элементы, которые должны находиться в окне просмотра для загрузки.
Еще одна оговорка: иногда вам нужно ждать относительно долгое время для загрузки актива, поэтому в приведенном выше примере вам может потребоваться поэкспериментировать с количеством времени, которое вы ждете после каждой прокрутки. Кроме того, как я упоминал, произвольные ожидания в общем исполнении иногда влияют на загрузку актива или нет.
Как правило, при использовании Puppeteer для снимков экрана вы хотите убедиться, что ваша логика напоминает реальное поведение пользователя. Ваша цель - воспроизвести сценарии рендеринга, как если бы кто-то запускал Chrome на своем компьютере и переходил на этот веб-сайт.