检测渲染事件/布局更改(或任何知道页面何时停止“更改”的方式)

时间:2018-09-07 12:50:47

标签: puppeteer puppeteer-sharp

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

问题在于页面加载后页面会通过JavaScript进行几次布局更改,因此需要几秒钟的时间才能看到页面的“最终”呈现版本。

此刻,我只是在等待“安全”的秒数之后才截取屏幕截图,但这显然不是一个好方法,因为计算机的暂时性能下降会导致渲染不完整。

由于puppeteer在后台使用Chromium,是否有办法拦截Chromium的布局/渲染事件(就像您可以在Chrome的DevTools控制台中所做的那样)?或者,实际上,可以通过其他任何方式来知道页面何时停止“更改”(以视觉方式)

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

2 个答案:

答案 0 :(得分:0)

您应该使用PrimeNg Cell Style等待动态元素完成渲染。

必须有一种可以根据生成的内容进行识别的模式。

请记住,您可以使用灵活的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.waitForSelector()

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

或者,您可以将page.waitForXPath()接口插入MutationObserver,以监视对DOM树所做的更改。在一段时间内停止更改后,您可以继续执行程序。

答案 1 :(得分: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,这些计数器和ID会随时间而变化,因此我考虑了一些小的差异。