Retour au blog
Guides
Raluca Penciuc21 décembre 20229 minutes de lecture

Le scraping avec Cheerio : comment collecter facilement des données à partir de pages Web

Le scraping avec Cheerio : comment collecter facilement des données à partir de pages Web

Introduction à Cheerio

« Mais qu'est-ce que Cheerio ? », vous demandez-vous peut-être. Eh bien, pour dissiper une idée fausse courante, je commencerai par ce que Cheerio n'est pas : un navigateur.

La confusion vient peut-être du fait que Cheerio analyse des documents écrits dans un langage de balisage, puis propose une API pour vous aider à manipuler la structure de données obtenue. Mais contrairement à un navigateur, Cheerio ne rendra pas le document visuellement, ne chargera pas de fichiers CSS et n’exécutera pas de Javascript.

En gros, Cheerio reçoit une entrée HTML ou XML, analyse la chaîne de caractères et renvoie l’API. Cela le rend incroyablement rapide et facile à utiliser, d’où sa popularité auprès des développeurs Node.js.

Configuration de l'environnement

Voyons maintenant quelques exemples concrets de ce que Cheerio peut faire. Avant toute chose, vous devez vous assurer que votre environnement est correctement configuré.

Il va sans dire que Node.js doit être installé sur votre machine. Si ce n'est pas le cas, suivez simplement les instructions fournies sur leur site web officiel, en fonction de votre système d'exploitation.

Veillez à télécharger la version à support à long terme (LTS) et n'oubliez pas le gestionnaire de paquets Node.js (NPM). Vous pouvez exécuter ces commandes pour vérifier que l'installation s'est bien déroulée :

node -v
npm -v

Le résultat devrait ressembler à ceci :

Windows terminal showing Node.js and npm version commands and their outputs

Maintenant, concernant le débat sur les IDE : pour ce tutoriel, j'utiliserai Visual Studio Code, car il est assez flexible et facile à utiliser, mais vous êtes libre d'utiliser l'IDE de votre choix.

Créez simplement un dossier qui contiendra votre petit projet et ouvrez un terminal. Exécutez la commande suivante pour configurer un projet Node.js :

npm init -y

Cela créera une version par défaut du fichier package.json, qui peut être modifiée à tout moment.

Étape suivante : je vais installer TypeScript ainsi que les définitions de types pour Node.js :

npm install typescript @types/node -save-dev

J'ai choisi TypeScript dans ce tutoriel pour son typage statique optionnel des objets JavaScript, ce qui rend le code plus résistant aux erreurs de typage. 

C'est cet avantage qui a progressivement accru sa popularité au sein de la communauté JavaScript, selon une récente enquête CircleCI sur les langages de programmation les plus populaires.

Pour vérifier que la commande précédente a bien été installée, vous pouvez exécuter :

npx tsc --version

Je vais maintenant créer le fichier de configuration tsconfig.json à la racine du répertoire du projet, qui doit définir les options du compilateur. Si vous souhaitez mieux comprendre ce fichier et ses propriétés, la documentation officielle de TypeScript est là pour vous aider. 

Sinon, il vous suffit de copier-coller ce qui suit :

{
    "compilerOptions": {
        "module": "commonjs",
        "esModuleInterop": true,
        "target": "es6",
        "moduleResolution": "node",
        "sourceMap": true,
        "outDir": "dist"
    },
    "lib": ["es2015"]
}

C'est presque terminé ! Il vous reste maintenant à installer Cheerio (évidemment) :

npm install cheerio

Enfin, créez le répertoire src qui contiendra les fichiers de code. Et en parlant de fichier de code, créez et placez le fichier index.ts dans le répertoire src.

Comment fonctionne Cheerio

Parfait ! Vous pouvez maintenant vous lancer.

Pour l'instant, je vais illustrer certaines fonctionnalités de base de Cheerio à l'aide d'un document HTML statique. Copiez-collez simplement le contenu ci-dessous dans un nouveau fichier static.html au sein de votre projet :

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Page Name - Static HTML Example</title>
</head>
<body>
    <div class="page-heading">
        <h1>Page Heading</h1>
    </div>
    <div class="page-container">
        <div class="page-content">
            <ul>
                <li>
                    <a href="#">Item 1</a>
                      <p class="price">$100</p>
                      <p class="stock">12</p>
                </li>
                <li>
                    <a href="#">Item 2</a>
                    <p class="price">$200</p>
                    <p class="stock">422</p>
                </li>
                <li>
                    <a href="#">Item 3</a>
                    <p class="price">$150</p>
                    <p class="stock">5</p>
                </li>
            </ul>
        </div>
    </div>
    <footer class="page-footer">
        <p>Last Updated: Friday, September 23, 2022</p>
    </footer>
</body>
</html>

Ensuite, vous devez fournir le fichier HTML comme entrée à Cheerio, qui renverra alors l'API résultante :

import fs from 'fs'
import * as cheerio from 'cheerio'

const staticHTML = fs.readFileSync('static.html')
const $ = cheerio.load(staticHTML)

Si vous recevez une erreur à cette étape, assurez-vous que le fichier d'entrée contient un document HTML valide, car à partir de la version 1.0.0 de Cheerio, ce critère est également vérifié.

Vous pouvez désormais commencer à explorer les possibilités offertes par Cheerio. Le package NPM est réputé pour sa syntaxe de type jQuery et l’utilisation de sélecteurs CSS pour extraire les nœuds que vous recherchez. Vous pouvez consulter la documentation officielle pour vous faire une meilleure idée.

Imaginons que vous souhaitiez extraire le titre de la page :

const title = $('title').text()
console.log("Static HTML page title:", title)

Nous devrions tester cela, n'est-ce pas ? Vous utilisez Typescript, vous devez donc compiler le code, ce qui créera le répertoire dist, puis exécuter le fichier index.js associé. Pour simplifier, je vais définir le script suivant dans le fichier package.json :

"scripts": {
    "test": "npx tsc && node dist/index.js",
}

De cette façon, il me suffit d'exécuter :

npm run test

et le script se chargera des deux étapes que je viens de décrire.

D'accord, mais que se passe-t-il si le sélecteur correspond à plusieurs éléments HTML ? Essayons d'extraire le nom et la valeur boursière des éléments présentés dans la liste non ordonnée :

const itemStocks = {}
$('li').each((index, element) => {
    const name = $(element).find('a').text()
    const stock = $(element).find('p.stock').text()
    itemStocks[name] = stock
})
console.log("All items stock:", itemStocks)

Exécutez à nouveau le script de raccourci, et la sortie de votre terminal devrait ressembler à ceci :

Static HTML page title: Page Name - Static HTML Example
All items stock: { 'Item 1': '12', 'Item 2': '422', 'Item 3': '5' }

Cas d&#x27;utilisation de Cheerio

Ce n'était en fait que la partie émergée de l'iceberg. Cheerio est également capable d'analyser des documents XML, d'extraire le style des éléments HTML et même de modifier les attributs des nœuds.

Mais comment Cheerio peut-il vous aider dans un cas d'utilisation concret ?

Imaginons que nous souhaitions collecter des données pour entraîner un modèle d’apprentissage automatique en vue d’un futur projet de plus grande envergure. Habituellement, vous rechercheriez des fichiers d’entraînement sur Google pour les télécharger, ou vous utiliseriez l’API du site web.

Mais que faire lorsque vous ne trouvez pas de fichiers pertinents ou que le site web que vous consultez ne propose pas d’API, impose une limitation de débit sur les données ou ne fournit pas l’intégralité des données que vous voyez sur une page ?

C'est là que le web scraping s'avère utile. Si vous souhaitez découvrir d'autres cas d'utilisation pratiques du web scraping, vous pouvez consulter cet article très bien rédigé sur notre blog.

Revenons à notre sujet : pour les besoins de l'exemple, imaginons que nous nous trouvions précisément dans cette situation : nous voulons des données, mais celles-ci sont introuvables. N'oubliez pas que Cheerio ne gère ni l'extraction du code HTML, ni le chargement du CSS, ni l'exécution du JS.

Ainsi, dans notre tutoriel, j'utilise Puppeteer pour naviguer vers le site web, récupérer le code HTML et l'enregistrer dans un fichier. Ensuite, je répéterai le processus décrit dans la section précédente.

Plus précisément, je souhaite recueillir des avis publics sur Reddit concernant un module de batterie populaire et centraliser les données dans un fichier unique qui sera ensuite utilisé pour alimenter un modèle d'apprentissage automatique potentiel. Ce qui se passe ensuite peut également varier : analyse des sentiments, étude de marché, et la liste est longue.

Old Reddit thread page with the post shortlink shown in the sidebar

Demande du code HTML

Voyons à quoi ressemblera ce cas d'utilisation une fois codé. Tout d'abord, vous devez installer le package NPM Puppeteer :

npm install puppeteer

Je vais également créer un nouveau fichier reddit.ts, pour mieux organiser le projet, et définir un nouveau script dans le fichier package.json :

"scripts": {
    "test": "npx tsc && node dist/index.js",
    "parse": "npx tsc && node dist/reddit.js"
},

Pour récupérer le document HTML, je vais définir une fonction qui ressemblera à ceci :

import fs from 'fs'
import puppeteer from 'puppeteer'
import * as cheerio from 'cheerio'

async function getContent(url: string): Promise<void> {

    // Open the browser and a new tab
    const browser = await puppeteer.launch()
    const page = await browser.newPage()

    // Navigate to the URL and write the content to file
    await page.goto(url)
    const pageContent = await page.content()
    fs.writeFileSync("reddit.html", pageContent)

    // Close the browser
    await browser.close()
    console.log("Got the HTML. Check the reddit.html file.")
}

Pour tester rapidement cela, ajoutez un point d'entrée dans votre code et appelez la fonction :

async function main() {


    const targetURL = 'https://old.reddit.com/r/Drumming/comments/r3tidc/yamaha_ead10/'
    await getContent(targetURL)
}

main()
    .then(() => {console.log("All done!")})
    .catch(e => {console.log("Unexpected error occurred:", e.message)})

Le fichier reddit.html devrait apparaître dans l'arborescence de votre projet, et contenir le document HTML souhaité.

Où sont mes nœuds ?

Passons maintenant à une partie un peu plus difficile : vous devez identifier les nœuds qui nous intéressent pour notre cas d'utilisation. Retournez dans votre navigateur (le vrai) et accédez à l'URL cible. Placez le curseur de votre souris sur la section des commentaires, cliquez avec le bouton droit, puis choisissez l'option « Inspecter ».

L'onglet « Outils de développement » s'ouvrira, vous montrant exactement le même document HTML que vous avez précédemment enregistré sur votre ordinateur.

Reddit thread opened alongside browser developer tools inspecting a comment element in the HTML

Pour extraire uniquement les commentaires, vous devez identifier les sélecteurs propres à cette section de la page. Vous remarquerez que la liste complète des commentaires se trouve dans un conteneur div avec une classe sitetable nestedlisting.

En approfondissant, chaque commentaire individuel a pour parent un élément form, avec la classe usertext warn-on-unload. Ensuite, en bas, vous pouvez voir que le texte de chaque commentaire est réparti entre plusieurs éléments p.

Analyse et enregistrement des données

Voyons comment cela fonctionne en code :

function parseComments(): void {

    // Load the HTML document
    const staticHTML = fs.readFileSync('reddit.html')
    const $ = cheerio.load(staticHTML)

    // Get the comments section
    const commentsSection = $('div.sitetable.nestedlisting')

    // Iterate each comment
    const comments = []


    $(commentsSection).find('form.usertext.warn-on-unload').each((index, comment) => {
        let commentText = ""

          // Iterate each comment section and concatenate them
          $(comment).find('p').each((index, piece) => {
            commentText += $(piece).text() + '\n'
          })

          comments.push(commentText)
    })

    // Write the results to external file
    fs.writeFileSync("comments.json", JSON.stringify({comments}))
}

Très bien, mettons maintenant à jour le point d'entrée avec la fonction nouvellement définie et voyons comment ce code fonctionne ensemble :

async function main() {

    const targetURL = 'https://old.reddit.com/r/Drumming/comments/r3tidc/yamaha_ead10/'
    await getContent(targetURL)
    parseComments()
}

main()
    .then(() => {console.log("All done. Check the comments.csv file.")})
    .catch(e => {console.log("Unexpected error occurred:", e.message)})

Exécutez le code avec le script défini précédemment :

npm run parse

Il faudra environ 5 à 10 secondes pour que le navigateur sans interface s'ouvre et accède à notre URL cible. Si cela vous intéresse davantage, vous pouvez ajouter des horodatages au début et à la fin de chacune de nos fonctions pour vraiment constater la rapidité de Cheerio.

Le fichier comments.json devrait correspondre à notre résultat final :

Code editor showing a comments.json file containing an array of scraped comment text

Ce cas d'utilisation peut facilement être étendu pour analyser le nombre de votes positifs et négatifs pour chaque commentaire, ou pour récupérer les réponses imbriquées des commentaires. Les possibilités sont infinies.

Conclusion

Merci d'être arrivé à la fin de ce tutoriel. J'espère que vous avez compris à quel point Cheerio est indispensable au processus d'extraction de données et comment l'intégrer rapidement à votre prochain projet de scraping.

Nous utilisons également Cheerio dans notre produit, WebScrapingAPI. Si vous vous retrouvez un jour confronté aux nombreux défis du web scraping (blocages d'IP, détection de bots, etc.), pensez à l'essayer.

À propos de l'auteur
Raluca Penciuc, Développeur full-stack @ WebScrapingAPI
Raluca PenciucDéveloppeur full-stack

Raluca Penciuc est développeuse Full Stack chez WebScrapingAPI ; elle conçoit des robots de collecte de données, améliore les techniques de contournement et recherche des moyens fiables de réduire le risque de détection sur les sites cibles.

Commencez à créer

Prêt à faire évoluer votre système de collecte de données ?

Rejoignez plus de 2 000 entreprises qui utilisent WebScrapingAPI pour extraire des données Web à l'échelle de l'entreprise, sans aucun coût d'infrastructure.