À quoi ça sert

Quand on veut extraire des données d'une page web (titres, prix, liens, tableaux…), on procède toujours en deux temps :

  1. Récupérer le HTML de la page (avec requests, Selenium ou Playwright).
  2. Le parser pour aller chercher ce qui nous intéresse — c'est là que BeautifulSoup entre en jeu.

BeautifulSoup (souvent appelé BS4 car la version actuelle est la 4) prend une chaîne de HTML et renvoie une structure Python navigable : tu peux demander « tous les <a> dont la classe est btn » ou « le premier <h1> dans la div d'id main » en quelques lignes.

En une phrase

BeautifulSoup transforme une soupe de HTML en arborescence Python où on peut piocher avec des sélecteurs CSS ou des recherches par balise.

BS4 ne télécharge pas la page

BeautifulSoup ne fait que parser du HTML qu'on lui donne. Pour récupérer une page, il faut un autre outil : requests pour les pages statiques, Selenium ou Playwright pour celles dont le contenu n'apparaît qu'après exécution du JavaScript.

Un exemple d'usage

On veut récupérer toutes les citations affichées sur la page d'accueil de quotes.toscrape.com (un site fait exprès pour s'entraîner au scraping). Avec requests + BS4, ça tient en une dizaine de lignes :

python
import requests
from bs4 import BeautifulSoup

r = requests.get("https://quotes.toscrape.com")
soup = BeautifulSoup(r.text, "html.parser")

for bloc in soup.select(".quote"):
    texte = bloc.select_one(".text").get_text(strip=True)
    auteur = bloc.select_one(".author").get_text(strip=True)
    print(f"{auteur} — {texte}")

Tu obtiens 10 citations affichées dans la console. Si la page utilisait du JavaScript pour charger les citations (c'est le cas de quotes.toscrape.com/js/), requests ne verrait qu'une coquille HTML vide : il faudrait alors passer par Selenium ou Playwright pour exécuter le JS, puis donner le HTML rendu à BS4.

How-to : installer et utiliser BeautifulSoup

  1. Créer un venv et installer les paquets

    BeautifulSoup s'installe avec pip (ou UV). On installe aussi requests pour télécharger les pages :

    bash
    python3 -m venv .venv
    source .venv/bin/activate
    pip install beautifulsoup4 requests
    Astuce

    Le paquet s'appelle beautifulsoup4 mais on l'importe avec from bs4 import BeautifulSoup. C'est un piège classique des débutants.

  2. Charger une page

    On télécharge le HTML avec requests :

    python
    import requests
    
    r = requests.get("https://quotes.toscrape.com")
    r.raise_for_status()  # lève une exception si HTTP >= 400
    html = r.text

    raise_for_status() est ta sécurité : si le site renvoie 404 ou 500, ton script s'arrête net au lieu de parser une page d'erreur.

  3. Parser le HTML

    python
    from bs4 import BeautifulSoup
    
    soup = BeautifulSoup(html, "html.parser")

    "html.parser" est le parser intégré à Python — suffisant dans 95 % des cas. Pour des HTML cassés ou du XML, tu peux installer lxml et passer "lxml" à la place.

  4. Sélectionner avec find et find_all

    L'API historique : on cherche par balise et par attributs.

    python
    # Le premier <h1> de la page
    titre = soup.find("h1")
    
    # Tous les <a> dont la classe est "tag"
    tags = soup.find_all("a", class_="tag")
    
    # Le bloc avec id="main"
    main = soup.find(id="main")

    Note bien class_ avec un underscore : class est un mot réservé en Python.

  5. Sélectionner avec select (sélecteurs CSS)

    Souvent plus pratique : tu utilises la même syntaxe que dans une feuille de style. Si tu sais inspecter une page avec les DevTools du navigateur, tu sais déjà écrire un sélecteur CSS.

    python
    # Tous les blocs de classe "quote"
    quotes = soup.select(".quote")
    
    # Le premier h1 dans la div d'id "main"
    titre = soup.select_one("#main h1")
    
    # Tous les liens dans une nav
    liens = soup.select("nav a")

    select renvoie une liste, select_one renvoie le premier match (ou None si rien ne correspond).

  6. Extraire le texte et les attributs

    python
    lien = soup.select_one("a.next")
    
    # Texte affiché
    print(lien.get_text(strip=True))
    
    # Attribut href (deux écritures équivalentes)
    print(lien["href"])
    print(lien.get("href"))  # renvoie None si l'attribut manque, plus sûr

    get_text(strip=True) nettoie au passage les espaces et retours à la ligne inutiles — pratique parce que le HTML brut en contient toujours.

  7. Combiner avec Selenium ou Playwright

    Pour les pages dont le contenu est généré par JavaScript, le pattern le plus pratique est de récupérer le HTML rendu par le navigateur et de le donner à BS4 :

    python
    # Avec Selenium
    soup = BeautifulSoup(driver.page_source, "html.parser")
    
    # Avec Playwright
    soup = BeautifulSoup(page.content(), "html.parser")

    On laisse le navigateur s'occuper du JS, et BS4 fait ce qu'il fait de mieux : extraire les données. Voir les fiches Selenium et Playwright pour le détail.

  8. Bonnes pratiques de scraping

    • Lis le robots.txt du site (https://exemple.com/robots.txt) et respecte ses interdictions.
    • Identifie ton script avec un User-Agent honnête : requests.get(url, headers={"User-Agent": "MonProjet/0.1 contact@email.fr"}).
    • Espace tes requêtes avec time.sleep(1) entre chaque page : tu n'es pas pressé, le serveur en face oui.
    • Vérifie les conditions d'utilisation du site avant de scraper massivement, et préfère l'API officielle si elle existe.
    • Mets en cache ce que tu as déjà téléchargé pendant le développement, pour ne pas frapper le site à chaque exécution.

Aide-mémoire

python
from bs4 import BeautifulSoup

soup = BeautifulSoup(html, "html.parser")

# Recherche par balise et attributs
soup.find("h1")                       # 1er match ou None
soup.find_all("a", class_="btn")        # liste
soup.find(id="main")
soup.find("a", attrs={"data-id": "42"})

# Sélecteurs CSS (souvent plus simple)
soup.select(".quote .author")
soup.select_one("#main h1")
soup.select("a[href^='/page/']")        # href commence par "/page/"

# Extraire
el.get_text(strip=True)             # texte propre
el["href"]                          # attribut (KeyError si absent)
el.get("href")                       # attribut (None si absent)
el.attrs                              # tous les attributs

# Naviguer dans l'arbre
el.parent
el.children                           # itérable
el.next_sibling, el.previous_sibling

Pour aller plus loin