Web scraping avec BeautifulSoup

Temps de lecture : 7 minutes

Oui, je te l’accorde tout de suite : le titre a l’air quelque peu barbare 😅 ! Mais reste avec moi, je t’explique rapidement ce que nous allons réaliser dans cet article. Nous allons écrire un script Python qui ira « gratter » (scrape en anglais) la page web de l’Université Paris-Panthéon-Assas pour récupérer les adresses email de toutes ses associations étudiantes, toutes. Pour réaliser notre mission XXL, nous allons nous servir d’une bibliothèque Python se prénommant joliment BeautifulSoup . Tu vois, le titre n’a finalement pas pas grand chose de mystique 😉 !

Script de web scraping en action

J’aurais bien aimé te dire qu’on est dans un spin-off de You et que je suis un charmant libraire avec un penchant pervers pour les emails… surtout ceux d’Assas ! Mais tu vois, ce serait trop beau ! Cette fois-ci, notre cas d’étude répond à une problématique réelle…

Contexte général et problématique

J’ai récemment lancé une application mobile d’économie circulaire qui te permet de créer des espaces personnalisés et protégés pour échanger des biens et services dans tes cercles proches (immeuble, école, entreprise … etc). Parait-il que le concept est top, tu devrais aller regarder… Mais restons concentrés sur notre problématique actuelle 😛. Pour les besoins de communication de l’application, j’ai voulu réaliser un sondage auprès de quelques associations étudiantes à Paris. Certaines universités donnent accès publiquement aux adresses email de leurs associations étudiantes sur leur site web… et devine quoi, Assas en fait partie (oui, tout s’explique). Je pouvais donc copier-coller les adresses dans ma boite Gmail et envoyer mon sondage. Quel est le problème alors ?

Bonne question ! Figure-toi que Assas a environ 300 associations listées sur son site web 😅. Pour récupérer l’email d’une association, il faut cliquer dessus, puis une autre page s’ouvre, et ce n’est qu’en ce moment que tu peux effectuer ton copier-coller. Tu vois le problème venir ? Au bout de 5 emails récupérés avec cette mécanique, ma souris a commencé à s’ennuyer et mon instinct paresseux de développeur a pris le dessus.


Dans mon monde tout rose, je voudrais tout simplement récupérer mes 300 emails en une minute et en un clic idéalement ! J’ai pensé à embaucher un stagiaire mais parait-il que ça ne se fait pas trop de ne pas les payer… Et comme je ne parle plus à mon banquier, je me suis tourné vers mon éditeur de code pour me générer un bébé stagiaire… Comment procède-t-on ? Je te dis tout 😃

BeautifulSoup et le principe du scraping

Pour te la faire simple, BeautifulSoup est une bibliothèque de Python qui te permet de lire le contenu d’une page web et éventuellement de récupérer des informations d’intérêt (nos adresses email par exemple). Bien sûr, étant donné que nous ne sommes pas dans Hogwarts Legacy, aucune magie ne s’opère 😞! BeautifulSoup se contente de nous servir le contenu de la page web et il nous revient de lui dire quelle information aller récupérer et où aller la récupérer. Pour répondre à ces deux dernières questions, il faut tout simplement connaitre la structure de la page web.

De fait, le visuel que tu as sur n’importe quelle page web n’est qu’une succession de balises HTML. Ne te casse pas la tête avec ces nouveaux mots. En gros, vois ça juste comme des tags qui disent à ta page web de disposer telle information à tel endroit, d’aller sur telle nouvelle page si on clique sur tel mot… etc. Une fois donc que tu as accès à ce code HTML, tu as toute la structure de ta page entre les mains. Mais comment accède-t-on au code HTML de notre page ? Simple 😁! On fait un clic droit sur la page et on sélectionne « Inspecter », c’est tout ! N’est-ce pas génial ça ?

Et cerise sur le gâteau, si tu regardes bien la vidéo d’inspection de la page, tu peux remarquer que chaque fois qu’on se place sur une ligne de code HTML (à droite), le navigateur nous surligne la ligne à laquelle elle correspond sur la page (à gauche). Les noms des associations sont encapsulés par exemple dans des <div class = 'views-rows....></div>. Super 🙂! Nous connaissons désormais la structure de notre page. Et si on rentrait dans le vif du code ?

Récupération de l’adresse email de la première association sur la page

Ne cassons pas tout de suite la baraque en allant récupérer toutes les adresses email, on le fera juste après. Pour maîtriser les bases, commençons par récupérer uniquement celle de la première association de la page : « Association Master 2 Sécurité et Défense (SECUDEF Paris 2) ». C’est parti !

Installation des bibliothèques

Niveau environnement, il faut commencer par installer les bibliothèques nécessaires. C’est simple sur Python. Il suffit d’ouvrir la ligne de commande et de taper pip install nom_de_la_bibliothèque. En l’occurrence, nous aurons besoin d’installer :

  • beautifulsoup4 : ça tu l’as peut-être deviné, cela fait un petit moment qu’on en parle 😅. Le 4, c’est juste pour signifier qu’on veut la version 4
  • requests : tu te souviens quand je te parlais de la structure HTML de la page web ? Je te l’ai montrée graphiquement sur le navigateur mais si on travaille en ligne de code, on a besoin de cette bibliothèque pour récupérer toute la structure HTML de la page
  • lxml : celle-ci est un peu technique. Je ne vais pas rentrer dans les détails mais c’est juste un intermédiaire pour améliorer la mise en forme du code HTML qu’on récupère avec notre bibliothèque requests
Que la fête commence !

Maintenant que nous avons installé nos bibliothèques, commençons par les importer :

from bs4 import BeautifulSoup
import requests

Remarque, nous n’avons pas besoin d’importer lxml. Python, le malin va l’utiliser par défaut 🙂. Bon go, allons récupérer la structure HTML de notre page web https://www.u-paris2.fr/fr/campus/associations/associations-etudiantes. On utilisera notre charmante bibliothèque requests.

html_text = requests.get("https://www.u-paris2.fr/fr/campus/associations/associations-etudiantes").text

Tu peux t’amuser à afficher le résultat contenu dans html_text. C’est juste le code/structure HTML de la page en vrac donc je te préviens, c’est assez moche ! Mais tu sais ce qu’on dit, la vraie beauté vient de l’intérieur 😐. Nous allons désormais passer cette structure à beautifulSoup pour qu’elle nous génère la délicieuse soupe dans laquelle nous irons piocher nos informations après.

soup = BeautifulSoup(html_text, 'lxml')

Nous avons passé lxml en argument pour améliorer la mise en forme de notre « soupe ». Si tu affiches soup, tu devrais avoir une structure beaucoup plus élégante que précédemment. Mais pour l’instant, laissons l’élégance aux défilés Balmain et contentons-nous d’aller chercher notre adresse email 😅.

Pour ce faire, je te rappelle la mécanique manuelle pour récupérer l’adresse email de SECUDEF. Dans un premier temps, il faut cliquer sur le lien de l’association sur notre page actuelle. Le clic nous ramène sur une deuxième page et c’est sur cette dernière que nous pouvons récupérer l’adresse email.

Jusqu’ici, avec soup nous sommes sur la première page. Si tu inspectes la page et que tu essaies de retrouver la localisation de notre association SECUDEF dans le code HTML , tu devrais retrouver ceci :

Notre première association SECUDEF se trouve donc dans une balise <div></div> avec une propriété class = "views-row views-row-1 views-row-odd views-row-first". Et si tu regardes ce qu’il y a dans le corps, tu peux remarquer qu’il y a notre lien vers la deuxième page sur laquelle nous pouvons récupérer notre email. C’est dans la balise <a></a> avec la propriété href. Il ne nous reste donc qu’à coder la récupération de ce lien contenu dans href et à aller scraper la deuxième page.

asso_secudef = soup.find('div', class_ = 'views-row views-row-1 views-row-odd views-row-first')
balise_lien_secudef = asso_secudef.a
lien_secudef = balise_lien_secudef['href']

J’utilise la méthode find pour trouver notre balise div avec la propriété class_ = "views-row views-row-1 views-row-odd views-row-first". Après, je vais chercher la balise a qui contient le lien vers la deuxième page. Une fois que j’ai cette balise, je n’ai plus qu’à chercher l’attribut href pour récupérer mon lien.

Bon, ça fait peut-être beaucoup d’informations d’un seul coup mais amuse-toi à afficher les résultats intermédiaires. Tu verras, c’est juste une petite question de logique 😉.

Scraping de la page contenant l’adresse email de SECUDEF

Nous avons donc réussi à récupérer le lien vers la page qui contient l’adresse email de SECUDEF. Tu l’as peut-être deviné, c’est rebelote. On rapplique la même logique depuis la récupération de la structure HTML de la page jusqu’à la récupération de la balise qui contient l’adresse email. Je te laisse faire ça comme exercice 😉. Mais en gros, tu devrais avoir :

lien_complet = 'https://www.u-paris2.fr' + lien_secudef
html_text2 = requests.get(lien_complet).text
soup2 = BeautifulSoup(html_text2, 'lxml')
balise_email = soup2.find("div", class_ = 'mail')
text_email = balise_email.text
email = text_email.split()[-1]

La logique est donc globalement la même que précédemment. Une fois que j’ai retrouvé la balise contenant l’adresse email, j’ai recupéré le texte contenu dedans en lui appliquant la méthode .text. Si tu affiches le contenu de text_email, tu devrais avoir 'Courriel : secudef.paris2@gmail.com' exactement comme affiché dans le navigateur.

Après, il s’agit simplement d’utiliser la méthode split pour conserver uniquement l’adresse email. Et voilà comment on récupère l’adresse de notre première association de la page 🎉.

Parce que j’ai détaillé toutes les étapes du raisonnement, ça peut peut-être paraître long mais en soi ça tient sur quelques lignes :

# Première page
html_text = requests.get("https://www.u-paris2.fr/fr/campus/associations/associations-etudiantes").text
soup = BeautifulSoup(html_text, 'lxml')
asso_secudef = soup.find('div', class_ = 'views-row views-row-1 views-row-odd views-row-first')
balise_lien_secudef = asso_secudef.a
lien_secudef = balise_lien_secudef['href']

# Deuxième page
lien_complet = 'https://www.u-paris2.fr' + lien_secudef
html_text2 = requests.get(lien_complet).text
soup2 = BeautifulSoup(html_text2, 'lxml')
balise_email = soup2.find("div", class_ = 'mail')
text_email = balise_email.text

email = text_email.split()[-1]

Mais bien sûr, si c’est pour récupérer une seule adresse email, c’est de l’overkill. L’approche prend tout son intérêt quand on ira récupérer tous les emails de toutes les associations listées sur la page.

Récupération des adresses email de toutes les associations listées

La logique appliquée pour la première association de la page reste évidemment la même pour toutes les autres associations listées. Il suffit donc de récupérer tous les <div> d’associations et de tous les parcourir pour aller récupérer leur email. Si tu ré-inspectes la structure HTML de la première page, tu remarqueras que pour toutes les balises contenant les associations, la propriété class commence par "views-row views-row". Bingo, nous avons trouvé le point commun 🤩! Il ne reste qu’à utiliser la méthode find_all pour aller toutes les récupérer.

Ton code final pour avoir les 290 adresses email devrait donc en principe ressembler à ceci :

## Paris-Assas
def stringStartsWith(myStr):
    return myStr is not None and myStr.startswith('views-row views-row')

html_text = requests.get("https://www.u-paris2.fr/fr/campus/associations/associations-etudiantes").text
soup = BeautifulSoup(html_text, 'lxml')
assos = soup.find_all('div', class_ = stringStartsWith)

emails = []
for i in range(len(assos)):
    extension = assos[i].a['href']
    lien_complet = 'https://www.u-paris2.fr' + extension
    html_text2 = requests.get(lien_complet).text
    soup2 = BeautifulSoup(html_text2, 'lxml')
    balise_email = soup2.find("div", class_ = 'mail')
    text_email = balise_email.text
    if (len(text_email) > 0):
        email = text_email.split()[-1]
        emails.append(email)
        print (email)

Bon, j’ai rajouté quelques lignes pour gérer les exceptions mais tu devrais te retrouver facilement 🙂. Cet article est suffisamment long comme ceci donc arrêtons-nous là 🙈! Va en paix spammer des gens… avec modération sinon c’est avec Gmail que tu auras des problèmes… (oui c’est du vécu)

Partager

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *