Retour au blog
Guides
Mihnea-Octavian Manolache28 février 202311 min de lecture

Le guide complet pour créer un outil de scraping Web avec Pyppeteer

Le guide complet pour créer un outil de scraping Web avec Pyppeteer

Qu'est-ce que Pyppeteer exactement et comment l'utiliser ?

Si vous lisez ceci, il y a de fortes chances que vous sachiez déjà ce qu’est le script web en général. Et vous avez probablement déjà entendu parler de Puppeteer ou de Selenium, selon votre langage de programmation préféré. Mais Pyppeteer est en effet plus récent dans le domaine du web scraping. Pour faire court, Pyppeteer ressemble beaucoup plus à Puppeteer qu’à Selenium.

Puppeteer est une bibliothèque Node.js qui facilite le contrôle d'une version headless de Chrome via le protocole DevTools. Pyppeteer est un portage Python de Puppeteer. Tout comme le Puppeteer original, Pyppeteer est une bibliothèque, écrite en Python, qui automatise essentiellement un navigateur. En d'autres termes, Pyppeteer est une implémentation Python de l'API Puppeteer, qui vous permet d'utiliser les fonctionnalités de Puppeteer dans un environnement Python. La principale différence entre les deux réside dans le langage utilisé.

Terminologie Pyppeteer à connaître

Avant d'aller plus loin, je pense que nous devrions aborder certains termes couramment utilisés dans le contexte de Pyppeteer :

  • Headless : cela signifie démarrer un navigateur sans interface utilisateur graphique (GUI). En d'autres termes, il s'exécute « en arrière-plan » et vous ne pouvez pas le voir à l'écran. Il est généralement utilisé pour réduire l'utilisation des ressources lors du scraping.
  • Headful : À l'inverse, un navigateur « headful » est un navigateur qui s'exécute avec une interface graphique. C'est le contraire d'un navigateur headless et il est souvent utilisé pour tester, déboguer ou interagir manuellement avec des pages web.
  • Contexte du navigateur : Il s'agit d'un état partagé entre toutes les pages d'un navigateur. Il est généralement utilisé pour définir des paramètres à l'échelle du navigateur, tels que les cookies, les en-têtes HTTP et la géolocalisation.
  • DOM : Le Document Object Model (DOM) est une interface de programmation pour les documents HTML et XML. Il représente la structure d’une page web sous forme d’arborescence, avec des nœuds représentant les éléments. Pyppeteer vous permet d’interagir avec les éléments d’une page en manipulant le DOM.
  • Éléments : Les éléments constitutifs d'une page web. Ils sont définis à l'aide de balises, d'attributs et de valeurs.

Bien sûr, ce n'est pas tout et vous en apprendrez davantage au fur et à mesure. Mais je tenais à ce que vous en ayez une idée générale afin que nous partions sur de bonnes bases. Je suis convaincu que la connaissance de ces termes vous aidera à mieux comprendre l'essence de cet article.

Pourquoi utiliser Pyppeteer dans votre projet de scraping ?

Je pense qu'il y a deux aspects à cette question. Le premier est de savoir pourquoi Pyppeteer est un bon choix pour le web scraping en général. Le second est de savoir pourquoi utiliser Pyppeteer plutôt que Selenium. De manière générale, voici quelques-uns des avantages de Pyppeteer :

  • Évaluation de JavaScript : Pyppeteer fournit une fonction `page.evaluate()`. Elle vous permet d'exécuter du code JavaScript dans le contexte de la page.
  • Contrôle du réseau : Pyppeteer fournit une méthode `page.on()`. Celle-ci vous permet d'écouter les événements réseau, tels que les requêtes et les réponses, qui se produisent sur une page.
  • Traçage et journalisation : Pyppeteer vous permet de tracer l'activité du navigateur et de journaliser les messages du navigateur provenant d'une page. Cela facilite le débogage, le traçage et la compréhension du fonctionnement d'un site web.

Comparé à Selenium, il est assez similaire, dans la mesure où les deux sont utilisés pour automatiser un navigateur web. Cependant, il existe quelques différences et avantages clés que Pyppeteer présente par rapport à Selenium :

  • Simplicité : Pyppeteer dispose d'une API plus simple et plus cohérente que Selenium, ce qui le rend plus facile à utiliser pour les débutants. L'API Pyppeteer s'appuie sur le protocole DevTools, qui est proche du navigateur et facile à apprendre et à utiliser.
  • Performances : Pyppeteer peut être plus rapide que Selenium car il s'appuie sur le protocole DevTools. Ce protocole est conçu pour le débogage de pages web et est bien plus rapide que Selenium WebDriver.
  • Meilleur contrôle du réseau : Pyppeteer permet un meilleur contrôle des paramètres réseau du navigateur, tels que l'interception des requêtes et le blocage des requêtes/réponses. Cela facilite le test et le diagnostic des problèmes liés au réseau.

Et bien sûr, c'est aussi une question de choix. Prenez mon cas, par exemple. Au quotidien, je code en JavaScript. Et je connais bien Pyppeteer. Mais mon langage de programmation préféré est Python. Donc, si je devais créer un scraper avec une technologie que je connais dans un langage que je préfère, j'opterais probablement pour Pyppeteer.

Cela étant dit, je pense que nous avons couvert les aspects « théoriques » de cet article. Il est temps de passer au codage proprement dit.

Comment créer un scraper web avec Pyppeteer

Avant de commencer à coder, laissez-moi vous présenter la documentation officielle de Pyppeteer. Je recommande vivement de consulter la documentation officielle dès que l'on se sent bloqué. C'est à faire avant de poser des questions à la communauté (comme sur Stackoverflow). Je constate généralement que la plupart des réponses se trouvent en lisant d'abord la documentation. Considérez donc ceci comme une gentille recommandation de ma part. Chaque fois que vous êtes bloqué, consultez la documentation, puis cherchez des réponses et ne posez des questions qu'en dernier recours.

#1 : Configurer l'environnement

Commençons par le commencement : en tant que développeur Python, vous connaissez probablement les environnements virtuels. La première chose à faire est donc de créer un environnement virtuel pour notre projet. Voici généralement la séquence de commandes que j'utilise :

# Create a new directory and navigate into it

~ » mkdir py_project && cd py_project 

# Create the virtual environment

~ » python3 -m venv env 

# Activate the virtual environment

~ » source env/bin/activate

En ce qui concerne l'environnement virtuel, tout est prêt. Il est temps de passer à l'étape suivante et d'installer Pyppeteer. Puisque votre terminal est ouvert, tapez simplement :

# Install the package using pip

~ » python3 -m pip install pyppeteer

# Open the project in your IDE

~ » code .

#2 : Créer un simple scraper Pyppeteer

La dernière commande ouvre Visual Studio Code ou votre IDE préféré. Maintenant que vous êtes dans l’« environnement de développement », créons un nouveau fichier `.py` qui contiendra notre code. Je vais appeler mon fichier `scraper.py`. Notez que Pyppeteer prend en charge nativement l’exécution asynchrone. Importons donc à la fois `asyncio` et `pyppeteer` dans notre fichier :

import asyncio
from pyppeteer import launch

Cela étant fait, nous pouvons passer à un code plus complexe. En général, je ne suis pas le plus grand défenseur de la programmation fonctionnelle. Cependant, je pense que diviser le code en petits morceaux facilite l'apprentissage. Mettons donc notre code dans une fonction :

async def scrape(url):

   browser = await launch()

   page = await browser.newPage()

   await page.goto(url)

   content = await page.content()

   await browser.close()

  

   return content
Side-by-side code editor panes showing a Puppeteer script that launches a browser and loads a page

Cette fonction prend une URL en entrée et lance un navigateur sans interface utilisateur à l'aide de pyppeteer. Elle se rend ensuite à l'URL fournie, récupère le contenu de la page et ferme le navigateur. La valeur qu'elle renvoie n'est autre que le code HTML collecté sur la page. Vous pouvez utiliser cette fonction pour extraire les données de presque n'importe quel site web. Pour l'utiliser, vous devez l'appeler dans une boucle d'événements `asyncio`, comme ceci :

async def main():

   content = await scrape('https://www.example.com')

   print(content)

loop = asyncio.get_event_loop()

loop.run_until_complete(main())

#3 : Ajouter des fonctionnalités supplémentaires

À ce stade, nous disposons d’un scraper fonctionnel. Mais c’est à peu près tout ce que nous avons. Si vous souhaitez créer un scraper web plus avancé avec Pyppeteer, vous devrez ajouter des fonctionnalités supplémentaires à id. Attention, spoiler : nous allons nous plonger dans l’univers de la programmation orientée objet. Mais avant cela, définissons nos objectifs. Que voulons-nous que notre scraper soit capable de faire ?

  • Initialiser le navigateur avec des valeurs personnalisées
  • Naviguer et extraire le contenu d'une page web
  • Saisir du texte dans un champ de saisie
  • Extraire la valeur d'un élément unique
  • Extraire la valeur de plusieurs éléments

3.1. Options personnalisées

Créons donc pour l'instant une nouvelle classe `Scraper` et nous ajouterons ses méthodes par la suite :

class Scraper:

   def __init__(self, launch_options: dict) -> None:

   	self.options = launch_options['options']

 	self.viewPort = launch_options['viewPort'] if 'viewPort' in launch_options else None

pass

Le seul argument que nous utilisons pour notre Scraper est un dictionnaire `launch_options`. Comme vous pouvez le voir, il contient deux clés. Une clé définit les options du lanceur Pyppeteer. La deuxième option est soit `None`, soit un dictionnaire contenant la `width` et la `height` du `viewPort`. C'est cette dernière qui est utilisée pour cette méthode.

3.2. Accéder à une page

Si vous examinez la fonction que nous avons utilisée précédemment, vous verrez qu’elle couvre à la fois la navigation et l’extraction de données brutes à partir d’une URL spécifique. Il ne nous reste plus qu’à modifier légèrement cette fonction pour la transformer en méthode pour notre Scraper :

async def goto(self, url: str) -> None:

       self.browser = await launch(options=self.options)

       self.page = await self.browser.newPage()

       await self.page.setViewport(self.viewPort) if self.viewPort != None else print('[i] Using default viewport')

       await self.page.goto(url)

Cette méthode est assez simple. Tout d’abord, elle lance un nouveau navigateur, avec les options personnalisées que nous avons définies précédemment. Elle crée ensuite une nouvelle page et, si notre dictionnaire `launch_options` contient `viewPort`, elle définit le viewPort de la page. Sinon, elle affiche un simple message. Enfin, elle nous amène à la page cible.

3.3. Extraire les données brutes d'une page

Une fois encore, cette méthode se trouve dans notre fonction `scraper` initiale. Nous allons simplement attendre que `page.content()` se charge et renvoie sa valeur :

async def get_full_content(self) -> str:

       content = await self.page.content()

       return content

3.4. Écrire du texte dans un champ de saisie

Pour écrire quelque chose dans un champ de saisie à l’aide de Pyppeteer, vous aurez besoin de deux choses. Premièrement, localisez l’élément. Deuxièmement, ajoutez-y une valeur. Heureusement, Pyppeteer dispose de méthodes pour ces deux actions :

async def type_value(self, selector: str, value: str) -> None:

       element = await self.page.querySelector(selector)

       await element.type(value)

3.5. Extraire une valeur d'une page

Rappelez-vous que nous voulons pouvoir extraire soit la valeur d’un seul élément, soit les valeurs de plusieurs éléments. Nous pourrions utiliser une seule méthode pour les deux. Mais j’aime généralement séparer les choses. Donc, pour l’instant, je vais ajouter deux méthodes supplémentaires :

async def extract_one(self, selector) -> str:

       element = await self.page.querySelector(selector)

       text = await element.getProperty("textContent")

       return await text.jsonValue()

Ici, nous localisons l'élément à l'aide de la méthode `querySelector`. Nous attendons ensuite le `textContent` et renvoyons sa `jsonValue()`. D'autre part, lorsque nous voulons sélectionner plusieurs éléments, nous utiliserons `querySelector` :

async def extract_many(self, selector) -> list:

       result = []

       elements = await self.page.querySelectorAll(selector)

       for element in elements:

           text = await element.getProperty("textContent")

           result.append(await text.jsonValue())

       return result

Cette méthode fonctionne de manière similaire à `extract_one`. La seule différence réside dans sa valeur de retour. Cette fois-ci, nous renvoyons une liste de tout le texte contenu dans les éléments sélectionnés. Et je pense qu’avec cet ajout, nous avons atteint tous nos objectifs.

#4 : Rendre le processus furtif

En web scraping, la discrétion peut être décrite comme la capacité à passer inaperçu. Bien sûr, créer un scraper totalement indétectable demande beaucoup de travail. Par exemple, le mode furtif de Web Scraping API est géré par une équipe dédiée. Et les efforts qui y sont consacrés rendent l’empreinte de notre scraper unique à chaque requête.

Mais mon objectif global pour ce tutoriel est de vous mettre sur la bonne voie. Et la bonne voie pour un scraper web complet avec Pyppeteer implique d'y ajouter des fonctionnalités de discrétion. Heureusement, tout comme il existe `puppeteer-extra-plugin-stealth` dans Node, il existe également un package pour Python. Et il s'appelle, de manière intuitive, `pyppeteer-stealth`. Pour l'ajouter à votre projet, commencez par l'installer à l'aide de pip :

~ » python3 -m pip install pyppeteer_stealth

Importez-le ensuite dans votre projet et ajoutez simplement une ligne de code supplémentaire :

async def goto(self, url: str) -> None:

       self.browser = await launch(options=self.options)

       self.page = await self.browser.newPage()

	  # Make it stealthy

       await stealth(self.page)  

       await self.page.setViewport(self.viewPort) if self.viewPort != None else print('[i] Using default viewport')

       await self.page.goto(url)

Et voici comment exécuter votre scraper. J'ai ajouté quelques commentaires au code pour mettre en évidence ce que fait chaque étape :

async def main():

   # Define the launch options dictionary

   launch_options = {

       'options': {

           'headless': False,

           'autoClose': True

       },

       'viewPort': {

           'width': 1600,

           'height': 900

       }

   }

   # Initialize a new scraper

   scraper = Scraper(launch_options)

   # Navigae to your target

   await scraper.goto('https://russmaxdesign.github.io/accessible-forms/accessible-name-input01.html')

   # Type `This is me` inside the input field

   await scraper.type_value(

       '#fish',

       'This is me')

   # Scrape the entire page

   content = await scraper.get_full_content()

   print(content)

   # Scrape one single element

   el = await scraper.extract_one('body > div:nth-child(14) > ul')

   print(el)

   # Scrape multiple elements

   els = await scraper.extract_many('p')

   print(els)

loop = asyncio.get_event_loop()

loop.run_until_complete(main())

Conclusion

Pyppeteer est un outil formidable pour le web scraping. Il porte l’intégralité de l’API Puppeteer vers Python, permettant ainsi à la communauté Python d’utiliser cette technologie sans avoir à apprendre JavaScript. De plus, je ne pense pas qu’il s’agisse d’un remplacement de Selenium, mais c’est certainement une bonne alternative.

J'espère que l'article d'aujourd'hui a apporté une valeur ajoutée à votre parcours d'apprentissage. Et comme j'aime pousser tout le monde à repousser ses limites, je vous mets au défi d'approfondir ce que vous avez appris aujourd'hui. Le scraper que nous avons construit ensemble est un très bon point de départ et introduit un élément clé de la programmation : la POO. Je vous mets donc au défi d'ajouter d'autres méthodes à `Scraper` pour le rendre vraiment incroyable.

À propos de l'auteur
Mihnea-Octavian Manolache, Développeur Full Stack @ WebScrapingAPI
Mihnea-Octavian ManolacheDéveloppeur Full Stack

Mihnea-Octavian Manolache est ingénieur Full Stack et DevOps chez WebScrapingAPI, où il développe des fonctionnalités pour les produits et assure la maintenance de l'infrastructure qui garantit le bon fonctionnement de la plateforme.

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.