Selenium pilote un vrai navigateur. C'est toute sa raison d'être pour le scraping : quand une page construit son contenu avec JavaScript après le chargement initial du HTML, une simple requête HTTP vous donne un shell vide, mais un navigateur exécute les scripts et vous donne le DOM fini. Le coût est que vous faites maintenant tourner Chrome pour chaque page, ce qui est plus lent et plus lourd que de récupérer du HTML. Ce guide construit un scraper Selenium fonctionnel sur les outils actuels (Selenium 4), puis montre où faire tourner un navigateur vous-même cesse d'être rentable.

L'écart de version compte ici. La plupart des tutoriels Selenium enseignent encore les schémas Selenium 3 : find_element_by_id, binaires ChromeDriver téléchargés manuellement, arguments executable_path. Tout cela est supprimé ou déprécié. Selenium 4 a remplacé les helpers de recherche par une seule API de localisateur By, et Selenium Manager (intégré depuis la version 4.6) résout maintenant automatiquement le bon pilote pour votre navigateur installé, donc l'étape de téléchargement du pilote que la plupart des guides ouvrent avec n'existe plus. Tout ce qui suit est écrit pour cette configuration actuelle.

Ce dont vous avez besoin

Trois choses : Python 3.8 ou supérieur, Google Chrome installé, et le package Selenium. C'est tout. Vous n'avez pas besoin de télécharger ChromeDriver, et vous n'avez pas besoin de webdriver-manager sur une installation actuelle, parce que Selenium Manager s'occupe du pilote pour vous.

bash
# Selenium 4.6+ ships Selenium Manager, which resolves
# the matching driver for your installed Chrome.
pip install selenium
Ignorez webdriver-manager sur les nouveaux projets

Si vous avez vu webdriver-manager dans des guides plus anciens, vous n'en avez plus besoin. Il résolvait le même problème que Selenium Manager résout maintenant nativement. Gardez-le uniquement pour du code existant qui en dépend déjà ; pour tout nouveau projet, une installation de selenium seule suffit.

Lancer Chrome en mode headless

Selenium 4 configure le navigateur via un objet Options passé au pilote. Pour le scraping, vous voulez presque toujours le mode headless (pas de fenêtre visible), plus une vraie taille de fenêtre et un user agent, parce que certains sites se comportent différemment quand le viewport est minuscule ou que l'agent crie l'automatisation.

python
from selenium import webdriver
from selenium.webdriver.chrome.options import Options

options = Options()
options.add_argument("--headless=new")
options.add_argument("--window-size=1920,1080")
options.add_argument("--disable-gpu")
options.add_argument(
    "user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
    "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0 Safari/537.36"
)

# No driver path needed. Selenium Manager resolves it.
driver = webdriver.Chrome(options=options)

Si vous avez besoin de fixer un binaire de pilote spécifique, cela passe maintenant par un objet Service (webdriver.Chrome(service=Service("/path/to/chromedriver"), options=options)), pas par le mot-clé executable_path supprimé. Pour la plupart des gens, laisser Selenium Manager faire son travail est le bon choix.

Ouvrir une page et localiser des éléments

Avec un pilote en main, driver.get(url) navigue et bloque jusqu'au chargement initial. Ensuite vous trouvez des éléments avec l'API By. C'est le plus grand changement par rapport à Selenium 3 : chaque helper find_element_by_* a disparu, remplacé par find_element(By.X, value).

python
from selenium.webdriver.common.by import By

driver.get("https://quotes.toscrape.com/")

# One element, then many.
title = driver.find_element(By.TAG_NAME, "h1").text
quotes = driver.find_elements(By.CLASS_NAME, "quote")

for q in quotes:
    text = q.find_element(By.CLASS_NAME, "text").text
    author = q.find_element(By.CLASS_NAME, "author").text
    print(author, "-", text)

Les localisateurs à connaître sont By.ID, By.CLASS_NAME, By.CSS_SELECTOR, et By.XPATH. Les sélecteurs CSS couvrent la plupart des besoins et se lisent proprement ; utilisez XPath uniquement quand vous devez correspondre sur le contenu textuel ou remonter vers un parent, ce que CSS ne peut pas faire. Notez que find_element lève une exception si rien ne correspond, tandis que find_elements retourne une liste vide, donc bouclez sur la forme plurielle et vérifiez la longueur plutôt que d'envelopper les recherches simples dans des try/except.

Attendre le contenu dynamique

Voici l'erreur qui casse la plupart des scrapers Selenium débutants : appeler find_element à l'instant où get() retourne. Sur une page rendue par JavaScript, l'élément que vous voulez peut ne pas exister encore, et vous obtenez une NoSuchElementException sur une page qui se charge bien quand vous la regardez. La solution est une attente explicite, qui vérifie périodiquement jusqu'à ce qu'une condition soit vraie ou qu'un délai expire.

Ne recourez pas à time.sleep(). Un délai fixe soit gaspille du temps quand la page est rapide, soit échoue quand elle est lente ; une attente explicite retourne dès que l'élément est prêt et n'expire que s'il n'apparaît vraiment jamais.

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

wait = WebDriverWait(driver, 10)

# Wait until at least one .quote is present in the DOM.
wait.until(
    EC.presence_of_element_located((By.CLASS_NAME, "quote"))
)

# Now it is safe to read the rendered content.
quotes = driver.find_elements(By.CLASS_NAME, "quote")

Utilisez presence_of_element_located quand vous avez seulement besoin de l'élément dans le DOM, et visibility_of_element_located ou element_to_be_clickable quand vous vous apprêtez à lire du texte ou à cliquer. Le localisateur est passé comme tuple, ce qui est facile à rater : c'est ((By.CLASS_NAME, "quote")), un argument tuple, pas deux.

Gérer la pagination

La plupart des vraies cibles répartissent les données sur plusieurs pages. Le schéma est une boucle : scraper la page actuelle, trouver le contrôle de page suivante, cliquer dessus, attendre le nouveau contenu, répéter jusqu'à ce que le contrôle disparaisse. Le piège est que l'objet page devient périmé après la navigation, donc vous re-trouvez le bouton suivant à chaque itération plutôt que de tenir une référence à celui-ci.

python
from selenium.common.exceptions import NoSuchElementException

all_quotes = []

while True:
    wait.until(
        EC.presence_of_element_located((By.CLASS_NAME, "quote"))
    )
    for q in driver.find_elements(By.CLASS_NAME, "quote"):
        all_quotes.append(q.find_element(By.CLASS_NAME, "text").text)

    try:
        next_btn = driver.find_element(By.CSS_SELECTOR, "li.next a")
    except NoSuchElementException:
        break  # last page reached
    next_btn.click()

print("scraped", len(all_quotes), "quotes")
driver.quit()

Appelez toujours driver.quit() quand vous avez terminé. Cela ferme le navigateur et le processus pilote ; driver.close() ne ferme que la fenêtre actuelle et laisse le processus tourner, ce qui fait fuir des instances Chrome rapidement dans une boucle. Pour les pages à défilement infini plutôt qu'un bouton suivant, l'équivalent est de faire défiler avec driver.execute_script("window.scrollTo(0, document.body.scrollHeight)") dans une boucle et d'attendre que le nombre d'éléments cesse d'augmenter.

Router Selenium via un proxy

Scrapez n'importe quelle cible en volume depuis une IP et vous serez limité ou bloqué. Router le navigateur via un serveur proxy répartit les requêtes sur différentes adresses pour qu'aucune ne dépasse une limite. La forme la plus simple passe le proxy comme argument Chrome.

python
options = Options()
options.add_argument("--headless=new")
options.add_argument("--proxy-server=http://proxy.example.com:8080")

driver = webdriver.Chrome(options=options)
driver.get("https://httpbin.org/ip")
print(driver.find_element(By.TAG_NAME, "body").text)

Cela fonctionne pour un proxy sans authentification. Le problème : le flag --proxy-server de Chrome n'accepte pas de nom d'utilisateur et de mot de passe dans l'URL, donc un proxy avec identifiants nécessite une extension de navigateur qui injecte l'en-tête d'auth, ou un relais local qui détient les identifiants. C'est l'un des aspects les plus rugueux de piloter Chrome directement. Un endpoint rotatif qui s'authentifie par token contourne le problème : vous pointez Selenium vers un seul hôte et la rotation et la confiance vivent derrière lui. Les compromis entre gérer votre propre pool et utiliser un endpoint géré sont couverts dans proxy backconnect vs Crawling API, et si vos cibles sont renforcées, proxies datacenter vs résidentiels explique quel type d'IP vous avez réellement besoin.

Quand un navigateur n'est pas le bon outil

Selenium convient quand la page a vraiment besoin d'un navigateur : rendu côté client lourd, contenu conditionné à des interactions, ou flux de travail qui dépendent de clics et de saisies. C'est le mauvais outil quand vous l'utilisez par habitude. Si les données sont dans le HTML initial, requests plus un analyseur est d'un ordre de grandeur plus rapide et plus léger. Et une fois qu'une cible résiste avec de sérieuses défenses anti-bot, un navigateur headless brut se fait quand même prendre et bloquer, et vous finissez par reconstruire rotation, nouvelles tentatives, et discrétion à la main.

Approche Rend JS Vitesse / coût Meilleur pour
requests + analyseur Non Le plus rapide, le plus léger Données dans le HTML initial, sans JS
Selenium Oui (vrai navigateur) Lent, lourd Rendu JS, clics, formulaires, connexions
API de crawling gérée Oui (côté serveur) Une requête, pas d'infra Cibles renforcées, volume, sans flotte de navigateurs

La troisième ligne est là où atterrit la plupart du scraping en production. Une API de crawling gérée rend la page côté serveur, fait tourner les IPs, effectue de nouvelles tentatives sur les blocages, et vous donne du HTML fini depuis une seule requête, donc vous ne faites jamais tourner ni ne durcissez contre l'empreinte une flotte de navigateurs vous-même. Vous gardez Selenium pour le vrai travail d'interaction et déléguez le travail à fort volume qui se fait bloquer à l'API.

Crawlbase Crawling API

Quand la page a besoin de JavaScript mais que vous préférez ne pas faire tourner une flotte de navigateurs, la Crawling API la rend côté serveur, fait tourner les IPs, et effectue des nouvelles tentatives sur les blocages, retournant du HTML fini depuis une requête. Envoyez une URL avec &javascript=true et ignorez totalement l'infrastructure headless. Essayez-la sur le palier gratuit contre votre vraie cible.

L'appeler est une seule requête HTTP, sans pilote, sans attentes, sans danse d'authentification proxy : envoyez votre token et l'URL cible, demandez le rendu, et lisez le corps en retour.

python
import requests

resp = requests.get(
    "https://api.crawlbase.com/",
    params={
        "token": "_YOUR_TOKEN_",
        "url": "https://quotes.toscrape.com/js/",
        "javascript": "true",  # render the page server-side
    },
)
print(resp.status_code)
print(resp.text[:500])

Même résultat rendu que vous obtiendriez de Selenium, sans le navigateur, les attentes explicites, ou la solution de contournement de l'auth proxy. Pour une comparaison plus approfondie de gérer votre propre pool versus un endpoint qui possède tout le travail, voir les meilleurs proxies pour les web scrapers.

Récapitulatif

Points clés

  • Utilisez les schémas Selenium 4. L'API de localisateur By a remplacé chaque helper find_element_by_*, et Selenium Manager résout le pilote, donc l'étape de téléchargement manuel est terminée.
  • Configurez le navigateur via Options. Le mode headless, une vraie taille de fenêtre, et un user agent sont la base pour le scraping.
  • Attendez explicitement, ne dormez jamais. WebDriverWait plus expected_conditions retourne dès que le contenu est prêt et est la solution pour les conditions de course de rendu JS.
  • Re-trouvez les éléments après la navigation. Les références deviennent périmées entre les pages ; ré-interrogez le contrôle de page suivante à chaque boucle et appelez driver.quit() à la fin.
  • Un navigateur n'est pas toujours la réponse. Utilisez requests quand les données sont dans le HTML, et une API de crawling gérée quand les cibles sont renforcées ou que vous avez besoin de volume sans flotte de navigateurs.

Foire aux questions

Dois-je encore télécharger ChromeDriver pour Selenium 4 ?

Non. Depuis Selenium 4.6, le Selenium Manager intégré détecte votre Chrome installé et télécharge automatiquement le pilote correspondant, donc un simple pip install selenium suffit. Vous ne spécifiez un chemin de pilote via un objet Service que quand vous avez délibérément besoin de fixer un binaire particulier ; l'ancien mot-clé executable_path a été supprimé.

Pourquoi find_element lance-t-il NoSuchElementException sur une page qui se charge bien ?

Parce que l'élément est rendu par JavaScript après le chargement initial, et votre code a interrogé le DOM avant qu'il existe. Remplacez la recherche immédiate par une attente explicite : WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.CLASS_NAME, "quote"))). L'attente vérifie périodiquement jusqu'à ce que l'élément apparaisse ou que le délai expire, ce qui évite à la fois les délais fixes et les conditions de course.

Devrais-je utiliser Selenium ou BeautifulSoup pour le web scraping ?

Utilisez requests avec un analyseur comme BeautifulSoup quand les données sont présentes dans le HTML initial de la page ; c'est bien plus rapide et plus léger car cela ne démarre jamais un navigateur. Utilisez Selenium quand le contenu est rendu par JavaScript ou que vous avez besoin de cliquer, taper, faire défiler, ou vous connecter. De nombreux scrapers combinent les deux : Selenium rend la page, puis BeautifulSoup analyse le HTML résultant.

Comment utiliser un proxy authentifié avec Selenium ?

L'argument --proxy-server de Chrome n'accepte pas de nom d'utilisateur et de mot de passe dans l'URL, donc un proxy avec identifiants nécessite une extension de navigateur qui injecte l'en-tête d'auth ou un relais local qui détient les identifiants. Un endpoint rotatif authentifié par token évite le problème : vous pointez Selenium vers un seul hôte et les identifiants vivent derrière lui plutôt que dans les flags de lancement.

Selenium est-il bon pour le scraping à grande échelle ?

Il passe mal à l'échelle. Chaque page fait tourner un navigateur complet, qui est lent et gourmand en mémoire, et sur les cibles renforcées un navigateur headless brut se fait quand même prendre et bloquer. Pour un fort volume, une API de crawling gérée qui rend côté serveur, fait tourner les IPs, et effectue des nouvelles tentatives sur les blocages est généralement le meilleur choix, laissant Selenium pour le vrai travail à forte interaction.

Quelle est la différence entre driver.close() et driver.quit() ?

driver.close() ferme la fenêtre de navigateur actuelle mais laisse le processus pilote et toutes les autres fenêtres tourner. driver.quit() ferme chaque fenêtre et arrête le processus pilote. Dans une boucle de scraping, terminez toujours avec driver.quit(), sinon des processus Chrome orphelins s'accumulent et épuisent la mémoire.

Commencer à construire

Crawlez n'importe quel site à grande échelle, sans combattre l'infrastructure.

Crawlbase gère les proxies, les empreintes et les CAPTCHA afin que votre équipe livre des pipelines de données au lieu de maintenir la plomberie de crawl. 1 000 requêtes gratuites, sans carte requise.

En libre-service · Sans appel commercial requis · Volumes de crawl entreprise disponibles