NodeJS 中的 Puppeteer 报告“错误:节点不可见或不是 HTMLElement”

新手上路,请多包涵

我正在使用 NodeJS 的“puppeteer”来测试特定的网站。在大多数情况下它似乎工作正常,但它报告的某些地方:

错误:节点不可见或不是 HTMLElement

下面的代码选择了一个在这两种情况下都在屏幕之外的链接。

第一个链接工作正常,而第二个链接失败。

有什么区别?两个链接都在屏幕外。

任何帮助表示赞赏,干杯,:)

示例代码

const puppeteer = require('puppeteer');

const initialPage = 'https://website.com/path';
const selectors = [
    'div[id$="-bVMpYP"] article a',
    'div[id$="-KcazEUq"] article a'
];

(async () => {
    let selector, handles, handle;
    const width=1024, height=1600;
    const browser = await puppeteer.launch({
        headless: false,
        defaultViewport: { width, height }
    });
    const page = await browser.newPage();
    await page.setViewport({ width, height});
    page.setUserAgent('UA-TEST');

    // Load first page
    let stat = await page.goto(initialPage, { waitUntil: 'domcontentloaded'});

    // Click on selector 1 - works ok
    selector = selectors[0];
    await page.waitForSelector(selector);
    handles = await page.$$(selector);
    handle = handles[12]
    console.log('Clicking on: ', await page.evaluate(el => el.href, handle));
    await handle.click();  // OK

    // Click that selector 2 - fails
    selector = selectors[1];
    await page.waitForSelector(selector);
    handles = await page.$$(selector);
    handle = handles[12]
    console.log('Clicking on: ', await page.evaluate(el => el.href, handle));
    await handle.click();  // Error: Node is either not visible or not an HTMLElement

})();

我试图模拟真实用户在网站周围点击的行为,这就是为什么我使用 .click() 而不是 .goto() ,因为 aonclick 事件。

原文由 Vbakke 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 2.2k
2 个回答

首先,传递给 puppeteer.launch()defaultViewport 对象没有键,只有值。

您需要将其更改为:

 'defaultViewport' : { 'width' : width, 'height' : height }

您传递给 page.setViewport() 的对象也是如此。

您需要将这行代码更改为:

 await page.setViewport( { 'width' : width, 'height' : height } );

三、函数 page.setUserAgent() 返回一个 promise ,所以需要 await 这个函数:

 await page.setUserAgent( 'UA-TEST' );

此外,您忘记在 handle = handles[12] 之后添加分号。

您应该将其更改为:

 handle = handles[12];

此外,单击第一个链接后,您无需等待导航完成 ( page.waitForNavigation() )。

单击第一个链接后,您应该添加:

 await page.waitForNavigation();

我注意到第二页有时会挂在导航上,因此您可能会发现增加默认导航超时很有用( page.setDefaultNavigationTimeout() ):

 page.setDefaultNavigationTimeout( 90000 );

再次,您忘记在 handle = handles[12] 之后添加分号,因此需要将其更改为:

 handle = handles[12];

请务必注意,您为单击的第二个链接使用了错误的选择器。

您最初的选择器试图选择仅对 xs 超小 屏幕(手机)可见的元素。

您需要收集一组对您指定的视口可见的链接。

因此,您需要将第二个选择器更改为:

 div[id$="-KcazEUq"] article .dfo-widget-sm a

您也应该在单击第二个链接后等待导航完成:

 await page.waitForNavigation();

最后,您可能还想在完成程序后关闭浏览器( browser.close() ):

 await browser.close();

注意: 您可能还想研究 处理 unhandledRejection 错误


这是最终的解决方案:

 'use strict';

const puppeteer = require( 'puppeteer' );

const initialPage = 'https://statsregnskapet.dfo.no/departementer';

const selectors = [
    'div[id$="-bVMpYP"] article a',
    'div[id$="-KcazEUq"] article .dfo-widget-sm a'
];

( async () =>
{
    let selector;
    let handles;
    let handle;

    const width = 1024;
    const height = 1600;

    const browser = await puppeteer.launch(
    {
        'defaultViewport' : { 'width' : width, 'height' : height }
    });

    const page = await browser.newPage();

    page.setDefaultNavigationTimeout( 90000 );

    await page.setViewport( { 'width' : width, 'height' : height } );

    await page.setUserAgent( 'UA-TEST' );

    // Load first page

    let stat = await page.goto( initialPage, { 'waitUntil' : 'domcontentloaded' } );

    // Click on selector 1 - works ok

    selector = selectors[0];
    await page.waitForSelector( selector );
    handles = await page.$$( selector );
    handle = handles[12];
    console.log( 'Clicking on: ', await page.evaluate( el => el.href, handle ) );
    await handle.click();  // OK

    await page.waitForNavigation();

    // Click that selector 2 - fails

    selector = selectors[1];
    await page.waitForSelector( selector );
    handles = await page.$$( selector );
    handle = handles[12];
    console.log( 'Clicking on: ', await page.evaluate( el => el.href, handle ) );
    await handle.click();

    await page.waitForNavigation();

    await browser.close();
})();

原文由 Grant Miller 发布,翻译遵循 CC BY-SA 4.0 许可协议

代替

await button.click();

做这个:

 await button.evaluate(b => b.click());

不同之处在于 button.evaluate(b => b.click()) 在浏览器上下文中 的给定元素上运行 JavaScript HTMLElement.click() 方法,这将触发 click 事件,即使它是隐藏的元素,屏幕外或被不同的元素覆盖,而 button.click() 使用 Puppeteer 的点击 ElementHandle.click()

  1. 滚动页面 直到元素在视图中
  2. 获取 元素的边界框(这一步 错误 发生的 地方)并找到该框中间的屏幕 xy 像素坐标
  3. 虚拟鼠标移动到这些坐标并将鼠标设置为“向下”然后返回“向上”,这会触发鼠标下方元素的点击事件

原文由 Boris Verkhovskiy 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题