Practical Programming
marionettes représentant puppeteer pour le scraping de site

Web scraping : tutoriel basique en Node.js avec code d’exemple complet

Cet article est le troisième de notre série Web scraping : le guide complet avec tutoriels. Il est fortement recommandé de lire les deux précédents articles avant celui-ci : Data scraping et Web scraping : la vraie définition et Web scraping : quels langages et technologies choisir

Bienvenue dans ce tutoriel basique de web scraping en Node.js. Pour voir le même tutoriel en Python ou en PHP et constater les différences entre chaque langage, rendez-vous sur l’article correspondant : 

Pour commencer simple mais avec un cas d’usage concret, nous allons créer un programme qui va extraire les articles de la page d’accueil de LeMonde.fr.

Peu importe la technologie que vous allez choisir pour coder, vous devez forcément réaliser un travail préliminaire pour conceptualiser votre programme de web scraping et établir son mode de fonctionnement. Je vais vous montrer ma méthodologie. 

Travail préliminaire

Quand on veut se lancer dans l’écriture d’un programme de web scraping, la première chose à faire est de se rendre sur le site ciblé, ici LeMonde.fr. 

Page d'accueil lemonde.fr pour illustrer un exemple de web scraping

La page d’accueil de LeMonde.fr le jour de la rédaction de ce guide

Dans notre cas, l’objectif de notre programme est simple : extraire tous les articles de la page d’accueil

Nous allons donc observer le code HTML de la page pour en comprendre la structure et repérer un pattern qui nous permettra de cibler tous les articles de la page d’accueil en faisant le moins d’efforts possibles. En web scraping, les développeurs utilisent la plupart du temps les sélecteurs CSS et/ou les sélecteurs XPath pour sélectionner un ou plusieurs éléments sur la page et en extraire le contenu.

Exemples de sélecteurs CSS pour le web scraping

Il existe de nombreux sélecteurs CSS et chaque élément HTML d’une page peuvent être accédés par de nombreux sélecteurs différents. Ce qui est important est de choisir le bon sélecteur pour sélectionner les éléments que vous avez besoin d’extraire : le sélecteur ne devra pas rater des éléments, ni inclure des éléments dont vous n’avez pas besoin. 

La liste ci-dessous est un extrait traduit de la liste de w3schools qui proposent également un testeur de sélecteur pour vous aider à comprendre leur fonctionnement.

SélecteurExempleDescription de l’exemple
.classe.produitSélectionne tous les éléments avec class=”produit”
.classe1.classe2.nom1.nom2Sélectionne tous les éléments qui contiennent nom1 et nom2 dans leur attribut classe
.classe1 .classe2.nom1 .nom2Sélectionne tous les éléments de classe nom2 contenus dans des éléments de classe nom1
#id#introSélectionne l’élément avec id=”intro”
élémentpSélectionne tous les éléments <p>
élément.classep.descriptionSélectionne tous les éléments <p> avec class=”description”
élément élémentdiv p Sélectionne tous les éléments <p> contenus dans des éléments <div>
élément > élémentdiv > pSélectionne tous les éléments <p> dont le parent est un élément <div>
[attribut=valeur][target=_blank]Sélectionne tous les éléments avec target=”_blank”
:not(sélecteur):not(p)Sélectionne tous éléments qui ne sont pas des éléments <p>

Analysons maintenant la structure du code de la page d’accueil du site ciblé à l’aide des outils de développement de votre navigateur pour trouver le bon sélecteur qui va nous permettre de sélectionner tous les articles de la page et aucun autre élément. 

Analyse de la structure HTML pour le web scraping

Une bonne façon de trouver rapidement un sélecteur avec notre navigateur est de faire un clic droit sur un des éléments ciblés (dans notre cas, un article) et de cliquer sur inspecter. Les outils de développement nous montrent alors l’élément HTML et on peut dès lors chercher des patterns ou répétitions dans le code qui nous permettent de trouver un sélecteur. Par exemple : les éléments que l’on cible sont tous de la même classe, ont le même attribut ou le même parent etc. 

Sur le site du Monde, on peut voir par exemple que chaque article semble être contenu dans un élément div de classe article comme le montre la capture ci-dessous : 

Essayons donc de sélectionner tous les éléments div de classe article avec le sélecteur CSS  div.article en le tapant dans la barre de recherche des outils de développement : 

Pour tester un sélecteur CSS ou XPath sur le code d’une page dans Google Chrome, il faut ouvrir les outils de développement avec F12 puis faire apparaître la barre de recherche (entourée en rouge sur la capture ci-dessus) avec Ctrl+F

Le navigateur nous montre ensuite le nombre d’éléments sélectionnés (ici 81) et nous pouvons appuyer successivement sur Entrée pour voir défiler tous les éléments sélectionnés. 

Le sélecteur div.article semble bien nous sélectionner tous les articles de la page d’accueil. En réalité, le sélecteur n’est pas le bon pour notre programme car un peu plus loin sur la page, plusieurs articles sont en réalité contenus dans un élément div.article comme le montre la capture ci-dessous : 

Nous devons donc affiner notre sélecteur CSS pour nous permettre d’obtenir également ces articles à l’intérieur de ces éléments. Sinon, notre scraping produira des erreurs car il considérera cet élément comme un seul et même article alors qu’il en contient en réalité 3. 

Chaque article de presse sur Le Monde étant une page distincte, on peut logiquement considérer que chaque article sur la page d’accueil sera accessible par un lien. Essayons donc d’accéder à chaque élément <a> contenu dans un élément <div class=”article”> avec le sélecteur div.article > a. 

Ça fonctionne ! Nous avons maintenant 151 résultats : il y a bien 151 articles sur la page d’accueil du site

Parfois, dans certains cas complexes, nous ne sommes jamais sûrs du sélecteur que nous avons choisi. Une petite astuce que j’utilise consiste à tester directement dans la console du navigateur notre sélecteur avec un petit script JavaScript. Ici, je vais tester notre sélecteur avec le code suivant :

const selector = 'div.article > a';
const list = document.querySelectorAll(selector);

for (let i = 0; i < list.length; ++i) {
  console.log(list[i].getAttribute('href'));
}

Ce code nous imprime bien les URL de tous les articles de la page d’accueil, nous pouvons donc choisir ce sélecteur pour notre programme de scraping.

Nous savons donc maintenant comment va fonctionner notre programme de web scraping : 

  • Fetching : Il va se connecter à la page d’accueil du site et en télécharger le code HTML
  • Parsing : Il va parser le code HTML à l’aide du sélecteur CSS div.article > a pour extraire les liens vers chaque article
  • Stockage : Il va stocker les titres et les URL des articles dans un fichier CSV

A nos IDEs ! 

Exemple de code web scraping en Node.js avec Puppeteer

Pour ce tutoriel, vous aurez besoin de Node (voir nos tutoriels Node.js pour les guides d’installation) et des librairies Puppeteer et Objects-to-CSV à installer dans votre dossier de projet avec : 

npm i puppeteer
npm i objects-to-csv

Lorsque vous installez Puppeteer pour la première fois, une version de Chromium compatible avec l’API de la librairie est téléchargée automatiquement. 

Créons dans notre dossier de projet un fichier index.js et invoquons les deux librairies fraîchement installées : 

const puppeteer = require('puppeteer')
const ObjectsToCsv = require('objects-to-csv')

Une bonne pratique avec Puppeteer est de contenir tout notre code dans un bloc try/catch pour prendre en charge les exceptions. Par exemple, si l’on demande à notre programme de sélectionner un élément qui n’existe pas, une exception sera levée et loggée dans notre console : 

try {
    (async () => { 
        // CODE DU SCRAPING
 
    })()
} catch (err) {
    // LOG DES EXCEPTIONS DANS LA CONSOLE
        console.error(err)
}


Remarquez que notre code sera inclus dans une IIAFE : une fonction asynchrone immédiatement invoquée. Il y a bien sûr de nombreuses façons possibles de faire, cette méthode est simplement ma préférée pour mettre en place très rapidement un petit scraper. 

Passons maintenant au scraping pur en commençant par la phase de fetching

    const browser = await puppeteer.launch({headless: false});
    const page = await browser.newPage();
    await page.setViewport({ width: 1280, height: 800 });
    await page.goto('https://www.lemonde.fr/');

La première ligne lance le navigateur avec le paramètre headless désactivé : le navigateur sera donc bien lancé dans une fenêtre, ce qui vous permettra de voir l’activité du bot en temps réel sous vos yeux. Pour gagner en performances et lancer le navigateur en mode headless, activez le paramètre headless ou retirez la propriété car il est activé par défaut. 

Les lignes suivantes sont assez explicites : on ouvre une nouvelle page, on définit la taille du viewport et enfin on navigue vers le site ciblé. 

Lançons notre programme avec la commande node index.js et constatons : 

Chromium s’est bien lancé et a navigué vers LeMonde.fr ! 

Maintenant que nous avons accès au code HTML de la page, nous pouvons parser le code à l’aide de notre sélecteur CSS et extraire les données voulues que l’on va pour l’instant logger dans la console. 

Voilà le code de notre étape parsing

const r = [];
const results = await page.$$('div.article > a');
for(const result of results){
   const url = await page.evaluate(el => el.getAttribute('href'), result);
   const titre = await page.evaluate(el => el.getAttribute('href').innerText.trim(), result);
   r.push([titre,url]);
}
console.log(r);
  1. On déclare un tableau vide qui contiendra nos résultats
  2. On lance un document.querySelectorAll sur la page à l’aide de page.$$ 
  3. On itère sur les résultats du sélecteur et on extrait l’url et le titre. Notez l’utilisation de .trim() pour nettoyer le titre d’espaces non voulus.
  4. On pousse l’url et le titre dans notre tableau vide
  5. Après la fin de la boucle, on log les résultats dans la console

Relançons notre script et constatons les résultats de notre web scraping : 

Terminons maintenant notre programme avec la dernière étape : le stockage dans un fichier CSV

Avec Objects to CSV, cela prend deux lignes : 

const csv = new ObjectsToCsv(r)
await csv.toDisk(‘resultat.csv', { append: true })

Consultons les résultats : 

Malheureusement, les résultats ne sont pas bons ! Notre sélecteur de titre d’article doit encore être affiné car les éléments <a> sélectionnés contiennent des éléments non voulus tels que des mentions “articles réservé aux abonnés”. Changeons la ligne correspondante dans le code pour affiner notre sélecteur et seulement sélectionner les éléments de classe article__title dans chaque élément <a> : 

const titre = await page.evaluate(el => el.querySelector('.article__title').innerText.trim(), result);

Relançons notre script de web scraping et consultons le CSV généré à nouveau : 

Les résultats sont parfaits ! 

On pourrait encore améliorer notre programme en par exemple : 

  • Ajoutant une nouvelle colonne “réservé aux abonnés : oui ou non” selon la présence d’un élément “article réservé aux abonnés” dans les éléments <a> sélectionnés
  • Ouvrant chaque URL extraite et collectant des informations sur chaque article : contenu, auteur, date…
  • Analysant les titres des articles et en les regroupant par catégorie ou tags etc. 

Tous les codes d’exemple complets de web scraping en Node.js, Python et PHP sont en téléchargement sur GitHub.

Pour constater les différences entre chaque langage au niveau du web scraping, je vous conseille de consulter les autres tutoriels

Sinon, vous pouvez passer directement au tutoriel avancé de web scraping de web app avec Puppeteer (Node.js)

Lucas Roquilly

Lucas Roquilly

Commenter

Retrouvez nous

N'hésitez pas à nous suivre sur les différents réseaux sociaux !

Most popular

Most discussed

Share This