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

Vba*_*kke 9 node.js puppeteer

我正在使用'puppeteer'为NodeJS测试一个特定的网站.它似乎在大多数情况下工作正常,但有些地方报道:

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

以下代码选择了两种情况下都不在屏幕上的链接.

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

有什么不同?两个链接都在屏幕外.

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

示例代码

const puppeteer = require('puppeteer');

const initialPage = 'https://statsregnskapet.dfo.no/departementer';
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

})();
Run Code Online (Sandbox Code Playgroud)

我试图模仿真实用户点击网站的行为,这就是我使用的原因.click(),而不是.goto()因为a标签有onclick事件.

Bor*_*ris 13

如果你有类似的东西

const button = await page.$(selector);
await button.click();
Run Code Online (Sandbox Code Playgroud)

尝试改变

await button.click();
Run Code Online (Sandbox Code Playgroud)

await button.evaluate(b => b.click());
Run Code Online (Sandbox Code Playgroud)

不同的是,button.click()使用木偶的点击ElementHandle.click()

  1. 滚动页面直到元素可见
  2. 获取元素的屏幕xy坐标(是在此问题的标题中产生错误的步骤)
  3. 虚拟鼠标移动到这些坐标并将鼠标设置为“向下”然后回到“向上”
  4. 这大概会触发鼠标点击事件下的元素

button.evaluate(b => b.click())通过在浏览器中HTMLElement.click()的给定元素上运行 JavaScript方法来“点击”元素,这会触发一个事件。它不会滚动页面或移动鼠标,即使元素在屏幕外也能工作。click

  • 好的,这也解决了我的问题。这是什么黑魔法? (2认同)
  • @Billy [`Page.click()`](https://pptr.dev/#?product=Puppeteer&show=api-elementhandleclickoptions) 单击的方式与单击 [使用 JavaScript](https://developer.mozilla. org/en-US/docs/Web/API/HTMLElement/click)(这是我建议的替代方案)。它会在单击元素之前将元素滚动到视图中,但[显然这可能会失败](https://github.com/puppeteer/puppeteer/issues/6462#issuecomment-717163409),直到 18 天前我也看到了这一点。 click()` 可能有竞争条件([在 7097 中修复](https://github.com/puppeteer/puppeteer/pull/7097)),也许是相关的。 (2认同)
  • `让 x = 等待页面。$(选择器); wait x.click()` 和 `await (await page.$(selector)).click()` 是完全相同的东西。我在评论中解释了 `.click()` 和 `.evaluate(e => e.click())` 之间的区别 (2认同)

chr*_*ine 10

对于仍然遇到麻烦的人,这对我有用:

await page.evaluate(()=>document.querySelector('#sign-in-btn').click())

基本上只是以不同的方式获取元素,然后单击它。

我必须这样做的原因是因为我试图单击位于应用程序其余部分之外的通知窗口中的按钮(Chrome 似乎认为它是不可见的,即使它不是)。


Gra*_*ler 9

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

您需要将其更改为:

'defaultViewport' : { 'width' : width, 'height' : height }
Run Code Online (Sandbox Code Playgroud)

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

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

await page.setViewport( { 'width' : width, 'height' : height } );
Run Code Online (Sandbox Code Playgroud)

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

await page.setUserAgent( 'UA-TEST' );
Run Code Online (Sandbox Code Playgroud)

此外,你忘了添加分号后handle = handles[12].

你应该改为:

handle = handles[12];
Run Code Online (Sandbox Code Playgroud)

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

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

await page.waitForNavigation();
Run Code Online (Sandbox Code Playgroud)

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

page.setDefaultNavigationTimeout( 90000 );
Run Code Online (Sandbox Code Playgroud)

再一次,你忘了添加分号后handle = handles[12],所以需要将其更改为:

handle = handles[12];
Run Code Online (Sandbox Code Playgroud)

请务必注意,您正在使用错误的选择器来处理您单击的第二个链接.

您的原始选择器试图选择仅对xs 超小屏幕(手机)可见的元素.

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

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

div[id$="-KcazEUq"] article .dfo-widget-sm a
Run Code Online (Sandbox Code Playgroud)

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

await page.waitForNavigation();
Run Code Online (Sandbox Code Playgroud)

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

await browser.close();
Run Code Online (Sandbox Code Playgroud)

注意:您可能还想查看处理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();
})();
Run Code Online (Sandbox Code Playgroud)

  • 谢谢!mising.dfo-widget-sm引起了恐慌!还要感谢您提供的其他提示.这是一个缩小的测试脚本,我显然没有给予足够的重视.(但是,宽度和高度初始化遵循ECMA 2015表示法,并且应该是正确的.:-) (2认同)

小智 5

我知道我参加聚会迟到了,但我发现了一个让我非常悲伤的边缘案例,还有这个帖子,所以我想我会发布我的发现。

罪魁祸首:CSS

scroll-behavior: smooth
Run Code Online (Sandbox Code Playgroud)

如果你有这个,你会过得很糟糕。

解决方案:

await page.addStyleTag({ content: "{scroll-behavior: auto !important;}" });
Run Code Online (Sandbox Code Playgroud)

希望这对你们中的一些人有所帮助。

  • 我认为您缺少选择器?例如 `* {滚动行为:自动!重要;}` (2认同)