0

我正在使用 Puppeteer(实际上是 PuppeteerSharp,但 API 相同)从我的应用程序中截取网页的屏幕截图。

问题是页面加载后通过 JavaScript 进行了几次布局更改,因此在看到页面的“最终”呈现版本之前经过了几秒钟。

目前我只是在截屏前等待“安全”的几秒钟,但这显然不是一个好方法,因为机器上的暂时性能下降可能会导致渲染不完整。

由于 puppeteer 在后台使用 Chromium,有没有办法拦截 Chromium 的布局/渲染事件(就像你可以在 Chrome 的 DevTools 控制台中做的那样)?或者,真的,知道页面何时停止“变化”的任何其他方式(我的意思是视觉上)

编辑,更多信息:内容是动态的,所以我事先不知道它将绘制什么以及如何绘制。基本上,它是一个绘制不同图表/表格/图像/等的框架。(不幸的是不是开源的)。然而,通过使用 Chrome DevTools 中的“性能”工具进行测试,我注意到在页面完成渲染后,时间轴中的所有活动都会停止,所以如果我可以访问该信息,那就太好了。不幸的是,在 Puppeteer(我可以看到)中做到这一点的唯一方法是使用“跟踪”功能,但这并不是实时运行的。相反,它将跟踪转储到文件,并且缓冲区太大而无法使用(在我的页面已经完成渲染后文件仍然是 0 字节,当我调用“stopTracing”时它只会刷新到磁盘)。我需要的是实时访问 puppeteer 的跟踪功能,例如通过事件或内存流,但这似乎不受 API 支持。有什么办法吗?

4

2 回答 2

0

您应该使用page.waitForSelector()等待动态元素完成渲染。

必须有一种可以根据正在生成的内容来识别的模式。

请记住,您可以使用灵活的 CSS 选择器来匹配元素或属性,而无需知道它们的确切值。

await page.goto( 'https://example.com/', { 'waitUntil' : 'networkidle0' } );

await Promise.all([
    page.waitForSelector( '[class^="chart-"]' ),    // Class begins with 'chart-'
    page.waitForSelector( '[name$="-image"]' ),     // Name ends with '-image'
    page.waitForSelector( 'table:nth-of-type(5)' )  // Fifth table
]);

这在等待 DOM 中存在特定模式时很有用。

如果page.waitForSelector()功能不足以满足您的需求,您可以使用page.waitForXPath()

await page.waitForXPath( '//div[contains(text(), "complete")]' ); // Div contains 'complete'

或者,您可以插入MutationObserver接口page.evaluate()以观察对 DOM 树所做的更改。当更改在一段时间内停止时,您可以恢复您的程序。

于 2018-09-07T19:00:07.730 回答
0

经过反复试验,我选择了这个解决方案:

string traceFile = IOHelper.GetTemporaryFile("txt");
long lastSize = 0;
int cyclesWithoutTraceActivity = 0;
int totalCycles = 0;
while (cyclesWithoutTraceActivity < 4 && totalCycles < 25)
{

    File.Create(traceFile).Close();
    await page.Tracing.StartAsync(new TracingOptions()
    {
        Categories = new List<string>() { "devtools.timeline" },
        Path = traceFile,
    });

    Thread.Sleep(500);                

    await page.Tracing.StopAsync();

    long curSize = new FileInfo(traceFile).Length;
    if(Math.Abs(lastSize - curSize) > 5)
    {
        logger.Debug("Trace activity detected, waiting...");
        cyclesWithoutTraceActivity = 0;
    }
    else
    {
        logger.Debug("No trace activity detected, increasing idle counter...");
        cyclesWithoutTraceActivity++;
    }
    lastSize = curSize;

    totalCycles++;
}
File.Delete(traceFile);
if(totalCycles == 25)
{
    logger.Warn($"WARNING: page did not stabilize within allotted time limit (15 seconds). Rendering page in current state, might be incomplete");
}

基本上我在这里所做的是:我以 500 毫秒的间隔运行 Chromium 的跟踪,每次我将最后一个跟踪文件的大小与当前跟踪文件的大小进行比较。大小的任何显着变化都被解释为时间线上的活动,它们会重置空闲计数器。如果足够长的时间过去了而没有发生重大变化,我认为页面已经完成渲染。请注意,跟踪文件总是以一些调试信息开头(即使时间线本身没有要报告的活动),这就是我不进行精确大小比较的原因,而是检查文件的长度是否超过相隔 5 个字节:由于初始调试信息包含一些随时间变化的计数器和 ID,因此我允许有一点差异来解释这一点。

于 2018-09-20T15:59:14.300 回答