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()
を使用するのは、サイト内をクリックする実際のユーザーの動作をエミュレートしようとしています。
何よりもまず、あなたが渡す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番目のリンクに間違ったセレクターを使用していることに注意することが重要です。
元のセレクターは、xs
extra 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();
})();
_
私のやり方
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()
]);
}