【问题标题】:How to speed up puppeteer?如何加快傀儡师的速度?
【发布时间】:2020-11-01 06:27:41
【问题描述】:

网页有一个按钮,puppeteer 必须在按钮变得可见时尽快单击该按钮。这个按钮并不总是可见的,它同时对每个人都可见。所以我必须不断刷新才能发现该按钮变得可见。为此,我在下面编写了该脚本:

    const browser = await puppeteer.launch({
        headless: true,
        args: ['--no-sandbox']
    });
    const page = await browser.newPage()
    await page.setViewport({ width: 1920, height: 1080})


//I am calling my pageRefresher method here

async function pageRefresher(page,browser, url) {
        try {
            await page.goto(url, {waitUntil: 'networkidle2'})
            try {
                await page.waitForSelector('#ourButton', {timeout: 10});
                await page.click('#ourButton')
                console.log(`clicked!`)
                await browser.close()
            } catch (error) {
                console.log('catch2 ' + counter + ' '  + error)
                counter += 1
                await pageRefresher(page, browser, url)
            }
        }catch (error) {
            console.log('catch3' + error)
            await browser.close();
        }
}

如您所见,我的方法是递归的。它转到那个页面并寻找那个按钮。如果没有按钮,则它会再次调用自身以重做相同的工作,直到找到并单击该按钮。

实际上它现在运行良好。但它很慢。我正在运行此脚本,同时我在我的桌面 chrome 上打开同一页面,并且我开始手动刷新该页面。而且我总是在获胜,我总是在木偶师之前点击那个按钮。

我怎样才能加快这个过程?脚本不应该输给只有手动控制(如 F5 按钮)的人。

【问题讨论】:

  • 使用递归是循环的不良替代品。如果递归调用过多,您可能会导致程序崩溃。

标签: javascript node.js puppeteer


【解决方案1】:

脚本不应该输给只有 F5 按钮等手动控制的人。

这是因为有时 puppeteer 遵循的规则比我们认为的“完全加载的网页”要严格得多。即使您作为人类可以决定您想要的元素是已经在 DOM 中(因为您看到该元素在那里)还是不在那里(因为您看不到它)。例如:即使背景图像仍在后台加载,您也会看到您的按钮不存在,或者 webfonts 仍未加载并且您有备用字体,但 puppeteer 等待后台中的特定事件获得权限要么转到 catch 块(超时),要么抓取所需的元素(waitForSelector 成功)。这实际上取决于您访问的网站,但您可以加快识别所需元素的过程。

我给出了一些例子和想法,你可以如何做到这一点。


加快识别所需元素的方法

1.) 如果您的任务不需要每个网络连接,您可以通过将 waitUntil: 'networkidle2' 替换为 waitUntil: 'domcontentloaded' 来加速页面加载,因为此事件通常发生得更早,并且会在 #ourButton 已经存在时触发存在于 DOM 中。

page.goto/page.reload的可能选项:

  • load - 考虑在load 事件触发时完成导航。
  • domcontentloaded - 考虑在DOMContentLoaded 事件触发时完成导航。
  • networkidle0 - 在至少 500 毫秒内没有超过 0 个网络连接时考虑完成导航。
  • networkidle2 - 在至少 500 毫秒内没有超过 2 个网络连接时考虑完成导航。

因为networkidle2 太严格了,所以你赢了剧本。您可能需要此选项(例如,您正在访问单页应用程序或稍后您将需要来自 3rd 方网络连接的数据,例如 cookie),但如果不是强制性的,您将体验到更好的性能 domcontentloaded

2.) 您可以在循环中使用page.reload 方法,而不是不断导航到相同的网址,例如:

await page.goto(url, { waitUntil: 'domcontentloaded' })
let selectorExists = await page.$('#ourButton')

while (selectorExists === null) {
  await page.reload({ waitUntil: 'domcontentloaded' })
  console.log('reload')
  selectorExists = await page.$('#ourButton')
}
await page.click('#ourButton')
// code goes on...

它的主要好处是您可以缩短和简化您的pageRefresher 函数。但是我也体验到了更好的性能(但是我没有进行基准测试,但我觉得它比重新打开页面要快得多)。

3.) 如果您的任务不需要每种资源类型,您还可以通过使用以下脚本禁用图像或 css 来加快页面加载:

await page.setRequestInterception(true)
page.on('request', (request) => {
  if (request.resourceType() === 'image') request.abort()
  else request.continue()
})

[source]

List of resourceType-s.

【讨论】:

  • 请求图像阻止技术与阻止谷歌分析等非常相似,这也在固定请求......:D
【解决方案2】:

尽量不要等待 goto:

page.goto(url) // no await because it doesn't have to resolve fully
await page.waitForSelector('#ourButton') // await this because we need it to be there

有些人为此喜欢 Promise.race,但这种方式更简单

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-09-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-04-28
    • 2014-06-06
    相关资源
    最近更新 更多