Scraper le web avec Puppeteer
Dans ce tutoriel vous apprendrez à contrôler un navigateur en JavaScript avec Puppeteer. À travers différents exemples vous verrez comment prendre le screenshot d'une page web, récupérer les données d'un site ou scraper les informations de 50 pages en parallèle.
Puppeteer est une librairie Node, développée par Google qui permet de contrôler une instance du navigateur Chrome Headless (Chrome sans UI). Elle est utilisée pour :
- Générer des screenshots et des PDF
- Scraper le contenu des sites web
- Automatiser le remplissage de formulaires
- Faire des tests d'UI
- Créer des tests automatisés
- Diagnostiquer des problèmes de performances avec l'API Dev Tools
Exemple #1 - Prendre un screenshot
Comment prendre le screenshot d'une page web en 10 lignes de code ? Dans cet exemple, vous découvrirez les commandes de base Puppeteer. Suivez toutes les étapes pour vous familiariser avec la librairie. Toutes les lignes de code du script vous sont expliquées en détail.
Pour commencer, créez un nouveau répertoire et utilisez la commande npm pour installer la librairie Puppeteer :
mkdir puppeteerTest
cd puppeteerTest
npm install puppeteer
Créez un fichier screenshot.js
et copiez/collez le code ci-dessous :
const puppeteer = require("puppeteer")
const getScreenshot = async () => {
const browser = await puppeteer.launch()
const page = await browser.newPage()
await page.goto("http://www.google.com")
await page.screenshot({ path: "screenshot.png" })
await browser.close()
}
getScreenshot()
Lancez le script en tapant la commande suivante dans votre terminal :
node screenshot.js
Ce programme a créé une instance de Chrome qui a navigué jusqu'à "www.google.com". Une fois la page chargée, une photo de la page a été prise et enregistrer sous le nom "screenshot.png". L'image est disponible dans le répertoire de votre projet.
Voici le même code commenté
// Import du paquet "puppeteer"
const puppeteer = require('puppeteer')
// Déclaration d’une arrow function asynchrone *
const screenshot = async () => {
// Création d’une instance de Chrome
const browser = await puppeteer.launch()
// Création d’un nouvel onglet
const page = await browser.newPage()
// Navigation vers l'URL souhaitée
await page.goto('http://www.google.com')
// Screenshot de la page
await page.screenshot({ path: 'screenshot.png' })
// Fermeture du navigateur
await browser.close()
}
// Appel de la fonction principale
screenshot()
* : Pour comprendre les fonctions asynchrones et maitrisez les mots clés async / await, regardez cet article Smooth Code.
Parfait ! Vous savez maintenant comment automatiser la capture d'écran d'une page web avec Puppeteer. Et en bonus, vous connaissez les fonctions de base offertes par Puppeteer : création d'une instance ou d'un onglet, navigation vers une page et fermeture du navigateur.
Pour suivre l’exécution de votre script et faciliter le débogage, vous pouvez lancer le programme sans la partie headless. Vous pourrez ainsi voir le navigateur s'ouvrir et naviguer en direct. Modifiez la ligne suivante de votre script et relancer l'exécution :
const browser = await puppeteer.launch({ headless: false })
Cool hein 😆 ?!
Un dernier petit ajout avant de passer à la suite. Le screenshot est petit et décentré parce que la fenêtre du navigateur chrome est trop petite. Nous pouvons corriger ce point en spécifiant la taille de la fenêtre de navigation. Ajoutez la ligne suivante avant l'enregistrement de l'image :
await page.setViewport({ width: 1000, height: 500 })
Voilà, l'image est maintenant fidèle à une navigation classique sur desktop.
C'est la fin du premier exemple. Dans les exemples suivants vous utiliserez les commandes de base de Puppeteer. Voilà un take await du premier exemple :
const browser = await puppeteer.launch() //Création d’une instance de Chrome
const page = await browser.newPage() // Création d’un nouvel onglet
await page.goto(url) // Navigation vers l'URL souhaitée
...
await browser.close() // Fermeture du navigateur
Exemple #2 - Scraper des données
Maintenant que vous maitrisez les bases de la navigation avec Puppeteer, regardons comment consulter un site web et en extraire les données.
Petite parenthèse : je vous invite à regarder la documentation de Puppeteer. Vous y trouverez toutes les fonctions pour naviguer, cliquer, remplir des formulaires, taper du texte ou lire des données. Utilisez-là comme livre de chevet lorsque vous écrirez vos scripts.
Revenons à nos moutons. Dans cet exemple, vous allez scraper Book To Scrape, un faux site fait justement pour s'entraîner au scraping.
Voilà la trame du script à écrire :
- Ouvrir la page "http://books.toscrape.com/"
- Cliquer sur l'image du premier livre
- Sur la nouvelle page, récuperer le titre et le prix du livre
- Afficher ces informations dans la console
Structure du code
Dans le code suivant des commentaires délimitents les différentes parties du code à écrire et les commandes de bases de Puppeteer ont étées ajoutées. Créez un fichier scrape.js
et copiez/collez ce code.
// Import de puppeteer
const puppeteer = require("puppeteer")
const getData = async () => {
// 1 - Créer une instance de navigateur
const browser = await puppeteer.launch({ headless: false })
const page = await browser.newPage()
// 2 - Naviguer jusqu'à l'URL cible
await page.goto("http://books.toscrape.com/")
// 3 - Cliquer sur un lien...
// 4 - Récupérer les données...
// 5 - Retourner les données
browser.close()
return result
}
// Appel de la fonction getData() et affichage des données
getData().then(value => {
console.log(value)
})
Si certaines de ces lignes vous sont inconnues, reportez-vous à l'exemple précédent ou à la documentation de l'API.
Il reste maintenant à écrire le code pour "cliquer sur un lien" et "récupérer les données".
Cliquer sur un lien
D'après la documentation de Puppeteer la fonction pour cliquer sur un lien est :
page.click(selector[, options])
selector
<string> selecteur de l'élement à cliquer. Si plusieurs éléments sont trouvés, le premier est selectionné.
Grâce à l'inspecteur Google, il est très facile de trouver le selecteur d'un élément. Suivez la méthode présentée dans l'image ci-dessous :

Il ne vous reste plus qu'à combiner les deux :
await page.click(
"#default > div > div > div > div > section > div:nth-child(2) > ol > li:nth-child(1) > article > div.image_container > a > img"
)
Récupérer le titre et le prix
Pour récupérer du contenu sur une page, vous devez utiliser la méthode evaluate()
. Dans cet exemple, vous devez renvoyer un object JavaScript contenant le titre et le prix du livre. Voilà le code pour le faire :
const result = await page.evaluate(() => {
let title = document.querySelector("h1").innerText
let price = document.querySelector(".price_color").innerText
return { title, price }
})
Element.querySelector("selector")
: selectionne le premier élément qui valide le selecteur passé en paramêtreinnerText
: renvoie le texte contenu dans l'élément HTML
Solution complète
const puppeteer = require('puppeteer')
const getData = async () => {
// 1 - Créer une instance de navigateur
const browser = await puppeteer.launch({ headless: false })
const page = await browser.newPage()
// 2 - Naviguer jusqu'à l'URL cible
await page.goto('http://books.toscrape.com/')
await page.click(
'#default > div > div > div > div > section > div:nth-child(2) > ol > li:nth-child(1) > article > div.image_container > a > img',
)
await page.waitFor(1000) // fait une pause d'une seconde
// 3 - Récupérer les données
const result = await page.evaluate(() => {
let title = document.querySelector('h1').innerText
let price = document.querySelector('.price_color').innerText
return { title, price }
})
// 4 - Retourner les données (et fermer le navigateur)
browser.close()
return result
}
// Appelle la fonction getData() et affichage les données retournées
getData().then(value => {
console.log(value)
})
En lançant votre code avec la commande node scrape.js
, les informations suivantes s'affichent dans votre console : { title: 'A Light in the Attic', price: '£51.77' }
Vous savez maintenant comment utiliser Puppeteer pour récupérer des informations sur une page web. Dans le dernier example vous verrez comment industrialiser ce script et récupérer les informations de tous les livres de la bibliothèque en parallèle.
Exemple #3 — Scraper plusieurs pages en parallèle
Passons à l'étape suivante l'industrialisation du scraping. Ici vous récupérerez les informations de tous les livres de la première page en parallèle. Dans un premier temps, vous devez recupérer les URLs des pages des livres présents sur "http://books.toscrape.com/". Ensuite, vous lancerez un navigateur qui récupérera les données sur chacune des pages listées.
Structure du code
Comme dans l'exemple précédent, copiez-collez le code suivant dans un nouveau ficher scraper.js
.
// 1 - Import de puppeteer
const puppeteer = require("puppeteer")
// 2 - Récupération des URLs de toutes les pages à visiter
const getAllUrl = async (browser) => {}
// 3 - Récupération du prix et du tarif d'un livre à partir d'une url
const getDataFromUrl = async (browser, url) => {}
// 4 - Fonction principale : instanciation d'un navigateur et renvoi des résultats
const scrap = async () => {
const browser = ...
const urlList = ...
const results = ...
browser.close()
return results
}
// 5 - Appel de la fonction `scrap()` et affichage des résulats
scrap()
.then(value => {
console.log(value)
})
.catch(e => console.log(`error: ${e}`))
Cette fois-ci, essayez de terminer seuls. La solution se trouve juste en dessous. Ne trichez pas !
Solution
// 1 - Import de puppeteer
const puppeteer = require('puppeteer')
/*
// 2 - Récupération des URLs de toutes les pages à visiter
- waitFor("body"): met le script en pause le temps que la page se charge
- document.querySelectorAll(selector): renvoie tous les noeuds qui vérifient le selecteur
- [...document.querySelectorAll(selector)]: caste les réponses en tableau
- Array.map(link => link.href): récupère les attributs href de tous les liens
*/
const getAllUrl = async browser => {
const page = await browser.newPage()
await page.goto('http://books.toscrape.com/')
await page.waitFor('body')
const result = await page.evaluate(() =>
[...document.querySelectorAll('.product_pod a')].map(link => link.href),
)
return result
}
// 3 - Récupération du prix et du tarif d'un livre à partir d'une url (voir exo #2)
const getDataFromUrl = async (browser, url) => {
const page = await browser.newPage()
await page.goto(url)
await page.waitFor('body')
return page.evaluate(() => {
let title = document.querySelector('h1').innerText
let price = document.querySelector('.price_color').innerText
return { title, price }
})
}
/*
// 4 - Fonction principale : instanciation d'un navigateur et renvoi des résultats
- urlList.map(url => getDataFromUrl(browser, url)):
appelle la fonction getDataFromUrl sur chaque URL de `urlList` et renvoi un tableau
- await Promise.all(promesse1, promesse2, promesse3):
bloque de programme tant que toutes les promesses ne sont pas résolues
*/
const scrap = async () => {
const browser = await puppeteer.launch({ headless: false })
const urlList = await getAllUrl(browser)
const results = await Promise.all(
urlList.map(url => getDataFromUrl(browser, url)),
)
browser.close()
return results
}
// 5 - Appel la fonction `scrap()`, affichage les résulats et catch les erreurs
scrap()
.then(value => {
console.log(value)
})
.catch(e => console.log(`error: ${e}`))
Vous savez maintenant comment construire un code de scraping industriel pour récupérer des données automatiquement sur le web. Si vous avez des questions sur le code, n'hésitez pas à les poster en commentaire.
Sources :