Licence CC BY-NC

Scraper des données sur une page web en Python avec BeautifulSoup

Dans ce billet vous allez apprendre à scraper des données précises dans un page web en Python (pour par exemple récupérer des listes d’adresses ou autres).

Le scraping de donnée consiste à récupérer automatiquement du contenu d’une page Web, par opposition au fait de les récupérer "à la main" avec des copier-coller.

Les prérequis et leur installation

Pour nous atteler à cette tâche, nous avons besoin de plusieurs choses :

  • Python (2 ou 3 peu importe, sachant que les exemples présentés seront en Python 3)
  • La bibliothèque BeautifulSoup
  • La bibliothèque Requests
  • Le gestionnaire de paquet pip

Nous allons partir du principe que vous avez déjà Python d’installé et connaissez les bases de ce langage. Sinon je ne peux que vous conseiller de lire l’excellent tutoriel disponible sur le site.

Le gestionnaire de paquet pip

Pip est un gestionnaire de paquet pour Python qui permet d’installer, mettre à jour et de désinstaller facilement les bibliothèques non intégrées de base avec Python. Je vous recommande chaudement de l’utiliser si ce n’est pas déjà le cas.

Pour le télécharger, vous pouvez vous rendre sur la documentation officielle de pip et suivre la procédure décrite en fonction de votre OS.

Une fois installé, si vous êtes sous Windows, n’oubliez pas d’ajouter pip dans votre variable PATH pour y avoir accès facilement, en rajoutant le chemin vers le dossier Script de votre installation Python dans le PATH (un peu d’aide sur le sujet chez Sam & Max).

Voilà vous pouvez désormais installer facilement des bibliothèques Python (et toutes leurs dépendances) directement depuis votre terminal.

Installer BeautifulSoup et Requests

Vous pouvez désormais récupérer ces merveilleuses bibliothèques grâce à pip.

Pour faire cela, vous devez rentrer la commande suivante dans votre terminal :

1
pip install bs4 requests

Une fois cela fait, nous sommes prêts à attaquer le vif du sujet.

Elle n'est pas belle ma soupe de données ?

Commençons donc la construction de notre scraper.

Pour ce billet nous allons apprendre à scraper une page de Zeste de Savoir : la liste des tutos informatiques.

Il faut donc que notre script commence par les déclarations des bibliothèques utiles.

1
2
3
4
import os
import csv
import requests
from bs4 import BeautifulSoup

Dans cette déclaration :

  • Nous importons la bibliothèque os pour gérer la pause à la fin du script ;
  • La bibliothèque CSV est optionnelle et seulement utile si vous voulez exporter les données scrappées dans un fichier CSV ;
  • Nous importons la bibliothèque Requests qui va nous permettre de charger la page et stocker son contenu dans une variable ;
  • Nous importons BeautifulSoup.

Nous récupérons la page avec requests.get (qui va faire une requête HTTP/HTTPS). Une fois cette requête réalisée, nous pouvons récupérer le contenu de la page avec .content . Enfin nous fournissons ce contenu à BeautifulSoup pour le parser.

1
2
3
requete = requests.get("https://zestedesavoir.com/tutoriels/?category=autres-informatique")
page = requete.content
soup = BeautifulSoup(page)

Nous avons désormais une variable soup qui contient toutes les données structurées de la page et nous allons pouvoir les exploiter.

Récupérer l’information d’une balise HTML précise

Si par exemple nous souhaitions récupérer sur cette page le texte du titre <h1>, nous pouvons procéder comme ceci.

1
2
h1 = soup.find("h1", {"class": "ico-after ico-tutorials"})
print(h1.string)

Détaillons un peu ce code. Pour récupérer les informations de la balise <h1> nous avons récupéré les données avec BeautifulSoup et les avons affectés à une variable. La donnée récupérée est la première occurrence de tag HTML qui soit un tag <h1> et qui possède les classes ico-after ico-tutorials.

Si jamais vous avez un doute sur quelle est la balise ou la class de l’info que vous voulez récupérer vous pouvez la voir avec l’inspecteur de votre navigateur :

l’inspecteur pour trouver votre tag

Si vous exécutez ce script, vous pouvez voir qu’effectivement le print affiche ce que nous souhaitons mais avec beaucoup d’espace vide. Cela est dû au générateur de page HTML du site qui rajoute ces espaces vides (ce n’est pas le cas partout sur le web donc la solution sera à utiliser seulement pour des cas précis).

Pour remédier à cela, nous allons utiliser la méthode str.strip() de Python après avoir sauvegardé le texte dans une variable.

1
titre = h1.string.strip()

Récupérer les informations de plusieurs balises identiques

Ok c’est bien sympa tout ça, mais si je veux récupérer le titre de tous les tutoriels de la page je fais comment ?

Nous y venons jeune impatient !

Pour cela BeautifulSoup a une autre méthode qui va vous aider : find_all().

Donc nous allons utiliser le code suivant pour récupérer le titre et le descriptif de chaque tutoriel :

1
2
3
4
5
6
h3 = soup.find_all("h3", {"class": "content-title"})
desc = soup.find_all("p", {"class": "content-description"})

liste_titre = [elt.string.strip() for elt in h3]

liste_description = [elt.string.strip() for elt in desc]

find_all() nous fournit une liste qu’il faut parcourir avec une boucle for. À partir de cette boucle, nous avons donc pu stocker dans deux listes tous les titres et les descriptions des tutoriels de la page grâce aux compréhensions de liste de Python.

Ici nous avons utilisé des listes mais vous auriez aussi pu stocker ces données dans des dictionnaires avec comme clés les titres et comme valeurs les descriptions.

Une autre solution aurait été de stocker les deux valeurs dans une liste de tuples.

Optionnel : Extraire les données dans un fichier CSV

Vous avez maintenant toutes ces données, mais il va falloir pouvoir les "sortir" de votre script dans un format exploitable par le commun des mortels.

Pour cela nous allons utiliser la bibliothèque csv.

Il faut au préalable déclarer et ouvrir votre fichier csv en écriture avec un gestionnaire de contexte  :

1
2
with open("donnees.csv", "w", encoding="utf-8") as fichier
    writer = csv.writer(fichier)

Une fois cela fait vous pouvez grâce à une boucle inscrire dans votre csv les données.

1
2
3
4
5
6
7
j = len(liste_titre)
i = 0
with open("donnees.csv", "w", encoding="utf-8") as fichier
    writer = csv.writer(fichier)
    while i < j:
        writer.writerow((liste_titre[i], liste_description[i]))
        i+=1

Comme vous pouvez le voir, l’instruction pour écrire une ligne dans un CSV est :

1
writer.writerow(())

Cette double parenthèse est due au fait que nous utilisons un tuple en argument ici. Comme un tuple est noté à l’intérieur de parenthèse, il y a donc ici une double parenthèse. Vous pouvez en apprendre plus sur les tuples dans le tuto d’apprendre-python.com si le sujet vous intéresse.

Pour votre information cette méthode (writer.writerow) ne peut recevoir en argument qu’un itérable. Pour en savoir plus si cela vous intéresse je ne peux que vous recommander la documentation officielle sur le sujet.

Et voilà, vous avez un fichier CSV contenant tous les informations qui peut donc être importé dans tableur (Excel ou LibreOfficeCalc par exemple).


Vous savez désormais comment scraper facilement des données structurées dans une page HTML. Pour aller plus loin, je ne peux que vous recommander d’aller faire un tour sur la documentation officielle de BeautifulSoup.

Je tiens à remercier les membres du site qui m’ont aidé dans la rédaction / correction de ce billet, en particulier Victor.

13 commentaires

Pour ma part, j’utilise lxml.html, généralement suffisant et bien plus rapide. Et pour récupérer le contenu qui m’intéresse, XPATH est excellent.

yoch

Tu l’as vraiment remarqué en utilisation ? J’ai scrapé quelques milliers de pages HTML avec un programme et le temps d’analyse était clairement négligeable face au temps de téléchargement (qui était threadé).

J’avais fait des benchmarks, mais non, je ne l’ai pas vérifié en utilisation réelle, et il est fort possible que le gain soit finalement négligeable au regard du download. Mais bon, si je peux économiser en temps processeur pourquoi s’en priver, d’autant plus qu’avec le XPATH qui est très rapide lui aussi, on est gagnant à tous les niveaux.

Pour ma part, j’utilise lxml.html, généralement suffisant et bien plus rapide.

yoch

Ça dépend énormément, le "généralement suffisant". Le problème c’est que les pages web ont parfois du HTML loin d’être valide. lxml.html, à la base, c’est pour parser de l’HTML, donc un truc qui suit la spec HTML. Ils ont été assez futé pour prévoir que l’HTML serait cassé dans presque 100% des pages web, chez lxml, par contre ils gèrent que si c’est "un peu" cassé. Si ta page web est une daube, là BS s’en sortira vraiment mille fois mieux.

C’est aussi là qu’est la différence de vitesse. Au fond, si t’as lxml d’installé BS va l’utiliser derrière comme parseur sans te demander ton avis. Tu peux aussi explicitement dire à BS quel autre parseur utiliser.

+2 -0

La lib charge le DOM (la page) ou elle travaille uniquement sur le code source de la page ?

A-312

J’ai pas compris ta question. La lib parse le XML et propose un moyen facile de naviguer/récupérer des données dans la structure. Recherche par sélecteur quelconque, récupération du texte, d’attributs, etc.

tleb

Si vous parlez de BeautifulSoup elle parse pas du XML mais du HTML. C’est assez différent, la spec HTML n’étant pas compatible XML. Tu peux faire du HTML valide qui n’est pas du XML valide, comme l’inverse.

Donc ce que fait BeautifulSoup c’est parser de l’HTML environ, dans le sens où ça tente de comprendre l’intention même si le HTML est hyper pas valide et cassé et moisi et daubé. Ensuite elle t’expose un genre d’arbre d’éléments HTML via une API assez pratique.

+2 -0

Pour ma part, j’utilise lxml.html, généralement suffisant et bien plus rapide. Et pour récupérer le contenu qui m’intéresse, XPATH est excellent.

yoch

Tu l’as vraiment remarqué en utilisation ? J’ai scrapé quelques milliers de pages HTML avec un programme et le temps d’analyse était clairement négligeable face au temps de téléchargement (qui était threadé).

tleb

Moi je l’ai remarqué sur un système en prod qui doit parser plusieurs milliers de documents par seconde, à l’époque il a même fallu que je remplace BS par lxml directement dans le code parce qu’à l’usage BS n’apportait pratiquement aucune valeur par rapport à lxml, alors que lxml faisait économiser un facteur 12 en temps CPU.

En gros, oui j’ai senti la différence : avec BS le système ne tenait pas mes objectifs de perfs. Avec lxml, oui.

+0 -0
Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte