【问题标题】:Clicking a selector with Puppeteer使用 Puppeteer 单击选择器
【发布时间】:2020-09-07 19:04:24
【问题描述】:

所以我在点击耐克网站上的登录按钮时遇到了问题..

我不确定为什么它总是崩溃,因为它找不到我猜的选择器,但我不确定我做错了什么。

我还想说在 puppeteer 崩溃之前我有某种内存泄漏,如果我不及时取消控制台内的进程,有时它甚至会使我的 macbook 完全崩溃。

编辑: 如果我没有足够快地取消应用程序,这段代码还会在崩溃时导致内存泄漏,迫使我不得不硬重置我的 mac。

节点版本:14.4.0 木偶版:5.2.1

当前代码:

const puppeteer = require('puppeteer');

(async () => {
    const browser = await puppeteer.launch({
        headless: false,
        defaultViewport: null,
        args: ['--start-maximized']
    })

    const page = await browser.newPage()
    await page.goto('https://www.nike.com/')

    const winner = await Promise.race([
        page.waitForSelector('[data-path="join or login"]'),
        page.waitForSelector('[data-path="sign in"]')
    ])

    await page.click(winner._remoteObject.description)
})()

我也试过了:

await page.click('button[data-var]="loginBtn"');

【问题讨论】:

    标签: javascript node.js puppeteer


    【解决方案1】:

    试试看:

    await page.click('button[data-var="loginBtn"]');
    

    【讨论】:

    • 在我的例子中,登录按钮位于这样一个选择器'button[data-type="click_navJoinLogin"]'
    • 感谢上面的答案,我确信这与 A/B 测试有关,这就是我们选择器不同的原因。 :)
    【解决方案2】:

    他们是A/B testing 他们的网站,因此您登陆的页面可能与您从自己的 chrome 浏览器访问该网站时检索到的选择器截然不同。

    在这种情况下,您可以尝试使用 XPath 及其contains 方法通过元素的文本内容来获取元素(不幸的是,在这种特殊情况下,设计也会发生变化)。例如。 $x('//span[contains(text(), "Sign In")]')[0]

    所以我建议检测两个按钮版本并获取它们最稳定的选择器,这些也可以基于数据属性:

    一个

    $('[data-path="sign in"]')
    

    B

    $('[data-path="join or login"]')
    

    然后使用Promise.race,您可以检测存在哪个按钮,然后从JSHandle@node 中提取其选择器,如下所示:._remoteObject.description

    {
      type: 'object',
      subtype: 'node',
      className: 'HTMLButtonElement',
      description: 'button.nav-btn.p0-sm.body-3.u-bold.ml2-sm.mr2-sm',
      objectId: '{"injectedScriptId":3,"id":1}'
    }
    

    =>

    button.nav-btn.p0-sm.prl3-sm.pt2-sm.pb2-sm.fs12-nav-sm.d-sm-b.nav-color-grey.hover-color-black
    

    例子:

    const browser = await puppeteer.launch({
      headless: false,
      defaultViewport: null,
      args: ['--start-maximized']
    })
    const page = await browser.newPage()
    await page.goto('https://www.nike.com/')
    const winner = await Promise.race([
      page.waitForSelector('[data-path="join or login"]'),
      page.waitForSelector('[data-path="sign in"]')
    ])
    
    await page.click(winner._remoteObject.description)
    

    仅供参考:同时最大化浏览器窗口以确保元素具有相同的选择器名称。

    defaultViewport: null, args: ['--start-maximized']
    

    Chromium 默认使用 puppeteer 以较小的窗口启动。

    【讨论】:

    • 太棒了,感谢您提供的所有新信息!我仍然无法点击它,我确实明白你所说的页面不同的意思,现在我什至没有注意到这一点!
    • 承诺被拒绝,所以我猜它仍然无法找到选择器?
    • 是的,它可以是一个包含两个以上 UX 迭代的 A/B 测试。还要确保最大化浏览器窗口(如果你还没有这样做的话),你可以避免 puppeteer 加载平板版本。
    • 是的,我已经完全按照您在示例中所做的。我将更新帖子以显示当前代码,铬窗口在 UnhandledPromiseRejection 之前冻结一秒钟,TimeoutError 在控制台中引发错误。然后我就可以关闭窗口了。
    • 我想await Promise.race 是导致 Mac 内存泄漏的罪魁祸首(在这个特定网站的情况下......否则这将是一个众所周知的问题。木偶开发人员也在 Mac 上工作)。你能用你的 Node.Js 版本、确切的 puppeteer 版本和你的确切操作系统来更新你的问题吗?谢谢(从现在开始我不确定是否能提供帮助,我在 Windows 环境和 Ubuntu 下工作)
    【解决方案3】:

    您需要将{ waitUntil: 'networkidle0' }page.goto 一起使用

    这告诉 puppeteer 等待网络空闲 (500 毫秒内没有请求)

    const puppeteer = require('puppeteer');
    
    (async () => {
        const browser = await puppeteer.launch({
            headless: false,
            defaultViewport: null,
            args: ['--start-maximized']
        })
    
        const page = await browser.newPage()
    
        // load the nike.com page and wait for it to fully load (inc A/B scripts)
        await page.goto('https://www.nike.com/', { waitUntil: 'networkidle0' })
    
        // select whichever element appears first
        var el = await page.waitForSelector('[data-path="join or login"], [data-path="sign in"]', { timeout: 1000 })
    
        // execute click
        await page.click(el._remoteObject.description)
    })()
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-04-01
      • 2019-05-01
      • 1970-01-01
      • 2022-01-04
      • 2019-11-16
      • 2020-05-24
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多