Puppeteer:屏幕截图懒惰图像不起作用

Puppeteer: Screenshot lazy images not working

我似乎无法从 https://today.line.me/HK/pc 成功捕获屏幕截图。

在我的 Puppeteer 脚本中,我还开始滚动到页面底部并再次向上滚动以确保图像已加载。但由于某种原因,它似乎在上面的 URL 行中不起作用。

function wait (ms) {
 return new Promise(resolve => setTimeout(() => resolve(), ms));
}

const puppeteer = require('puppeteer');

async function run() {
let browser = await puppeteer.launch({headless: false});
let page = await browser.newPage();
await page.goto('https://today.line.me/HK/pc', {waitUntil: 'load'});
//https://today.line.me/HK/pc
// Get the height of the rendered page
  const bodyHandle = await page.$('body');
  const { height } = await bodyHandle.boundingBox();
  await bodyHandle.dispose();

  // Scroll one viewport at a time, pausing to let content load
  const viewportHeight = page.viewport().height+200;
  let viewportIncr = 0;
  while (viewportIncr + viewportHeight < height) {
    await page.evaluate(_viewportHeight => {
      window.scrollBy(0, _viewportHeight);
    }, viewportHeight);
    await wait(4000);
    viewportIncr = viewportIncr + viewportHeight;
  }

  // Scroll back to top
  await page.evaluate(_ => {
    window.scrollTo(0, 0);

  });

 // Some extra delay to let images load
 await wait(2000);

await page.setViewport({ width: 1366, height: 768});
await page.screenshot({ path: './image.png', fullPage: true });
}

run();

我已经通过更改滚动页面和等待延迟的逻辑解决了这个问题。

对于任何想知道的人,有许多策略可以在 Puppeteer 中渲染延迟加载的图像或资产,但并非所有策略都同样有效。您尝试截屏的网站中的小实现细节可能会改变最终结果,因此如果您希望实现在许多案例场景中都能很好地工作,则需要隔离每个通用案例并单独解决。

我知道这一点,因为我 运行 是一个小型 Screenshot API 服务,我不得不分别处理许多情况。这是该项目的一项重大任务,因为似乎总是有一些新问题需要通过新库和每天使用的 UI 技术来解决。

话虽这么说,但我认为有一些渲染策略具有很好的覆盖率。可能最好的方法是像 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 并导航到该网站。

一个对我有用的解决方案:

调整我的测试运行程序 (mocha) 的超时限制。

// package.json
"scripts": {
  "start": "react-scripts start",
  "build": "react-scripts build",
  "eject": "react-scripts eject",
  "test": "mocha --timeout=5000" <--- set timeout to something higher than 2 seconds
},

x ~ half of what you set above 处等待 x 秒,然后截屏。

var path = require("path"); // built in with NodeJS
await new Promise((resolve) => setTimeout(() => resolve(), 2000));
var file_path = path.join(__dirname, "__screenshots__/initial.png");
await page.screenshot({ path: file_path });