Web scraping avec Puppeteer

Magazine
Marque
GNU/Linux Magazine
Numéro
255
Mois de parution
janvier 2022
Spécialité(s)


Résumé

Dans un précédent article [1], je vous avais montré comment scraper le contenu de pages HTML, grâce à NodeJS et à quelques packages complémentaires. La technique que j’avais utilisée fonctionnait très bien avec des pages statiques, mais elle se prêtait mal au scraping de pages générées dynamiquement. Pour régler ce problème, je vais vous présenter une alternative, reposant sur le projet Puppeteer, un package NPM très polyvalent.


Body

Dans le hors-série GLMF 114 [1], je vous avais expliqué les bases du web scraping, et nous nous étions amusés à gratter le contenu d’une page de newsletter. Comme je l’indiquais dans l’introduction, la technique que j’avais présentée est parfaite avec des pages statiques (ou générées intégralement côté serveur), mais elle est inapplicable dans le cas de pages web générées dynamiquement, comme on en trouve notamment dans les architectures de types SPA (Single Page Application). En effet, dans ce type d’architecture, certaines portions de page sont générées suite à des actions de l’utilisateur, comme le clic de certaines options (boutons, cases à cocher, etc.). Pour gérer cette difficulté, nous avons besoin d’un outil de scraping capable d’émuler un navigateur, ou mieux encore, capable de faire fonctionner un navigateur dans un mode « bac à sable », de manière à prendre le contrôle de la navigation via du code. C’est précisément ce que permet de faire le projet Puppeteer (en anglais marionettiste).

1. Le tour du propriétaire

1.1 Présentation de Puppeteer

Au cas où vous n’auriez pas lu mon précédent article [1], je rappelle brièvement que faire du web scraping, cela consiste à télécharger des pages web et à en extraire de l’information.

Concrètement, le projet Puppeteer est un package Node.js qui permet de piloter une instance de Chromium dont nous allons prendre le contrôle via du code JavaScript. Le concept est très puissant, car il nous permet de faire beaucoup de choses, telles que :

  • automatiser de nombreuses tâches, dont la soumission de formulaires et la surveillance de données en ligne ;
  • parcourir les pages de tout type d’application web, soit en vue de les tester, soit en vue d’en extraire du contenu (donc de faire du web scraping, on y revient). On va le voir, Puppeteer s’adapte particulièrement bien au parcours d’application de type SPA (Single Page Application) ;
  • générer du contenu prérendu, dans le cadre d’une architecture de type SSR (Server-Side Rendering) ;
  • générer des captures d’écran et des PDF à partir de pages web ;
  • capturer une trace chronologique d’un site pour aider à diagnostiquer des problèmes de performance ;
  • tester des extensions Chrome ;
  • etc.

On notera que le projet est développé et maintenu par Google.

Comme Puppeteer utilise Chromium sous le capot, il couvre l’essentiel des besoins en matière de tests d’interface utilisateur. Mais attention, Chromium c’est la partie open source de Chrome, donc Puppeteer n’a pas accès aux codecs propriétaires (pour le son et la vidéo). Puppeteer s’appuie sur le protocole DevTool de Chrome (et Chromium) pour accéder aux entrailles du navigateur et prendre le contrôle de la navigation. Mais je vous rassure, c’est transparent, car Puppeteer simplifie beaucoup de choses.

1.2 Installation de Puppeteer

L’installation se fait très simplement sous Node.js :

$ npm install puppeteer

On notera qu’il est possible d’installer puppeteer-core, une alternative à Puppeteer, développée aussi par Google :

  • l’installation de Puppeteer a pour effet de télécharger une version de Chromium, pilotée par des composants de puppeteer-core (qui fait partie des dépendances du package). Puppeteer implémente des variables d’environnement PUPPETEER_* qui sont très pratiques pour modifier son comportement ;
  • puppeteer-core est une bibliothèque qui aide à piloter tout ce qui supporte le protocole DevTools. puppeteer-core ne télécharge pas Chromium une fois installé. En tant que bibliothèque bas niveau, puppeteer-core est entièrement piloté par son API et ignore toutes les variables d’environnement PUPPETEER_*.

En résumé, Puppeteer est livré « clé en main », prêt à répondre à vos différents besoins, tandis que puppeteer-core est plus complexe à mettre en œuvre, et sera réservé à des usages très spécifiques (si vous voulez l’utiliser directement). On va se focaliser sur Puppeteer dans la suite de l’article.

1.3 Premiers tests

Dans l’article que j’avais publié dans le hors-série n° 114 [1], je vous avais montré comment scraper la page d’une newsletter pour en extraire quelques paragraphes de texte et une image. Nous allons voir dans un instant comment faire la même chose avec Puppeteer.

Mais tout d’abord, voici le squelette de base d’un script Puppeteer :

const puppeteer = require("puppeteer")
const url = "https://la_page_a_scrapper";
 
;(async () => {
    // const browser = await puppeteer.launch({ headless: false })
    const browser = await puppeteer.launch()
    const page = await browser.newPage()
    await page.goto(url);
 
    const source = await page.content();
    console.log(source);
})();

Le script ci-dessus instancie Chromium en mode headless, ce qui signifie que vous ne verrez pas la fenêtre du navigateur apparaître lors de l’exécution du script, même si elle est bien active dans les coulisses. Si vous voulez voir le navigateur apparaître, il vous suffit de dé-commenter la première ligne utilisant la commande puppeteer.launch, et de commenter la suivante :

...
    const browser = await puppeteer.launch({ headless: false })
    // const browser = await puppeteer.launch()
...

Dans quel cas pourrait-on avoir besoin de faire apparaître le navigateur ? Cela peut être utile de tester votre script et de vous assurer visuellement que le navigateur suit bien la navigation que vous aviez prévue. Cela peut être utile également pour prendre la main sur certaines étapes de la navigation, si vous souhaitez interagir vous-même avec certains formulaires. Puppeteer vous permettra ainsi d’éviter certaines étapes répétitives et fastidieuses de la navigation, pour vous rendre plus rapidement sur la partie que vous souhaitez tester manuellement.

Nous avons vu un exemple de script très simple, dans lequel on se contente de récupérer le contenu HTML d’une page, et de l’envoyer dans la console. C’était intéressant pour débuter, mais voyons plutôt comment générer une copie d’écran et un fichier PDF à partir d’une même page :

const puppeteer = require('puppeteer');
 
;(async () => {
    const browser = await puppeteer.launch();
    const page = await browser.newPage();
    const url = "https://freecontent.manning.com/on-this-day-01-21/";
    await page.goto(url, {
        waitUntil: 'domcontentloaded',
    });
    await page.screenshot({ path: 'output/example.png' });
    await page.pdf({ path: 'output/hn.pdf', format: 'a4' });
 
    await browser.close();
})();

Dans l’exemple ci-dessus, j’ai pris pour cible la newsletter de l’éditeur Manning qui, dans sa version du 21 janvier, présentait la machine analytique de Babbage. J’ai utilisé les fonctions page.screenshot et page.pdf pour générer une copie d’écran au format PNG et un fichier PDF. Tous deux sont générés dans un sous-répertoire output que vous prendrez soin de créer avant d’exécuter le script. Vous verrez après exécution que les sorties obtenues sont de très bonne qualité, et pour cause, c’est Chromium qui les a produites.

Vous noterez que dans l’instruction page.goto, j’ai utilisé l’option waitUntil, avec le paramètre domcontentloaded. Je reviendrai plus en détail, ultérieurement, sur les paramètres acceptés par page.goto.

Dans le hors-série n° 114 [1], je grattais le contenu d’une page de newsletter en récupérant plusieurs paragraphes de texte et le lien vers une image. Pour rappel, le contenu que je souhaitais récupérer se présentait tel que sur la figure 1.

babbage engine-s

Fig. 1 : Contenu à récupérer.

Pour récupérer les éléments du DOM (Document Object Model) qui m’intéressaient, j’avais utilisé un petit package très pratique qui s’appelle jsdom. Je l’avais sélectionné, car il était proche dans son fonctionnement des API querySelector et querySelectorAll, que j’utilise généralement pour travailler avec le DOM du navigateur. Avec Puppeteer, nous n’avons pas besoin de package complémentaire (comme jsdom), car l’objet document est présent nativement, sachant que nous travaillons avec une instance de navigateur.

Pour utiliser les API querySelector et querySelectorAll, on doit passer par la fonction page.evaluate et par sa fonction de callback :

    const result = await page.evaluate(() => {
         let sections = document.querySelectorAll('.elementor-widget-container');
         ...
    })

À l’intérieur de la fonction de callback, notre code s’exécute dans le contexte du navigateur, ce qui nous permet d’utiliser l’objet document et ses nombreuses propriétés et méthodes. On peut dès lors manipuler le DOM, comme on le ferait dans un navigateur classique, ce qui nous permet d’extraire les nœuds qui nous intéressent, comme dans l’exemple suivant :

    const result = await page.evaluate(() => {
         let sections = document.querySelectorAll('.elementor-widget-container');
         let title = sections[3].children[0].innerHTML;
         let evtdate = sections[4].children[0].innerHTML;
         let story = sections[6].children[0].innerText;
         let imgsrc = sections[7].children[0].querySelector('img').src;
         return {title, evtdate, story, img: imgsrc};
    })
    console.log(result)

Je dois souligner qu’à l’intérieur de la fonction de rappel, nous ne pouvons pas envoyer d’informations vers la console, car elles seraient transmises à la console du navigateur, et pas dans le terminal de Node.js. Si vous avez besoin de conserver des traces d’exécution, je vous invite à passer par un objet déclaré en amont de la fonction page.evaluate, que vous alimenterez selon vos besoins, pour pouvoir l’exploiter ultérieurement.

Voici le script complet me permettant de scraper le contenu de la newsletter :

const puppeteer = require("puppeteer")
const url = "https://freecontent.manning.com/on-this-day-01-21/";
 
;(async () => {
    const browser = await puppeteer.launch()
    const page = await browser.newPage()
    await page.goto(url);
 
    const result = await page.evaluate(() => {
         let sections = document.querySelectorAll('.elementor-widget-container');
         let title = sections[3].children[0].innerHTML;
         let evtdate = sections[4].children[0].innerHTML;
         let story = sections[6].children[0].innerText;
         let imgsrc = sections[7].children[0].querySelector('img').src;
         return {title, evtdate, story, img: imgsrc};
    })
    console.log(result);
    await browser.close();
})();

En sortie, on obtient les données suivantes (j’ai tronqué le contenu de la propriété story, car il était un peu long) :

{
  title: "Babbage's Analytics Engine Operates For The First Time",
  evtdate: 'January 21st 1888',
  story: 'Today in 1888, a working versio...',
  img: 'https://freecontent.manning.com/wp-content/uploads/AnalyticalEngine.jpg'
}

1.4 Approfondissement

Dans les exemples que je vous ai présentés, nous avons utilisé abondamment les méthodes async et await. Il aurait été possible d’écrire la même chose avec des Promises, ce qui donnerait ce genre de code :

;(() => {
   puppeteer.launch().then((browser) => {
      browser.newPage().then((page) => {
         page.goto("https://website.com").then(() => {
           //...
         })
      })
   })
})()

Dans des cas simples comme ceux que nous venons de voir, cela pourrait aller, mais dans des cas nécessitant une navigation plus complexe, le code deviendrait vite confus et difficile à maintenir. C’est pourquoi la plupart des exemples relatifs à Puppeteer que vous trouverez sur le Web utilisent massivement async et await.

Nous avons vu que Puppeteer s’exécute en mode headless par défaut, et que l’on peut facilement désactiver ce mode pour faire apparaître le navigateur. Dans ce cas, on peut jouer sur certains paramètres pour redéfinir la taille de la fenêtre. Pratique quand on souhaite prendre la main sur certaines étapes de la navigation :

const browser = await puppeteer.launch({
    headless: false,
    defaultViewport: {
        width: 1100,
        height: 1000
    }
})

On notera qu’il est possible de redimensionner le viewport en cours de route, avec le code suivant :

await page.setViewport({
  width: 1920,
  height: 1080,
  deviceScaleFactor: 1,
})

On peut transmettre d’autres paramètres à la fonction puppeteer.launch. On peut par exemple demander à ce que le navigateur ouvre automatique la fenêtre relative aux outils de développement (l’équivalent d’un <F12> automatique). Cela se fait via le paramètre devtools. On peut aussi activer le paramètre dumpio, ce qui nous permet d’utiliser le mode débogage. On peut aussi forcer le chemin d’accès au navigateur avec le paramètre executablePath, et lui transmettre des paramètres optionnels avec le paramètre args. Je vous fais un prix de gros avec l’exemple (fictif) suivant :

const browser = await puppeteer.launch({
    "headless": false,
    "devtools": true,
    "dumpio": true,
    "executablePath": '/usr/bin/chromium-browser',
    "args": [
        '--disable-setuid-sandbox',
        '--no-sandbox',
        '--disable-gpu',
    ]
})

Autre astuce très pratique : on peut ralentir la navigation avec la propriété slowMo (valeur exprimée en millisecondes). Cela évite de déclencher des timeouts intempestifs, notamment quand certaines requêtes Fetch sont trop lentes. Cela m’a rendu service dans un cas que je vous présenterai dans la suite de l’article :

const browser = await puppeteer.launch({
    headless: false,
    slowMo: 500,
})    

Avant de poursuivre, sachez qu’une documentation très détaillée des API fournies par Puppeteer existe [2]. Elle est un peu ardue à lire, mais elle contient pas mal d’exemples intéressants.

1.5 De l’art de se simplifier la vie

Dans les outils de développement de Chrome, il y a une fonction un peu cachée qui s’appelle Recorder. Elle est dédiée à la génération de scripts Puppeteer. Pour activer cette fonctionnalité, il faut cocher la case correspondante dans les options du navigateur (figure 2).

puppeteer recorder settings 01-s

Fig. 2 : Configuration de la fonctionnalité de génération de scripts Puppeteer depuis Chrome.

Avertissement : Je crois me souvenir que l’option Recordings n’apparaît dans les menus qu’après redémarrage du navigateur. Donc si vous ne la retrouvez pas dans ce qui suit, ne cherchez pas plus loin, relancez le navigateur, cela devrait régler le problème.

Rendez-vous maintenant sur votre page cible. Placez-vous dans les outils de développements (touche <F12>), allez dans l’onglet Sources, et pour finir, dans l’onglet Recordings (figure 3).

puppeteer recorder settings 02-s

Fig. 3 : Accès à l’onglet Recordings.

Dans la fenêtre Recordings, vous allez pouvoir créer de nouveaux enregistrements (figure 4). Cliquez d’abord sur l’option Add recording, puis sur le bouton Record un peu plus bas à droite (ou faites <Ctrl> + <E>).

puppeteer recorder settings 03-s

Fig. 4 : Création d’un nouvel enregistrement.

Le bouton Record est maintenant de couleur rouge, l’enregistrement est en cours...

Naviguez sur la page de votre choix, cliquez sur quelques boutons et/ou saisissez quelques valeurs dans un formulaire si vous en avez un, puis stoppez l’enregistrement en recliquant sur le bouton Record, qui va repasser en noir (ou faites <Ctrl> + <E>).

Votre code source Puppeteer est généré, vous n’avez plus qu’à le copier-coller vers votre éditeur de code préféré, pour le réutiliser ultérieurement.

1.6 De l’art de bien naviguer

Nous avons vu pas mal de commandes Puppeteer, mais il nous manque quelques fonctions essentielles, à savoir celles qui permettent de simuler la navigation d’un utilisateur.

Je vous propose un tour d’horizon, en commençant par les fonctions les plus fréquemment utilisées.

Nous avions vu brièvement la fonction goto avec l’exemple suivant :

await page.goto(url, {
    waitUntil: 'domcontentloaded',
});

L’option waitUntil peut recevoir les paramètres suivants :

  • load : considère que la navigation est terminée quand l'événement load est déclenché ;
  • domcontentloaded : considère que la navigation est terminée quand l'événement DOMContentLoaded est déclenché ;
  • networkidle0 : considère que la navigation est terminée lorsqu’il n’y a pas plus de 0 connexion réseau pendant au moins 500 ms ;
  • networkidle2 : considère que la navigation est terminée lorsqu’il n’y a pas plus de 2 connexions réseau pendant au moins 500 ms.

On peut aussi transmettre un tableau contenant plusieurs des paramètres ci-dessus, comme ceci :

await page.goto("https://zzz", {
    waitUntil: ['load', 'domcontentloaded', "networkidle2"]
});

La fonction type permet d’injecter une valeur dans un champ de formulaire :

await page.type("input#name", "Greg")

Il est possible de définir un délai optionnel, pour simuler la vitesse de saisie d’un utilisateur humain. Le délai appliqué entre la saisie de chaque caractère est exprimé en millisecondes :

await page.type("input#name", "Greg", { delay: 100 })

La fonction click est sans ambiguïté :

await page.click("button#submit")

Cette fonction accepte elle aussi des paramètres optionnels :

  • button : peut être défini à left (valeur par défaut), right ou middle ;
  • clickCount : définit le nombre de fois qu’un élément doit être cliqué (la valeur par défaut est 1) ;
  • delay : définit le nombre de millisecondes entre chaque clic (la valeur par défaut est 0).

La fonction focus permet, comme son nom l’indique, de mettre le focus sur un élément du DOM, comme par exemple un champ de formulaire :

await page.focus("input#name")

Les fonctions goBack et goForward permettent respectivement de reculer et d’avancer dans l’historique de navigation :

await page.goBack()
await page.goForward()

La fonction hover permet de déclencher un événement de type mouseover sur un élément du DOM :

await page.hover("input#name")

Puppeteer met à disposition un jeu de fonctions d’attente, adaptées à différents contextes. Ces fonctions commencent toutes par waitFor :

  • waitForSelector ;
  • waitForNavigation ;
  • waitForFunction ;
  • waitForRequest ;
  • waitForResponse ;
  • waitForXPath.

Jusqu’ici, je me suis surtout servi des deux premières fonctions (waitForSelector et waitForNavigation).

On utilise waitForSelector pour isoler un élément du DOM sur lequel on souhaite par exemple déclencher un clic de souris :

const element = await frame.waitForSelector("aria/Effectuer une demande de rendez-vous");
await element.click();

Dans le cas de waitForNavigation, je me suis aperçu qu’en la plaçant après un clic, elle me permettait dans certains cas d’empêcher l’apparition de l’erreur suivante : « Execution context was destroyed, most likely because of a navigation ».

Mais ce n’est pas systématique, à apprécier au cas par cas :

await element.click();
await page.waitForNavigation();

Nous avions vu que la fonction goto accepte un certain nombre de paramètres complémentaires, via l’option waitUntil. Eh bien, les fonctions waitFor acceptent aussi cette même série de paramètres :

await page.waitForNavigation({waitUntil: "networkidle2"});

En cas de doute sur l’utilisation de l’option waitUntil, je vous encourage à fouiller dans la documentation officielle [2]. En effectuant une recherche dans cette documentation, vous trouverez facilement toutes les fonctions acceptant l’option waitUntil (et beaucoup d’autres choses encore).

On notera aussi que la fonction waitForSelector accepte un paramètre optionnel visible, ce qui est intéressant quand un élément du DOM est généré dynamiquement :

await page.waitForSelector('#example', {
  visible: true,
});

Sans surprise, la fonction reload permet de déclencher un rechargement de page :

await page.reload();

Nous avions déjà vu dans un exemple la fonction evaluate, qui permet de renvoyer une valeur extraite d’un élément du DOM :

const result = await page.evaluate(() => {
    return document.querySelector("input#name").value
})

... ou une série de valeurs :

const result = await page.evaluate(() => {
    let items = document.querySelectorAll("a")
    let titles = [];
    [].forEach.call(items, function (item) {
        titles.push(item.innerText);
    });
    return titles;
})
console.log(result);

La fonction evaluateHandle est une variante qui renvoie un nœud du DOM en sortie (plutôt que des valeurs) :

const result = await page.evaluateHandle(() => {
   return document.querySelector("#madiv").querySelectorAll("a");
})

La fonction setContent permet de forcer un contenu HTML spécifique, on peut l’utiliser ensuite pour produire une image PNG ou un document PDF, comme dans l’exemple suivant :

const html = "<h1>Salut le monde!</h1>"
await page.setContent(html)
await page.pdf({ path: "helloworld.pdf" })

On peut utiliser aussi cette technique pour forcer un contenu HTML avec des valeurs erronées, en vue de contrôler si la gestion d’erreurs que l’on a implémentée fonctionne correctement.

Nous avons vu que la fonction evaluate permet d’accéder à des éléments du DOM via l’objet document. Mais il y a des situations dans lesquelles on souhaite connaître la valeur de certains éléments, sans nécessairement passer par la fonction evaluate. On peut dès lors utiliser les fonctions page.$eval et page.$$eval.

La fonction page.$eval accepte deux paramètres ou plus. Le premier paramètre est un sélecteur (qui utilise en interne la méthode querySelector), le second une fonction de rappel (callback). S’il y a des paramètres complémentaires (trois ou plus), ils sont utilisés comme paramètres d’entrée de la fonction de rappel.

const checkboxStatus = await page.$eval('#defaultCheck1', input => { return input.checked })
console.log('Checkbox checked status:', checkboxStatus)

La fonction page.$$eval fonctionne sur le même principe que sa cousine, mais elle utilise querySelectorAll en interne. Elle permet donc d’extraire et de manipuler des séries de valeurs :

const radios = await page.$$eval('input[name="exampleRadios"]',
    inputs => { return inputs.map(input => input.value) })
console.log('Radio values:', radios)

À noter que j’ai emprunté les deux exemples précédents au site internet Tabnine [3], qui propose pas mal de snippets intéressants autour de Puppeteer, entre autres.

Dans cette rapide présentation, je me suis efforcé de parcourir les fonctions les plus courantes, celles que vous avez toutes les chances d’utiliser pour du web scraping. Ce n’était pas une présentation exhaustive, loin de là, car Puppeteer est un outil très puissant qui peut répondre à des problématiques variées. Aussi, je vous encourage vivement à parcourir la documentation officielle [2], si ce que je vous ai présenté ici ne répond pas parfaitement à votre besoin.

2. Mini-étude de cas

Pour illustrer certains des points que nous venons d’aborder, j’ai choisi pour cible un site internet fonctionnant selon une architecture de type SPA. C’est le site officiel du framework P5.js [4], framework qui, comme chacun sait, est le petit frère du projet Processing.

Sachant que la page Reference du site regroupe l’essentiel de la documentation de P5, et sachant que cette page met la plupart du temps 1 à 2 secondes pour s’afficher, je me suis dit que cette page constituait un bon terrain de jeu.

J’ai commencé par utiliser l’outil Recorder embarqué dans Chrome. Avec cet outil, j’ai simulé une navigation dans laquelle je clique sur l’option de menu correspondant à la page Reference, de manière à faire apparaître son contenu. Mon objectif est de récupérer le contenu HTML spécifique à cette page, contenu dont je rappelle qu’il est généré dynamiquement.

J’ai récupéré le code JavaScript généré par l’outil Recorder et j’ai tenté de le faire fonctionner dans mon environnement de développement. J’ai constaté que le script n’était pas en mesure de fonctionner en l’état, et j’ai été obligé de procéder à de légères modifications. Je me suis heurté à un problème épineux, mais intéressant : quoi que je fasse, l’affichage du contenu de la page Reference s’affichait toujours trop tard, et je ne parvenais jamais à récupérer le contenu de la page souhaitée. Après de nombreuses tentatives, je me suis aperçu que je pouvais régler le problème grâce au paramètre slowMo qui est déclaré dans la fonction puppeteer.launch :

    const browser = await puppeteer.launch({
        headless: false,
        defaultViewport: {
            width: 1100,
            height: 1000
        },
        slowMo: 500,
    })

En effet, c’est seulement en ralentissant la navigation que j’ai pu faire fonctionner correctement mon script de scraping. Je vous avoue que c’est la première fois que je suis confronté à ce problème, qui est peut-être très spécifique à ce site.

En tout cas, je vous donne ci-dessous le code source complet du script de scraping. Vous verrez que j’utilise deux manières différentes pour extraire le contenu de la page Reference :

const puppeteer = require('puppeteer');
 
;(async () => {
    const browser = await puppeteer.launch({
        headless: false,
        defaultViewport: {
            width: 1100,
            height: 1000
        },
        slowMo: 500,
    })
    const page = await browser.newPage();
 
    const urlToFetch = "https://p5js.org/";
    await page.goto(urlToFetch, {
        waitUntil: ['load', 'domcontentloaded', "networkidle2"]
    });
 
    {
        const targetPage = page;
        const frame = targetPage.mainFrame();
        const element = await frame.waitForSelector("aria/Reference");
        await element.click();
        // ajout d'une temporisation indispensable pour éviter l'erreur suivante :
        //   "Execution context was destroyed, most likely because of a navigation"
        await page.waitForSelector('#reference-page', {visible: true});
    }
 
    // extraction du contenu de la page Reference - méthode 1
    const innerText = await page.$eval(
        '#reference-page',
        (el) => el.innerText
    )
    console.log(innerText);
 
    // extraction du contenu de la page Reference - méthode 2
    const result = await page.evaluate(() => {
        return document.querySelector('#reference-page').innerText
    })
    console.log(result);
 
    await browser.close();
})();

Conclusion

Dans cet article, j’ai essayé de vous présenter un tour d’horizon des possibilités de Puppeteer dans le contexte du web scraping. L’outil est relativement simple à prendre en main, mais il est très puissant et peut vous accompagner dans de nombreux usages. Vous pouvez par exemple l’utiliser pour développer des tests. Si cet autre sujet vous intéresse, je vous invite à lire également l’article de Gabriel Zerbib publié dans GNU/Linux Magazine n°232 [5].

Références

[1] G. JARRIGE, « Web scraping avec Node.js », GNU/Linux Magazine n°114, mai 2021 :
https://connect.ed-diamond.com/GNU-Linux-Magazine/glmfhs-114/web-scraping-avec-node.js

[2] Documentation de l’API : https://github.com/puppeteer/puppeteer/blob/main/docs/api.md

[3] Tabnine : https://www.tabnine.com/code/javascript/functions/puppeteer

[4] P5.js : https://p5js.org/

[5] G. ZERBIB, « Automatiser les tests end-to-end en PHP », GNU/Linux Magazine n° 232, décembre 2019 :
https://connect.ed-diamond.com/GNU-Linux-Magazine/glmf-232/automatiser-les-tests-end-to-end-en-php



Article rédigé par

Par le(s) même(s) auteur(s)

Et si nous retrouvions l’agilité, la vraie ?

Magazine
Marque
GNU/Linux Magazine
HS n°
Numéro
115
Mois de parution
juillet 2021
Spécialité(s)
Résumé

Depuis quelques années, je croise de plus en plus de jeunes développeurs et développeuses, déboussolé-e-s, qui n’en peuvent plus, et veulent désespérément sortir du monde de l’IT. Pourtant, certaines de ces personnes sont particulièrement brillantes. Pourquoi sont-elles essorées, cramées, au bout de quelques années ? J’ai remarqué que toutes travaillent en mode agile. Alors… y aurait-il quelque chose de pourri au royaume de l’agilité ?

Les derniers articles Premiums

Les derniers articles Premium

Sécurisez vos applications web : comment Symfony vous protège des menaces courantes

Magazine
Marque
Contenu Premium
Spécialité(s)
Résumé

Les frameworks tels que Symfony ont bouleversé le développement web en apportant une structure solide et des outils performants. Malgré ces qualités, nous pouvons découvrir d’innombrables vulnérabilités. Cet article met le doigt sur les failles de sécurité les plus fréquentes qui affectent même les environnements les plus robustes. De l’injection de requêtes à distance à l’exécution de scripts malveillants, découvrez comment ces failles peuvent mettre en péril vos applications et, surtout, comment vous en prémunir.

Bash des temps modernes

Magazine
Marque
Contenu Premium
Spécialité(s)
Résumé

Les scripts Shell, et Bash spécifiquement, demeurent un standard, de facto, de notre industrie. Ils forment un composant primordial de toute distribution Linux, mais c’est aussi un outil de prédilection pour implémenter de nombreuses tâches d’automatisation, en particulier dans le « Cloud », par eux-mêmes ou conjointement à des solutions telles que Ansible. Pour toutes ces raisons et bien d’autres encore, savoir les concevoir de manière robuste et idempotente est crucial.

Présentation de Kafka Connect

Magazine
Marque
Contenu Premium
Spécialité(s)
Résumé

Un cluster Apache Kafka est déjà, à lui seul, une puissante infrastructure pour faire de l’event streaming… Et si nous pouvions, d’un coup de baguette magique, lui permettre de consommer des informations issues de systèmes de données plus traditionnels, tels que les bases de données ? C’est là qu’intervient Kafka Connect, un autre composant de l’écosystème du projet.

Le combo gagnant de la virtualisation : QEMU et KVM

Magazine
Marque
Contenu Premium
Spécialité(s)
Résumé

C’est un fait : la virtualisation est partout ! Que ce soit pour la flexibilité des systèmes ou bien leur sécurité, l’adoption de la virtualisation augmente dans toutes les organisations depuis des années. Dans cet article, nous allons nous focaliser sur deux technologies : QEMU et KVM. En combinant les deux, il est possible de créer des environnements de virtualisation très robustes.

Les listes de lecture

9 article(s) - ajoutée le 01/07/2020
Vous désirez apprendre le langage Python, mais ne savez pas trop par où commencer ? Cette liste de lecture vous permettra de faire vos premiers pas en découvrant l'écosystème de Python et en écrivant de petits scripts.
11 article(s) - ajoutée le 01/07/2020
La base de tout programme effectuant une tâche un tant soit peu complexe est un algorithme, une méthode permettant de manipuler des données pour obtenir un résultat attendu. Dans cette liste, vous pourrez découvrir quelques spécimens d'algorithmes.
10 article(s) - ajoutée le 01/07/2020
À quoi bon se targuer de posséder des pétaoctets de données si l'on est incapable d'analyser ces dernières ? Cette liste vous aidera à "faire parler" vos données.
Voir les 65 listes de lecture

Abonnez-vous maintenant

et profitez de tous les contenus en illimité

Je découvre les offres

Déjà abonné ? Connectez-vous