web-dev-qa-db-ja.com

NodeJSのPuppeteerが「エラー:Node is not visible or not an HTMLElement」と報告する

NodeJSで「puppeteer」を使用して特定のWebサイトをテストしています。ほとんどの場合は問題なく動作するようですが、レポートする場所は次のとおりです。

エラー:Nodeが表示されていないか、HTMLElementではありません

次のコードは、どちらの場合も画面外にあるリンクを選択します。

最初のリンクは正常に機能しますが、2番目のリンクは失敗します。

違いはなんですか?両方のリンクが画面から外れています。

助けてくれてありがとう、乾杯、:)

コード例

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

})();

aタグにはonclickイベントがあるため、.click()ではなく.goto()を使用するのは、サイト内をクリックする実際のユーザーの動作をエミュレートしようとしています。

10
Vbakke

何よりもまず、あなたが渡すdefaultViewportオブジェクト puppeteer.launch() にはキーがなく、値だけがあります。

これを次のように変更する必要があります。

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

同じことは page.setViewport() に渡すオブジェクトにも当てはまります。

このコード行を次のように変更する必要があります。

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

3番目に、関数 page.setUserAgent()promise を返すため、必要なのは await です。 =この関数:

_await page.setUserAgent( 'UA-TEST' );
_

さらに、_handle = handles[12]_の後にセミコロンを追加するのを忘れていました。

これを次のように変更する必要があります。

_handle = handles[12];
_

さらに、最初のリンクをクリックした後、ナビゲーションが完了するのを待機していません( page.waitForNavigation() )。

最初のリンクをクリックした後、以下を追加する必要があります。

_await page.waitForNavigation();
_

2番目のページがナビゲーションでハングすることがあるので、デフォルトのナビゲーションタイムアウト( page.setDefaultNavigationTimeout() )を増やすと便利な場合があります。

_page.setDefaultNavigationTimeout( 90000 );
_

もう一度、_handle = handles[12]_の後にセミコロンを追加するのを忘れたため、これを次のように変更する必要があります。

_handle = handles[12];
_

クリックしている2番目のリンクに間違ったセレクターを使用していることに注意することが重要です。

元のセレクターは、xsextra small画面(携帯電話)にのみ表示される要素を選択しようとしました。

指定したビューポートに表示されるリンクの配列を収集する必要があります。

したがって、2番目のセレクターを次のように変更する必要があります。

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

2番目のリンクをクリックした後も、ナビゲーションが完了するまで待つ必要があります。

_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();
})();
_
10
Grant Miller

私のやり方

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()
   ]);
}
1
Ruslan Galimov