Formation Python | Web Scraping avec Beautiful Soup
Apprenez à utiliser Beautiful Soup pour extraire des données efficacement sur le web avec Python. Guide complet pour le scraping HTML et la manipulation DOM.
Qu’est-ce que le Web Scraping ?
Le scraping de données est une technique essentielle pour extraire des informations de sites web en utilisant Python.
On distingue aujourd’hui 2 principaux types de scraping sur le web :
- Le scraping de SERP (Search Engine Result Page) : visant à collecter des informations sur les résultats de recherche, comme les titres, descriptions, URLs ou autres métadonnées. Ce type de scraping est largement exploité par des outils SEO tels que SEMrush ou Ahrefs, qui analysent les performances des mots-clés, suivent les classements, et surveillent la concurrence sur les moteurs de recherche.
- Le scraping de sites web : visant à collecter des données spécifiques sur une ou plusieurs pages web, telles que des prix de produits, des avis clients, des articles de blog, ou toute autre information accessible publiquement.
Qu’il soit sur une SERP ou directement sur un site web, les cas d’usage du web scraping sont nombreux : veille concurrentielle, collecte de données, curation de contenus, alimentation pour machine learning, etc.
Je vous invite à lire notre guide du web scraping si vous souhaitez en apprendre plus sur cet univers passionnant.
Récupérer le contenu d’une page web avec Requests
Vous l’aurez compris, le web scraping consiste à analyser le contenu d’une page web. Il nous faut donc être capable de récupérer le contenu d’une page web depuis son URL ! Et ça, c’est le rôle de notre premier module Python indispensable : requests
.
La bibliothèque requests
est une bibliothèque Python très populaire pour effectuer des requêtes HTTP. Elle est utile pour interagir avec des API ou télécharger des données depuis des sites web.
Installation de requests
Pour installer requests
, taper la commande suivante :
pip install requests
Récupération avec la méthode get
Pour récupérer le contenu d’une page web, on utilise la méthode get
de la bibliothèque requests
. Cette méthode permet d’envoyer une requête HTTP GET à une URL et d’en récupérer la réponse.
Dans le cas du scraping de pages web, ce retour HTTP se présentera généralement sous la forme de document HTML (ou parfois XML) mais gardez en tête que la bibliothèque requests
est tout aussi centrale pour manipuler des retours API, bien souvent au format JSON.
import requests
response = requests.get("https://laconsole.dev/blog")
Si on print(response)
, on obtiendra alors le retour suivant : <Response [200]>
Extraction du résultat
Une bonne habitude est de s’assurer que requests.get()
nous retourne bien un code HTTP de succès.
Pour cela on vérifie ce que contient la propriété response.status_code
:
import requests
response = requests.get("https://laconsole.dev/blog")
if response.status_code == 200:
# ✅ CODE 200
else:
# ❌ CODES 4xx/5xx
Un code HTTP en retour c’est pas mal, mais on ne va pas aller bien loin avec ça. Nous ce qu’on veut, c’est des données !
Pour extraire le contenu brut de la réponse HTTP, on fait appel à la propriété response.text
.
import requests
response = requests.get("https://laconsole.dev/blog")
if response.status_code == 200:
print(response.text)
else:
print(f"Erreur lors de la requête : {response.status_code}")
Avec cette méthode, vous êtes maintenant capable de récupérer du contenu HTML ou JSON depuis une page web, une première étape clé pour le scraping !
Analyser le contenu d’une page web avec BeautifulSoup
Il est temps d’analyser le contenu du code HTML retourné. Et ça, c’est le rôle de la très célèbre librairie Python BeautifulSoup.
Installation de beautifulsoup4
Commençons par installer BeautifulSoup. Pour cela, taper la commande suivante :
pip install beautifulsoup4
Création de la soupe !
La soupe c’est tout simplement le nom donné à l’objet qui va stocker la structure du DOM de la page que nous souhaitons scraper.
import requests
from bs4 import BeautifulSoup
response = requests.get("https://laconsole.dev/blog")
if response.status_code == 200:
soup = BeautifulSoup(response.text, 'html.parser')
else:
print(f"Erreur lors de la requête : {response.status_code}")
La classe BeautifulSoup
attend 2 arguments :
- Le code HTML brut (contenu dans
response.text
) que nous souhaitons scraper - Un parser pour analyser ce contenu HTML. Par défaut, on utilisera
'html.parser'
.
BeautifulSoup propose 5 librairies pour analyser le code HTML : html.parser
, xml
, lxml
, lxml-xml
et html5lib
.
Le choix d’un parseur plutôt qu’un autre sera défini par le type de code à parser (XML ou HTML), les performances qu’il propose ou encore pour des raisons de compatibilité.
Une fois que nous avons créé notre objet BeautifulSoup
(généralement nommé soup
), il est possible d’explorer l’arbre du DOM (Document Object Model) de la page HTML.
Formater sa soupe pour un bel output !
L’utilisation de la méthode prettify()
permet de rendre notre soupe plus… digeste ! 🍜 Concrètement, celle-ci va formater le code HTML de manière plus lisible, en ajoutant des indentations et des retours à la ligne.
Idéal pour un affichage lisible.
import requests
from bs4 import BeautifulSoup
response = requests.get("https://laconsole.dev/blog")
if response.status_code == 200:
soup = BeautifulSoup(response.text, 'html.parser')
formatted_html = soup.prettify()
else:
print(f"Erreur lors de la requête : {response.status_code}")
Exploration de la soupe
Une soupe d’objets
BeautifulSoup nous permet de naviguer facilement dans cette structure HTML, qui se présente sous la forme d’une soupe d’objets.
Ces objets nous aident à récupérer, analyser et manipuler le contenu HTML de manière très flexible.
Voici les 3 principaux types d’objets que nous rencontrons :
Tag
: représente une balise HTML, comme<div>
,<p>
ou<a>
. Il permet d’accéder au contenu de la balise, à ses enfants ou à ses attributs.NavigableString
: représente le contenu textuel à l’intérieur d’une balise HTMLComment
: représente un commentaire HTML.
Les Tag
constituent la grande majorité des objets que nous manipulons.
Ici, soup.h1 retourne un Tag
:
html = "<body><h1 class='text-lg font-bold'>Je suis le titre principal</h1></body>"
soup = BeautifulSoup(response.text, 'html.parser')
print(soup.h1) # Affiche "<h1 class='text-lg font-bold'>Je suis le titre principal</h1>"
Depuis un Tag
, on peut aisément extraire son nom et son contenu textuel :
# ...
print(soup.h1.name) # Affiche "h1"
print(soup.h1.string) # Affiche "Je suis le titre principal"
Pour accéder, ajouter, modifier ou supprimer des attributs, on travaillera comme on le ferait avec une liste en python :
# ...
print(soup.h1['class']) # Affiche ["text-lg", "font-bold"]
soup.h1['id'] = 'main-title' # Ajoute un id => <h1 class="..." id="main-title">...</h1>
del soup.h1['id'] # Supprime l'id
Maintenant que nous visualisons bien de quels objets est constituée notre soupe HTML, il est temps de l’explorer !
Recherche dans l’arbre
BeautifulSoup propose 2 grandes approches lorsqu’il s’agit de rechercher dans l’arbre du DOM.
Les méthodes find()
et find_all()
Ces méthodes sont historiques et permettent de rechercher des éléments en spécifiant des tags, des classes ou des attributs. Elles sont pratiques pour des recherches simples ou ciblées.
h1 = soup.find('h1')
avatar = soup.find('img', id='avatar')
links = soup.find_all('a', href=True)
find()
retourne le premier élément qui matche avec le sélecteur, tandis que find_all()
retourne une liste.
Les méthodes select_one()
et select()
Ces méthodes utilisent des sélecteurs CSS, ce qui les rend intuitives pour les développeurs habitués à écrire des styles CSS ou manipuler le DOM en JavaScript.
Elles sont particulièrement adaptées pour des recherches complexes combinant classes, IDs, et relations hiérarchiques entre les éléments.
first_h2 = soup.select_one('div.content > h2')
secured_links = soup.select('a[href^="https://"]')
select_one()
retourne le premier élément qui matche avec le sélecteur, tandis que select()
retourne une liste.
Dans un objectif d’initiation au scraping web avec Python, il est donc important d’être en mesure de « faire beaucoup avec peu ». Je vous invite donc à privilégier les méthodes select_one()
et select()
, pour leur simplicité et leur universalité.
Naviguer dans l’arbre
L’un des principaux avantages de BeautifulSoup est la facilité avec laquelle on peut naviguer dans la structure hiérarchique d’une page HTML. Cette navigation permet de se déplacer entre les différents éléments, qu’il s’agisse de parents, d’enfants ou de frères et sœurs.
Il existe 2 manières de naviguer dans le DOM :
- En profondeur (decendants et ancêtres)
- Au même niveau (frères et soeurs)
En profondeur
BeautifulSoup nous permet de naviguer dans la profondeur des balises du DOM.
Descendants
Pour accéder aux balises enfants d’une autre, on utilise la propriété children
.
children
renvoie les enfants directs (balises, texte, espaces) d’un élément sous forme d’itérateur (iterable) à parcourir via une boucle.
Considérons le code HTML suivant :
<div id="demo">
<p>
Texte 1
<span>Texte 2</span>
</p>
<a>Texte 3</a>
</div>
div = soup.select_one("#demo")
for child in div.children:
print(child)
On obtient en sortie :
<p>Texte 1 <span>Texte 2</span></p>
<a>Texte 3</a>
Pour accéder directement à l’ensemble des éléments sous forme de liste, il existe la propriété alternative contents
.
div = soup.select_one("#demo")
div.contents
On obtient en sortie :
[<p>Texte 1 <span>Texte 2</span></p>, <a>Texte 3</a>]
La propriété descendants
permet quant à elle de parcourir tous les descendants d’un élément, à n’importe quel niveau de profondeur, plutôt que juste les enfants directs.
Le résultat sera également disponible sous la forme d’un itérateur (iterable) :
div = soup.select_one("#demo")
for descendant in div.descendants:
print(descendant)
On obtient en sortie :
<p>Texte 1 <span>Texte 2</span></p>
Texte 1
<span>Texte 2</span>
Texte 2
<a>Texte 3</a>
Texte 3
Ancêtres
Il est possible d’effectuer les opérations inverses avec les propriétés parent
et parents
.
Considérons le code HTML suivant :
<div id="demo">
<p>
Texte
<span>1</span>
</p>
</div>
parent
renvoie l’élément parent direct de l’élément actuel.
p = soup.select_one("p")
print(p.parent)
On obtient en sortie :
<div id="demo"><p>Texte <span>1</span></p></div>
parents
renvoie un itérateur sur tous les ancêtres de l’élément, du plus proche au plus éloigné (jusqu’à la racine [document]
du document).
span = soup.select_one("span")
for parent in span.parents:
print(parent)
On obtient en sortie :
<p>Texte <span>1</span></p>
<div id="demo"><p>Texte <span>1</span></p></div>
<div id="demo"><p>Texte <span>1</span></p></div>
Utilisez name
dans la boucle for
pour visualiser les noms des ancêtres :
span = soup.select_one("span")
for parent in span.parents:
print(parent.name)
Cela affichera en sortie :
p
div
[document]
On remarque bien que le contenu de [document]
est identique à la <div id="demo">
, encapsulant tout le contenu de la page.
Au même niveau
Les propriétés next_sibling
, previous_sibling
, next_siblings
et previous_siblings
permettent de naviguer entre les éléments frères (siblings) d’un élément, c’est-à-dire les éléments qui partagent le même parent.
Considérons le code HTML suivant :
<p id="p1">Premier</p>
<p id="p2">Deuxième</p>
<p id="p3">Troisième</p>
previous_sibling
: Renvoie l’élément précédent au même niveau que l’élément actuel dans le DOM.next_sibling
: Renvoie l’élément suivant au même niveau que l’élément actuel dans le DOM.
p2 = soup.select_one("#p2")
print(p2.previous_sibling)
print(p2.next_sibling)
Cela affichera en sortie :
<p id="p1">Premier</p>
<p id="p3">Troisième</p>
Les versions plurielles next_siblings
et previous_siblings
de ces attributs renvoient respectivement un itérateur de tous les frères suivants ou précédents d’un élément.
p1 = soup.select_one("#p1")
for next in p1.next_siblings:
print(next)
Cela affichera en sortie :
<p id="p2">Deuxième</p>
<p id="p3">Troisième</p>
p3 = soup.select_one("#p3")
for previous in p3.previous_siblings:
print(previous)
Cela affichera en sortie :
<p id="p2">Deuxième</p>
<p id="p1">Premier</p>
Modifier l’arbre
Si les propriétés vues précédemment s’avèrent très utiles pour analyser du code HTML/XML, il peut parfois être intéressant d’aller modifier cette structure.
C’est par exemple le cas si l’on cherche à :
- Ajouter de nouvelles balises pour enrichir le contenu
- Nettoyer le DOM en supprimant des éléments inutiles ou gênants (
<script>
,<style>
, espaces et retours à la ligne…) - Restructurer l’arbre pour faciliter l’analyse ou l’exploitation
- Corriger une erreur dans le DOM source
- Insérer des données dynamiques récupérées depuis une autre source
- Etc.
S’agissant d’un besoin à mon sens plus secondaire dans cette initiation au scraping web avec BeautifulSoup, je me contenterai de lister sans rentrer dans les détails quelques propriétés et méthodes utiles pour modifier le DOM :
Méthode/propriété | Rôle |
---|---|
append() | Ajoute un nouveau nœud comme dernier enfant d’une balise existante. |
insert() | Insère un nœud enfant à une position spécifique dans une balise. |
new_tag() | Crée une nouvelle balise HTML ou XML que vous pouvez ajouter dans l’arbre DOM. |
decompose() | Supprime complètement un nœud de l’arbre DOM. |
extract() | Supprime un nœud de l’arbre DOM et le retourne (utile pour réutilisation). |
clear() | Supprime tout le contenu enfant d’une balise sans supprimer la balise elle-même. |
replace_with() | Remplace un nœud existant par un autre nœud. |
wrap() | Enveloppe un nœud existant dans une nouvelle balise. |
unwrap() | Supprime une balise tout en conservant son contenu enfant. |
string | Définit ou modifie le texte à l’intérieur d’une balise. |