À quoi ça sert

Quand tu fais requests.get(url), tu récupères le HTML tel qu'il sort du serveur. Sur les sites modernes (SPA React/Vue, infinite scroll, données chargées en Ajax…), ce HTML est presque vide : le contenu est généré par du JavaScript exécuté dans le navigateur.

Selenium contourne le problème en pilotant un vrai navigateur. Il ouvre Chrome (ou Firefox) en arrière-plan, charge la page, attend que le JavaScript fasse son travail, et te donne accès au DOM rendu. Tu peux alors :

  • Récupérer le HTML rendu et le passer à BeautifulSoup pour parsing.
  • Cliquer sur des boutons, remplir des formulaires, naviguer entre les pages.
  • Attendre qu'un élément précis apparaisse avant d'extraire les données.

À l'origine, Selenium est un outil de test end-to-end pour applications web. C'est ce que tu verras dans la doc officielle. Mais l'API est exactement la même pour le scraping : c'est le même besoin (piloter un navigateur), avec une intention différente.

En une phrase

Selenium pilote un navigateur réel pour scraper des pages dont le contenu n'apparaît qu'après exécution du JavaScript.

Selenium vs Playwright

Selenium est l'historique (2004) que tu croiseras dans la plupart des projets et tutos. Playwright (Microsoft, 2020) est l'alternative moderne : auto-wait intégré, API plus simple, plus rapide. Pour un nouveau projet, regarde Playwright. Pour comprendre l'existant ou suivre un cours, tu auras Selenium en face.

Un exemple d'usage

On veut récupérer les citations de quotes.toscrape.com/js/ — la version JavaScript du même site (les citations sont injectées par JS au chargement). Avec requests seul, on récupère une page sans citations. Avec Selenium, on attend le rendu et on lit ce que le navigateur affiche :

python
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from bs4 import BeautifulSoup

driver = webdriver.Chrome()
try:
    driver.get("https://quotes.toscrape.com/js/")

    # On attend qu'au moins une citation soit dans le DOM
    WebDriverWait(driver, 10).until(
        EC.presence_of_element_located((By.CSS_SELECTOR, ".quote"))
    )

    soup = BeautifulSoup(driver.page_source, "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}")
finally:
    driver.quit()

Le pattern type du scraping moderne : Selenium ouvre la page et exécute le JS, BeautifulSoup parse le résultat. On profite du meilleur des deux outils.

How-to : installer et utiliser Selenium

  1. Installer Selenium dans un venv

    Avec pip (ou UV) :

    bash
    python3 -m venv .venv
    source .venv/bin/activate
    pip install selenium beautifulsoup4
  2. Driver : plus rien à faire à la main depuis Selenium 4

    Pendant des années, il fallait télécharger chromedriver manuellement et le mettre dans le PATH. Depuis Selenium 4.6 (fin 2022), Selenium Manager est intégré : il détecte ton navigateur, télécharge le bon driver et le met en cache. Tu n'as plus rien à installer côté driver.

    Il faut quand même que Chrome (ou Firefox) soit installé sur ta machine — Selenium pilote un navigateur, il ne l'embarque pas.

  3. Premier script : ouvrir une page

    python
    from selenium import webdriver
    
    driver = webdriver.Chrome()
    driver.get("https://quotes.toscrape.com/js/")
    print(driver.title)
    driver.quit()

    Lance le script : une fenêtre Chrome s'ouvre, charge la page, et se referme. N'oublie jamais driver.quit(), sinon les processus Chrome s'accumulent en arrière-plan. Le mieux est d'envelopper le tout dans un try / finally.

  4. Sélectionner des éléments avec By

    Selenium fournit plusieurs stratégies de sélection. En 2026, By.CSS_SELECTOR est la plus pratique (même syntaxe que dans BeautifulSoup ou jQuery).

    python
    from selenium.webdriver.common.by import By
    
    # Un seul élément (lève une exception si rien)
    titre = driver.find_element(By.CSS_SELECTOR, "h1")
    print(titre.text)
    
    # Plusieurs éléments (liste, vide si rien)
    quotes = driver.find_elements(By.CSS_SELECTOR, ".quote")
    print(len(quotes))

    Autres stratégies utiles : By.ID, By.CLASS_NAME, By.XPATH, By.TAG_NAME. Mais By.CSS_SELECTOR couvre 95 % des cas.

  5. Attendre les éléments avec WebDriverWait

    Le piège classique du débutant : utiliser time.sleep(3) pour laisser la page charger. Ça marche… jusqu'à ce que le réseau soit lent un jour et que ton script casse. La bonne pratique est d'attendre un événement précis.

    python
    from selenium.webdriver.support.ui import WebDriverWait
    from selenium.webdriver.support import expected_conditions as EC
    
    # Attend jusqu'à 10 s qu'un .quote apparaisse
    WebDriverWait(driver, 10).until(
        EC.presence_of_element_located((By.CSS_SELECTOR, ".quote"))
    )

    WebDriverWait repose en boucle jusqu'à ce que la condition soit vraie (ou que le timeout expire). C'est plus rapide qu'un sleep aveugle, et beaucoup plus fiable.

    À retenir

    Si ton script semble fonctionner avec time.sleep() mais casse aléatoirement, c'est presque toujours un problème de timing. Remplace par un WebDriverWait.

  6. Mode headless (sans fenêtre)

    Pour scraper sans qu'une fenêtre Chrome s'ouvre à chaque exécution (utile sur un serveur, dans un script automatisé, ou juste pour la tranquillité) :

    python
    from selenium import webdriver
    
    opts = webdriver.ChromeOptions()
    opts.add_argument("--headless=new")
    opts.add_argument("--window-size=1920,1080")
    
    driver = webdriver.Chrome(options=opts)

    Pendant le développement, garde le mode visible pour voir ce qui se passe. Une fois que ton script tourne, passe en headless.

  7. Combiner avec BeautifulSoup

    Selenium est très bien pour piloter le navigateur, mais BeautifulSoup est plus pratique pour parser méthodiquement de gros volumes de HTML. Le combo gagnant :

    python
    from bs4 import BeautifulSoup
    
    # Selenium charge et exécute le JS
    driver.get(url)
    WebDriverWait(driver, 10).until(...)
    
    # BeautifulSoup parse le HTML rendu
    soup = BeautifulSoup(driver.page_source, "html.parser")
    for bloc in soup.select(".quote"):
        ...
  8. Bonnes pratiques de scraping

    • Espace tes requêtes avec time.sleep() entre les pages — ne martèle pas un site.
    • Lis le robots.txt et respecte les conditions d'utilisation.
    • User-Agent honnête via opts.add_argument("user-agent=…").
    • Toujours driver.quit() dans un finally pour ne pas laisser de processus Chrome zombies.
    • Préfère l'API officielle du site si elle existe — c'est plus fiable et c'est gratuit pour le serveur en face.

Aide-mémoire

python
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

# Démarrer / arrêter
driver = webdriver.Chrome()
driver.quit()

# Naviguer
driver.get(url)
driver.back()
driver.refresh()

# Sélectionner
driver.find_element(By.CSS_SELECTOR, ".x")
driver.find_elements(By.CSS_SELECTOR, ".x")

# Interagir
el.click()
el.send_keys("texte")
el.clear()
el.text                      # texte affiché
el.get_attribute("href")

# Attendre
WebDriverWait(driver, 10).until(
    EC.presence_of_element_located((By.CSS_SELECTOR, ".x"))
)
EC.visibility_of_element_located(...)
EC.element_to_be_clickable(...)

# HTML rendu (à passer à BS4)
driver.page_source

# Options Chrome
opts = webdriver.ChromeOptions()
opts.add_argument("--headless=new")
opts.add_argument("--window-size=1920,1080")
driver = webdriver.Chrome(options=opts)

Pour aller plus loin