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

时间:2018-08-15 10:29:38

标签: node.js puppeteer

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

  

错误:节点不可见或不是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

})();

我试图模拟真实用户在网站上点击的行为,这就是为什么我使用.click()而不是.goto()的原因,因为a标签具有{{ 1}}个事件。

5 个答案:

答案 0 :(得分:9)

我遇到过这样的事情

const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto(url);

const button = await page.$(selector);
await button.click();

我变了

await button.click();

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

它奏效了。 ?

答案 1 :(得分:7)

首先,您传递给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();
  

注意:您可能还想研究handling unhandledRejection errors


这是最终解决方案:

'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();
})();

答案 2 :(得分:3)

对于仍然有问题的人,这对我有用:

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

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

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

答案 3 :(得分:2)

我知道我参加晚会很晚,但是我发现了一个极端的案例,这给了我很多悲伤,还有这个话题,所以我想发表我的发现。

罪魁祸首: 的CSS 滚动行为:流畅

如果您有这个,那将是一段糟糕的时光。

解决方案: 等待page.addStyleTag({content:‘* {scroll-behavior:auto!important;}’});

希望这对您有所帮助。

答案 4 :(得分:0)

我的方式

async function getVisibleHandle(selector, page) {

    const elements = await page.$$(selector);

    let hasVisibleElement = false,
        visibleElement = '';

    if (!elements.length) {
        return [hasVisibleElement, visibleElement];
    }

    let i = 0;
    for (let element of elements) {
        const isVisibleHandle = await page.evaluateHandle((e) => {
            const style = window.getComputedStyle(e);
            return (style && style.display !== 'none' &&
                style.visibility !== 'hidden' && style.opacity !== '0');
        }, element);
        var visible = await isVisibleHandle.jsonValue();
        const box = await element.boxModel();
        if (visible && box) {
            hasVisibleElement = true;
            visibleElement = elements[i];
            break;
        }
        i++;
    }

    return [hasVisibleElement, visibleElement];
}

用法

let selector = "a[href='https://example.com/']";

let visibleHandle = await getVisibleHandle(selector, page);

if (visibleHandle[1]) {

   await Promise.all([
     visibleHandle[1].click(),
     page.waitForNavigation()
   ]);
}