web-dev-qa-db-fra.com

Marionnettiste dans NodeJS signale «Erreur: Node n'est pas visible ou n'est pas un HTMLElement»

J'utilise "marionnettiste" pour NodeJS pour tester un site Web spécifique. Cela semble bien fonctionner dans la plupart des cas, mais à certains endroits, il rapporte:

Erreur: Node n'est pas visible ou n'est pas un HTMLElement

Le code suivant sélectionne un lien qui, dans les deux cas, est hors de l'écran.

Le premier lien fonctionne correctement, tandis que le deuxième lien échoue.

Quelle est la différence? Les deux liens sont hors de l'écran.

Toute aide appréciée, Santé, :)

Exemple de code

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

})();

J'essaie d'imiter le comportement d'un utilisateur réel en cliquant sur le site, c'est pourquoi j'utilise .click(), et non .goto(), car les balises a ont onclick événements.

10
Vbakke

D'abord et avant tout, votre objet defaultViewport que vous passez à puppeteer.launch() n'a pas de clés, seulement des valeurs.

Vous devez changer cela en:

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

Il en va de même pour l'objet vers lequel vous passez page.setViewport() .

Vous devez modifier cette ligne de code pour:

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

Troisièmement, la fonction page.setUserAgent() renvoie un promise , vous devez donc await = cette fonction:

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

De plus, vous avez oublié d'ajouter un point-virgule après handle = handles[12].

Vous devez changer cela en:

handle = handles[12];

De plus, vous n'attendez pas la fin de la navigation ( page.waitForNavigation() ) après avoir cliqué sur le premier lien.

Après avoir cliqué sur le premier lien, vous devez ajouter:

await page.waitForNavigation();

J'ai remarqué que la deuxième page se bloque parfois sur la navigation, vous pourriez donc trouver utile d'augmenter le délai de navigation par défaut ( page.setDefaultNavigationTimeout() ):

page.setDefaultNavigationTimeout( 90000 );

Encore une fois, vous avez oublié d'ajouter un point-virgule après handle = handles[12], Il faut donc le changer en:

handle = handles[12];

Il est important de noter que vous utilisez le mauvais sélecteur pour votre deuxième lien sur lequel vous cliquez.

Votre sélecteur d'origine tentait de sélectionner des éléments qui n'étaient visibles que sur les écrans xs très petits (téléphones mobiles).

Vous devez rassembler un tableau de liens visibles dans la fenêtre que vous avez spécifiée.

Par conséquent, vous devez modifier le deuxième sélecteur pour:

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

Vous devez également attendre la fin de la navigation après avoir cliqué sur votre deuxième lien:

await page.waitForNavigation();

Enfin, vous pouvez également vouloir fermer le navigateur ( browser.close() ) une fois que vous avez terminé avec votre programme:

await browser.close();

Remarque: Vous pouvez également examiner gestion des erreurs unhandledRejection .


Voici la solution finale:

'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

Mon chemin

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];
}

Usage

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