Retour au blog
Guides
Robert SfichiLast updated on Apr 29, 202634 min read

Web Scraping avec Selenium : Tutoriel Python pas à pas

Web Scraping avec Selenium : Tutoriel Python pas à pas
En bref : Selenium vous permet d'extraire des données de sites web riches en JavaScript en pilotant un navigateur réel à partir de code Python. Ce tutoriel vous guide à travers toutes les étapes : installation de Selenium, configuration de Chrome, localisation et interaction avec les éléments, gestion des délais d'attente et de la pagination, exportation de données propres, et mise à l'échelle de votre scraper à l'aide de proxys, de Selenium Grid et d'alternatives basées sur des API.

Selenium est un framework d'automatisation de navigateur qui contrôle une instance de navigateur réel (Chrome, Firefox, Edge et autres) via du code. Bien qu'il ait été initialement conçu pour tester des applications web, il est devenu l'un des outils les plus utilisés pour le scraping web avec Selenium, en particulier sur les sites où JavaScript affiche le contenu dont vous avez besoin.

Si vous avez déjà essayé de scraper une application monopage ou un flux à défilement infini avec requests et BeautifulSoup, vous connaissez déjà le problème : le code HTML que vous téléchargez est une coquille vide. Les données réelles se chargent après l'exécution de JavaScript, et un simple client HTTP n'exécute jamais ce JavaScript. Selenium résout ce problème en lançant un navigateur complet, en chargeant la page exactement comme le ferait un visiteur humain, puis en vous donnant un accès programmatique au DOM résultant.

Ce tutoriel couvre toutes les étapes pratiques du scraping Web avec Selenium en Python : configuration de l'environnement, stratégies de localisation des éléments, attente du contenu dynamique, défilement, pagination, exportation des données, intégration de proxy et optimisation des performances. À la fin, vous disposerez d'un scraper fonctionnel de bout en bout et d'une vision claire des cas où Selenium est le bon choix par rapport à des alternatives plus légères.

Qu'est-ce que Selenium et pourquoi l'utiliser pour le web scraping ?

Selenium a vu le jour en 2004 en tant que framework de test et a évolué au fil de plusieurs versions majeures depuis lors. Aujourd’hui, Selenium WebDriver communique avec les navigateurs via le protocole W3C WebDriver, une API standardisée que tous les principaux éditeurs de navigateurs implémentent en natif. Vous écrivez des instructions en Python (ou en Java, JavaScript, Ruby, C# et plusieurs autres langages), et WebDriver traduit ces instructions en actions au sein d’une véritable session de navigateur. Le navigateur affiche le code HTML, exécute le JavaScript, applique le CSS et envoie des requêtes réseau, exactement comme le ferait un utilisateur devant son clavier.

C'est précisément ce pipeline de rendu complet qui rend Selenium si précieux pour le scraping. Les bibliothèques HTTP traditionnelles telles que requests récupèrent le document HTML brut renvoyé par le serveur. Si la page s'appuie sur du JavaScript côté client pour remplir une grille de produits, charger des avis ou assembler un tableau de bord, vous n'obtenez aucune de ces données. Selenium, en revanche, attend que le JavaScript s'exécute et vous fournit le DOM entièrement rendu.

Selenium prend également en charge tous les principaux navigateurs (Chrome, Firefox, Edge, Opera, Safari) et fonctionne sur tous les systèmes d'exploitation. Cette compatibilité multi-navigateurs est importante lorsque le comportement d'un site cible varie en fonction du moteur du navigateur. Et comme Selenium imite le comportement réel d'un utilisateur (clics, saisie, défilement), il peut naviguer dans des flux de travail interactifs que les scrapers statiques ne peuvent tout simplement pas atteindre.

Le compromis réside dans le coût en ressources. L'exécution d'un navigateur complet nécessite de la puissance CPU et de la mémoire vive, et elle est nettement plus lente que l'envoi d'une requête HTTP légère. Selenium a été conçu comme un outil de test, de sorte que certaines de ses abstractions (la surcharge du protocole WebDriver, l'absence de fonction d'attente automatique intégrée) constituent un frein pour les cas d'utilisation purement liés au scraping. Cela dit, sa communauté massive, sa documentation exhaustive et sa large prise en charge des langages en font l'un des points de départ les plus sûrs pour quiconque se lance dans l'extraction de données via un navigateur. Nous aborderons les considérations de performance et les alternatives plus loin dans ce tutoriel.

Quand Selenium est (ou n'est pas) le bon outil de scraping

Selenium est le bon choix lorsque la page que vous devez scraper nécessite l'exécution de JavaScript pour afficher son contenu. Pensez aux applications monopages construites avec React, Angular ou Vue ; aux sites protégés par des formulaires de connexion ; aux pages à défilement infini ; et aux tableaux de bord où les données se chargent via des appels AJAX après le chargement initial de la page.

Ce n'est pas le meilleur choix pour l'exploration à grande échelle de pages HTML statiques. Pour ces tâches, requests associé à BeautifulSoup ou à un framework comme Scrapy, sera plus rapide, moins gourmand en mémoire et plus facile à faire évoluer. La comparaison entre Scrapy et Selenium se résume à savoir si vous avez réellement besoin d'un navigateur. Les bibliothèques d'automatisation de navigateur plus récentes, telles que Playwright et Puppeteer, méritent également d'être prises en considération si vous recherchez des API asynchrones modernes dotées de fonctionnalités d'attente automatique intégrées.

Voici une liste de contrôle rapide pour vous aider à prendre une décision :

  • HTML statique, pas besoin de JS : utilisez requests + BeautifulSoup.
  • Exploration à grande échelle, pages statiques : utilisez Scrapy.
  • Pages rendues en JS, équipe multilingue : utilisez Selenium.
  • Pages rendues par JS, Python/JS uniquement, vous voulez une API moderne : envisagez Playwright.

Une règle empirique pratique : commencez par l'outil le plus léger qui fonctionne. Si requests récupère les données dont vous avez besoin, arrêtez-vous là. Si le contenu est rendu par JavaScript et que vous souhaitez une large prise en charge des langages ainsi qu’un écosystème mature, le web scraping avec Selenium est un bon compromis.

Prérequis et configuration de l'environnement

Avant d'écrire la moindre ligne de code de scraping, vous devez avoir installé trois éléments : Python 3.8 ou une version ultérieure, le package Selenium et un pilote de navigateur correspondant à la version de votre navigateur.

Installer Python et Selenium

Ouvrez un terminal et vérifiez votre version de Python :

python --version

Installez ensuite Selenium via pip :

pip install selenium

À partir de la version 4.6, Selenium est fourni avec un outil intégré appelé selenium-manager qui peut télécharger et configurer automatiquement le pilote de navigateur approprié pour vous. Dans les versions antérieures, vous deviez télécharger manuellement ChromeDriver (ou GeckoDriver pour Firefox) et l'ajouter au PATH de votre système. Si vous utilisez Selenium 4.6 ou une version ultérieure, vous pouvez généralement éviter la gestion manuelle des pilotes, mais il est recommandé de vérifier que vos versions de Chrome et de ChromeDriver correspondent si vous rencontrez des erreurs de lancement.

Vérifiez l'installation

Créez un script de test rapide pour vérifier que tout fonctionne :

from selenium import webdriver

driver = webdriver.Chrome()
driver.get("https://example.com")
print(driver.title)  # Should print "Example Domain"
driver.quit()

Si une fenêtre Chrome s'ouvre, charge la page et affiche le titre dans votre console, votre environnement est prêt. Si ce n'est pas le cas, vérifiez que Chrome est bien installé et que votre version de Selenium correspond à la version majeure de Chrome sur votre machine.

Environnements virtuels

Il est recommandé de travailler dans un environnement virtuel afin que Selenium et ses dépendances n'entrent pas en conflit avec d'autres projets :

python -m venv scraper-env
source scraper-env/bin/activate   # On Windows: scraper-env\Scripts\activate
pip install selenium beautifulsoup4 pandas

BeautifulSoup gère l'analyse rapide du code HTML une fois que Selenium a récupéré le code source de la page affichée, et pandas facilite le nettoyage des données et l'exportation au format CSV. Ces deux outils sont facultatifs, mais ils sont fréquemment utilisés dans les scénarios réels de web scraping avec les workflows Selenium Python ; les installer dès le départ vous fera donc gagner du temps.

À ce stade, votre environnement est prêt. L'étape suivante consiste à configurer Chrome pour qu'il se comporte comme vous le souhaitez pendant une session de scraping.

Configuration des options de Chrome pour le scraping

Selenium lance Chrome avec ses paramètres par défaut, mais ceux-ci ne sont pas idéaux pour le scraping. La ChromeOptions classe vous permet de personnaliser la session du navigateur avant qu'elle ne démarre.

from selenium import webdriver

options = webdriver.ChromeOptions()
options.add_argument("--headless=new")          # Run without a visible window
options.add_argument("--disable-gpu")           # Prevents GPU-related issues in headless
options.add_argument("--window-size=1920,1080") # Consistent viewport for element visibility
options.add_argument("--no-sandbox")            # Required in some CI/Docker environments
options.add_argument("--disable-dev-shm-usage") # Avoids shared memory issues in containers
options.add_argument("user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
                     "AppleWebKit/537.36 (KHTML, like Gecko) "
                     "Chrome/125.0.0.0 Safari/537.36")

driver = webdriver.Chrome(options=options)

Le mode sans interface graphique exécute Chrome sans afficher de fenêtre graphique. Cela réduit l'utilisation des ressources et accélère l'exécution, ce qui est important lorsque vous exécutez des scrapers sur un serveur ou dans un pipeline d'intégration continue. Le --headless=new drapeau a remplacé l'ancien --headless dans Chrome 109 et offre une meilleure parité des fonctionnalités avec le mode avec interface graphique. Pour en savoir plus sur les navigateurs sans interface graphique et savoir quand les utiliser, un guide dédié à l'architecture des navigateurs sans interface graphique constitue une référence utile.

Définir une chaîne user-agent personnalisée permet à vos requêtes de se fondre dans le trafic normal du navigateur au lieu de révéler qu’elles proviennent d’un outil automatisé. Associez cela à une taille de fenêtre réaliste afin que les mises en page adaptatives affichent la version bureau de la page que vous comptez analyser.

Parmi les autres indicateurs utiles, on peut citer --disable-extensions (qui ignore le chargement des extensions qui allongent le temps de démarrage), --disable-infobars (masque la bannière « Chrome est contrôlé »), et --disable-notifications (bloque les fenêtres contextuelles susceptibles de masquer des éléments). Ensemble, ces indicateurs créent un profil de navigateur allégé, optimisé pour l'extraction de données plutôt que pour la navigation interactive.

Lancer un navigateur et accéder à une URL

Une fois vos options configurées, le démarrage d'un navigateur et la visite d'une page ne nécessitent que deux lignes :

driver = webdriver.Chrome(options=options)
driver.get("https://books.toscrape.com/")

driver.get() bloque jusqu'à ce que le navigateur déclenche son load , ce qui signifie que le HTML, le CSS et le JavaScript synchrone initiaux ont fini de se charger. Il n'attend pas les appels AJAX qui se déclenchent après l'événement de chargement, vous devrez donc peut-être encore attendre explicitement le contenu injecté dynamiquement (ce point est abordé dans la section sur les attentes ci-dessous).

Quelques propriétés utiles juste après la navigation :

print(driver.title)        # Page <title> text
print(driver.current_url)  # Final URL after any redirects

Appelez toujours driver.quit() lorsque vous avez terminé, soit dans un finally ou en utilisant Selenium comme gestionnaire de contexte. Laisser les processus du navigateur en cours d'exécution entraînera rapidement des fuites de mémoire, en particulier dans les boucles. Voici un exemple de modèle simple :

try:
    driver = webdriver.Chrome(options=options)
    driver.get("https://example.com")
    # ... scraping logic ...
finally:
    driver.quit()

Cela garantit que le navigateur s'arrête même si votre code lève une exception en cours d'exécution. Vous familiariser tôt avec ce type de gestion des ressources vous évitera que des processus Chrome orphelins ne grignotent la mémoire de votre serveur en production. Sur une machine de développement, vérifiez régulièrement votre gestionnaire de tâches pour vous assurer qu'aucun chrome ou chromedriver processus obsolètes provenant de scripts qui ont planté.

Localisation des éléments sur la page

La recherche d'éléments est au cœur de tout tutoriel de scraping avec Selenium. Selenium propose deux méthodes principales, find_element() (renvoie la première correspondance) et find_elements() (renvoie une liste de toutes les correspondances), chacune acceptant une By stratégie de localisation.

Stratégies de localisation courantes

from selenium.webdriver.common.by import By

# By ID (fastest, most reliable when available)
driver.find_element(By.ID, "search-input")

# By class name
driver.find_elements(By.CLASS_NAME, "product-card")

# By CSS selector
driver.find_element(By.CSS_SELECTOR, "div.results > a.item-link")

# By XPath
driver.find_element(By.XPATH, "//table[@id='data']//tr")

# By tag name
driver.find_elements(By.TAG_NAME, "tr")

# By name attribute
driver.find_element(By.NAME, "email")

Les localisateurs basés sur l'ID sont généralement les plus rapides, car le navigateur peut accéder directement à l'élément sans parcourir l'arborescence DOM. Si l'élément cible ne possède pas d'ID, les sélecteurs CSS constituent la meilleure alternative pour la plupart des cas d'utilisation : ils sont compacts, lisibles et largement pris en charge.

XPath vs sélecteurs CSS

XPath excelle lorsque vous devez remonter dans le DOM (axe parent), effectuer une correspondance par contenu textuel ou utiliser des prédicats complexes. Par exemple, //div[contains(text(), 'Price')] est facile à réaliser en XPath mais n'a pas d'équivalent CSS direct. Les sélecteurs CSS sont généralement plus rapides à évaluer et plus faciles à lire pour des modèles simples comme div.card > h3.title. Utilisez XPath lorsque le CSS ne permet pas d'exprimer la relation dont vous avez besoin ; privilégiez le CSS pour tout le reste. Il vaut la peine d'examiner de plus près les compromis entre XPath et les sélecteurs CSS si vous créez des sélecteurs pour des structures de page complexes.

find_element vs. find_elements

find_element() lance une NoSuchElementException si aucune correspondance n'est trouvée, tandis que find_elements() renvoie une liste vide. Lors du scraping, find_elements() est généralement plus sûr car vous pouvez vérifier la longueur de la liste avant le traitement, ce qui évite les blocs try/except pour les éléments manquants.

cards = driver.find_elements(By.CSS_SELECTOR, ".product-card")
if cards:
    for card in cards:
        title = card.find_element(By.CSS_SELECTOR, "h3").text
        price = card.find_element(By.CSS_SELECTOR, ".price").text
        print(title, price)

Notez que vous pouvez enchaîner les find_element les appels sur un WebElement (et pas seulement sur le pilote). Cela limite la recherche à la sous-arborescence de cet élément, ce qui est à la fois plus rapide et plus précis lorsque vous parcourez des structures répétitives telles que des cartes ou des lignes de tableau.

Le workflow de scraping Web avec Selenium FindElement consiste essentiellement à : identifier la stratégie de localisation, la tester dans la console DevTools du navigateur, puis l'encoder dans votre script Python. Chrome DevTools vous permet de tester les sélecteurs CSS avec $$() et les expressions XPath $x() directement dans la console, ce qui accélère considérablement le développement des sélecteurs.

Interagir avec les éléments de la page

Une fois que vous avez localisé un élément, Selenium vous fournit des méthodes pour agir sur celui-ci exactement comme le ferait un utilisateur humain. L'objet WebElement expose des actions telles que cliquer, taper, lire du texte et récupérer des valeurs d'attributs.

Cliquer, saisir du texte et lire

from selenium.webdriver.common.keys import Keys

# Click a button
driver.find_element(By.ID, "load-more").click()

# Type into an input field
search_box = driver.find_element(By.NAME, "q")
search_box.clear()
search_box.send_keys("web scraping with selenium")
search_box.send_keys(Keys.RETURN)

# Read text content
heading = driver.find_element(By.TAG_NAME, "h1").text

# Read an attribute value
link = driver.find_element(By.CSS_SELECTOR, "a.detail-link")
href = link.get_attribute("href")

La .text propriété renvoie le texte visible d'un élément, tandis que .get_attribute() lit n'importe quel attribut HTML (href, src, data-*, etc.). Ces deux méthodes constituent vos principaux outils d'extraction de données au niveau des éléments.

Utilisation des menus déroulants

Pour les <select> éléments, Selenium fournit une classe pratique :

from selenium.webdriver.support.ui import Select

dropdown = Select(driver.find_element(By.ID, "sort-by"))
dropdown.select_by_visible_text("Price: Low to High")

Vous pouvez également sélectionner par valeur (select_by_value("price_asc")) ou par index basé sur zéro (select_by_index(2)). Les applications web modernes utilisent de plus en plus souvent des composants de menus déroulants personnalisés à la place des éléments <select> , auquel cas vous devrez cliquer sur le déclencheur du menu déroulant, puis sur l'option souhaitée en tant qu' find_element et click appels distincts.

Enchaînement d'actions

L' ActionChains API vous permet de composer des interactions complexes telles que le survol, le glisser-déposer et le clic droit :

from selenium.webdriver.common.action_chains import ActionChains

menu = driver.find_element(By.ID, "mega-menu")
ActionChains(driver).move_to_element(menu).perform()

Cela est utile pour extraire des méga-menus, des info-bulles et d'autres éléments qui n'apparaissent qu'au survol. Vous pouvez enchaîner plusieurs actions avant d'appeler .perform() pour les exécuter dans l'ordre.

Chacune de ces méthodes d'interaction attend que l'élément soit présent dans le DOM, mais pas nécessairement visible ou cliquable. Combinez-les avec des attentes explicites (abordées ci-après) pour éviter les conditions de concurrence sur les pages qui chargent du contenu de manière asynchrone.

Stratégies d'attente : attentes implicites, explicites et fluides

Les pages dynamiques chargent le contenu de manière asynchrone, et l'une des erreurs les plus courantes en matière de scraping Web avec Selenium consiste à essayer de localiser un élément avant qu'il n'existe dans le DOM. Le codage en dur time.sleep(5) fonctionne techniquement, mais cela fait perdre du temps sur les pages rapides et échoue sur les pages lentes. Selenium propose trois alternatives plus intelligentes.

Attentes implicites

Un délai implicite indique au pilote d'interroger le DOM pendant un nombre de secondes spécifié avant de lever une NoSuchElementException:

driver.implicitly_wait(10)  # Applies globally to all find_element calls

C'est simple à configurer, mais cela s'applique à chaque recherche, ce qui peut masquer des problèmes de performances et ralentir votre scraper lorsque les éléments n'existent véritablement pas. Vous ne pouvez pas non plus personnaliser la condition : elle vérifie uniquement la présence, et non la visibilité ou la possibilité de cliquer.

Attentes explicites

Les attentes explicites constituent l'approche recommandée dans les bonnes pratiques des tutoriels de scraping Selenium. Vous spécifiez une condition et un délai d'expiration, et le pilote interroge le DOM jusqu'à ce que la condition soit remplie :

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

element = WebDriverWait(driver, 15).until(
    EC.visibility_of_element_located((By.CSS_SELECTOR, ".results-container"))
)

Les conditions courantes incluent :

  • presence_of_element_located: l'élément existe dans le DOM (il peut être masqué)
  • visibility_of_element_located: l'élément est à la fois présent et visible
  • element_to_be_clickable: l'élément est visible et activé
  • text_to_be_present_in_element: l'élément contient un texte spécifique
  • staleness_of: l'élément n'est plus associé au DOM (utile après une navigation)

Ces options vous permettent d'attendre exactement ce dont vous avez besoin, sans plus, ce qui garantit la rapidité de votre scraper sans compromettre sa fiabilité.

Attentes fluides

Une attente fluide est une attente explicite avec un réglage supplémentaire : vous définissez l'intervalle d'interrogation et les exceptions à ignorer pendant l'interrogation.

from selenium.webdriver.support.ui import WebDriverWait
from selenium.common.exceptions import NoSuchElementException

wait = WebDriverWait(
    driver, timeout=20, poll_frequency=0.5,
    ignored_exceptions=[NoSuchElementException]
)
element = wait.until(
    EC.presence_of_element_located((By.ID, "dynamic-table"))
)

Utilisez les attentes fluides lorsque l'intervalle d'interrogation par défaut de 500 ms est trop agressif pour une cible à débit limité ou à chargement lent. Dans la plupart des scénarios de scraping Web avec Selenium, une attente explicite standard avec WebDriverWait répond à vos besoins. Le point essentiel à retenir est le suivant : privilégiez toujours WebDriverWait à time.sleep(). C'est à la fois plus rapide et plus fiable.

Exécution de JavaScript et techniques de défilement

Certaines interactions sont plus faciles (ou seulement possibles) via l'exécution brute de JavaScript. La méthode execute_script() exécute n'importe quel extrait de code JavaScript dans le contexte du navigateur et renvoie le résultat à votre code Python.

Exécution de scripts de base

page_height = driver.execute_script("return document.body.scrollHeight;")
print(f"Total page height: {page_height}px")

Vous pouvez passer des objets Python en arguments au script et les référencer via arguments[0], arguments[1], etc. à l’intérieur de la chaîne JavaScript. Les valeurs de retour sont automatiquement converties en leurs équivalents Python (dictionnaires, listes, chaînes, nombres).

Faire défiler jusqu'au bas de la page

Le cas d'utilisation le plus courant en matière de scraping est le défilement infini. Voici un modèle de boucle fiable pour les scénarios de scraping de sites web dynamiques avec Selenium :

import time

last_height = driver.execute_script("return document.body.scrollHeight")

while True:
    driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
    time.sleep(2)  # Allow content to load
    new_height = driver.execute_script("return document.body.scrollHeight")
    if new_height == last_height:
        break
    last_height = new_height

La boucle compare la hauteur de défilement avant et après chaque défilement. Lorsque la hauteur cesse de changer, aucun nouveau contenu ne se charge et la boucle se termine. Vous pouvez ajouter une limite maximale d'itérations pour éviter les boucles infinies sur les pages qui chargent continuellement du contenu.

Faire défiler jusqu'à un élément spécifique

Il arrive parfois que vous deviez faire apparaître un élément particulier dans la fenêtre d'affichage (par exemple, une image chargée de manière différée ou un bouton « Charger plus ») :

element = driver.find_element(By.ID, "footer-section")
driver.execute_script("arguments[0].scrollIntoView({behavior: 'smooth'});", element)

Déclencher des actions cachées

L'exécution de JavaScript est également utile pour cliquer sur des éléments masqués par des superpositions, supprimer des en-têtes collants qui gênent les captures d'écran, ou extraire des données intégrées dans des variables JavaScript :

data = driver.execute_script("return window.__INITIAL_STATE__;")

Si le site stocke des données structurées dans un objet JS global (courant dans les applications React et Next.js), les récupérer directement est plus rapide que d'analyser le DOM rendu. Vous obtenez un dictionnaire Python en retour execute_script, que vous pouvez traiter immédiatement sans aucune analyse HTML.

Prise de captures d'écran pour le débogage

Lorsqu'un scraper échoue silencieusement ou renvoie des résultats inattendus, une capture d'écran de l'état de la page au moment de l'échec est inestimable.

driver.save_screenshot("debug_screenshot.png")

Vous pouvez également capturer un élément spécifique :

element = driver.find_element(By.ID, "captcha-container")
element.screenshot("captcha_element.png")

Enregistrez les captures d'écran dans vos blocs de gestion des erreurs afin de pouvoir inspecter visuellement ce que le navigateur affichait lorsqu'un problème s'est produit. En mode headless, cela est particulièrement important car il n'y a pas de fenêtre visible à consulter. Associez les captures d'écran à la journalisation de driver.current_url et driver.page_source[:500] pour obtenir un instantané de débogage complet.

Pour les pipelines de scraping de longue durée, pensez à enregistrer des captures d'écran horodatées à des points de contrôle clés (après la connexion, après la pagination, avant l'extraction des données) afin de pouvoir retracer exactement à quel moment une exécution s'est écartée du chemin prévu.

Gestion de la pagination sur plusieurs pages

La plupart des sites répartissent les données sur plusieurs pages, et un scraper de niveau production doit suivre automatiquement ces liens de pagination. Il existe deux modèles courants : la pagination par paramètre d'URL et la pagination par clic.

Pagination par paramètres URL

Si le site utilise des chaînes de requête telles que ?page=2, vous pouvez construire les URL directement :

all_results = []
for page_num in range(1, 50):
    driver.get(f"https://example.com/listings?page={page_num}")
    WebDriverWait(driver, 10).until(
        EC.presence_of_element_located((By.CSS_SELECTOR, ".listing-card"))
    )
    items = driver.find_elements(By.CSS_SELECTOR, ".listing-card")
    if not items:
        break  # No results means we passed the last page
    for item in items:
        all_results.append({
            "title": item.find_element(By.CSS_SELECTOR, "h2").text,
            "price": item.find_element(By.CSS_SELECTOR, ".price").text,
        })

La if not items: break vérification est la méthode la plus simple pour détecter la dernière page : lorsqu’une page renvoie zéro résultat, vous vous arrêtez. Une autre approche consiste à vérifier si le numéro de page actuel dépasse le total indiqué dans le widget de pagination, ou à rechercher un lien « page suivante » et à s’arrêter lorsqu’il disparaît.

Pagination par clic

Certains sites chargent la page suivante via JavaScript lorsque vous cliquez sur un bouton « Suivant » :

from selenium.common.exceptions import NoSuchElementException

all_results = []
while True:
    items = driver.find_elements(By.CSS_SELECTOR, ".listing-card")
    for item in items:
        all_results.append(item.find_element(By.CSS_SELECTOR, "h2").text)

    try:
        next_btn = driver.find_element(By.CSS_SELECTOR, "a.next-page")
        if "disabled" in next_btn.get_attribute("class"):
            break
        next_btn.click()
        WebDriverWait(driver, 10).until(EC.staleness_of(items[0]))
    except NoSuchElementException:
        break

La staleness_of condition attend que les anciens éléments soient détachés du DOM, confirmant ainsi que la nouvelle page s’est chargée. La vérification de la présence d’une classe « disabled » sur le bouton « Suivant » permet de gérer la dernière page de manière élégante.

Ces deux modèles nécessitent une condition de fin claire. Sans celle-ci, votre scraper tournera en boucle indéfiniment ou plantera sur la dernière page. Testez toujours votre logique de pagination jusqu'à la limite : que se passe-t-il sur la toute dernière page ?

Extraction de données à partir de plusieurs URL

Lorsque vos données cibles se trouvent sur des pages de détail distinctes (par exemple, une liste de produits renvoyant vers des pages de produit individuelles), vous effectuez généralement le scraping en deux étapes : collecter les URL de la liste, puis visiter chacune d'entre elles.

# Pass 1: Collect detail URLs from the listing page
driver.get("https://example.com/catalog")
links = driver.find_elements(By.CSS_SELECTOR, "a.product-link")
detail_urls = [link.get_attribute("href") for link in links]

# Pass 2: Visit each detail URL and extract data
products = []
for url in detail_urls:
    try:
        driver.get(url)
        WebDriverWait(driver, 10).until(
            EC.presence_of_element_located((By.CSS_SELECTOR, ".product-detail"))
        )
        products.append({
            "name": driver.find_element(By.CSS_SELECTOR, "h1.product-name").text,
            "description": driver.find_element(By.CSS_SELECTOR, ".description").text,
            "url": url,
        })
    except Exception as e:
        print(f"Skipped {url}: {e}")
        continue

driver.quit()

Réutilisez la même instance WebDriver tout au long du processus. Lancer un nouveau navigateur pour chaque URL gaspille du temps de démarrage et de la mémoire. En encapsulant la boucle interne dans un try/except, vous vous assurez qu’une seule page défectueuse ne plante pas l’ensemble de votre extraction.

Ce modèle maître-détail est l'un des workflows de scraping de sites Web les plus pratiques avec Python Selenium. Il se combine bien avec la boucle de pagination de la section précédente pour parcourir un catalogue entier. Collectez d'abord toutes les URL des pages de liste à travers la pagination, puis parcourez les pages de détail lors d'un deuxième passage.

Pour les longues listes d'URL, envisagez d'ajouter un court délai entre les requêtes (0,5 à 2 secondes) afin de respecter les limites de débit du site et de réduire le risque de déclencher les défenses anti-bot. Vous pouvez randomiser légèrement ce délai pour rendre votre modèle de requêtes moins prévisible.

Extraction et analyse des tableaux HTML

Les données tabulaires constituent l'une des cibles les plus structurées (et donc les plus faciles) pour le web scraping avec Selenium. Voici un modèle générique qui fonctionne sur n'importe quel tableau HTML standard :

table = driver.find_element(By.CSS_SELECTOR, "table#stats")

# Extract headers
headers = [th.text for th in table.find_elements(By.CSS_SELECTOR, "thead th")]

# Extract rows
rows = []
for tr in table.find_elements(By.CSS_SELECTOR, "tbody tr"):
    cells = [td.text for td in tr.find_elements(By.TAG_NAME, "td")]
    rows.append(dict(zip(headers, cells)))

Cela vous donne une liste de dictionnaires où chaque clé est un en-tête de colonne et chaque valeur est le texte de la cellule correspondante. Il s'agit d'une structure claire et prévisible qui s'intègre directement à votre pipeline d'exportation.

Si le tableau est volumineux ou si vous prévoyez de procéder à une analyse plus approfondie, il est plus efficace de passer le relais à pandas :

import pandas as pd

html = driver.page_source
tables = pd.read_html(html, attrs={"id": "stats"})
df = tables[0]

pd.read_html gère les colspan, rowspan et les tableaux imbriqués de manière plus élégante que l'itération manuelle des éléments. Utilisez le page_source pour lui fournir le code HTML entièrement rendu, incluant toutes les données injectées par JavaScript.

Faites attention aux tableaux qui chargent les lignes dynamiquement lorsque vous faites défiler la page. Dans ces cas-là, vous devez faire défiler le conteneur du tableau (et pas seulement la page) pour déclencher le chargement différé avant d'extraire les lignes. Certains sites financiers et d'analyse utilisent des tableaux virtualisés qui ne rendent visibles que les lignes visibles, ce qui nécessite un défilement incrémental pour capturer l'ensemble des données.

Combiner Selenium et BeautifulSoup pour un analyse plus rapide

Selenium est très performant pour le rendu des pages, mais ses méthodes de recherche d'éléments sont relativement lentes car chaque appel franchit la frontière du protocole WebDriver. Une astuce courante pour améliorer les performances consiste à laisser Selenium gérer le rendu, puis à transmettre le code HTML finalisé à BeautifulSoup pour l'analyse.

from bs4 import BeautifulSoup

driver.get("https://example.com/products")
WebDriverWait(driver, 10).until(
    EC.presence_of_element_located((By.CSS_SELECTOR, ".product-grid"))
)

soup = BeautifulSoup(driver.page_source, "html.parser")
cards = soup.select(".product-card")

for card in cards:
    title = card.select_one("h3").get_text(strip=True)
    price = card.select_one(".price").get_text(strip=True)
    print(title, price)

Ce modèle de web scraping Selenium-BeautifulSoup vous offre le meilleur des deux mondes : Selenium exécute le JavaScript pour que le DOM soit complet, et BeautifulSoup analyse l'instantané HTML statique en mémoire sans aucun aller-retour réseau. Sur les pages comportant des centaines d'éléments, la différence de vitesse est notable.

Le workflow est simple : utilisez Selenium pour la navigation et l'exécution de JavaScript, puis basculez vers BeautifulSoup (ou lxml) dès que vous disposez du page_source. Cela réduit le nombre d’appels WebDriver de plusieurs centaines à un seul. Si vous débutez avec BeautifulSoup, un guide sur l’extraction et l’analyse de données Web avec BeautifulSoup constitue un complément utile à ce tutoriel.

Exportation des données extraites au format CSV et JSON

Les données brutes stockées dans une liste Python ne sont utiles que pendant l'exécution de votre script. Vous souhaiterez presque toujours les conserver.

Exportation au format CSV

import csv

fieldnames = ["title", "price", "url"]
with open("products.csv", "w", newline="", encoding="utf-8") as f:
    writer = csv.DictWriter(f, fieldnames=fieldnames)
    writer.writeheader()
    writer.writerows(all_results)

Exportation au format JSON

import json

with open("products.json", "w", encoding="utf-8") as f:
    json.dump(all_results, f, indent=2, ensure_ascii=False)

Raccourci Pandas

Si vous utilisez déjà Pandas, une seule ligne de code permet de gérer les deux formats :

import pandas as pd

df = pd.DataFrame(all_results)
df.to_csv("products.csv", index=False)
df.to_json("products.json", orient="records", indent=2)

Choisissez le format CSV lorsque les outils en aval attendent des données tabulaires plates (feuilles de calcul, importations SQL). Choisissez le format JSON lorsque vos données sont imbriquées ou que vous devez conserver des types tels que les tableaux et les objets. Pour les très grands ensembles de données, envisagez d'écrire les lignes de manière incrémentielle plutôt que d'accumuler d'abord tout en mémoire. L' csv.DictWriter approche prend naturellement en charge les écritures incrémentielles, car vous pouvez vider le tampon après chaque ligne.

Nettoyage et validation des données extraites

Les données extraites sont rarement prêtes à être analysées dès leur sortie du navigateur. Les lignes en double, les champs manquants et les incohérences de formatage sont la norme, et non l'exception.

import pandas as pd

df = pd.DataFrame(all_results)

# Remove exact duplicate rows
df.drop_duplicates(inplace=True)

# Drop rows where critical fields are missing
df.dropna(subset=["title", "price"], inplace=True)

# Normalize price strings to floats
df["price"] = (df["price"]
               .str.replace(r"[^0-9.]", "", regex=True)
               .astype(float))

# Strip extra whitespace from text fields
df["title"] = df["title"].str.strip()

Cette étape de nettoyage comble le fossé entre les données brutes extraites et celles qui sont réellement utiles pour l'analyse, le reporting ou l'alimentation d'une base de données. Effectuer ces vérifications avant l'exportation permet de détecter les problèmes à un stade précoce, plutôt que de découvrir des données erronées en aval.

Parmi les contrôles de validation courants qu'il vaut la peine d'ajouter, citons la vérification que les URL sont bien formées, que les champs numériques se situent dans les plages attendues (un prix de produit de 0,00 $ pourrait être une erreur d'analyse) et que les champs de texte obligatoires ne sont pas des chaînes vides déguisées en valeurs réelles. La création d'une petite fonction de validation qui s'exécute après chaque session de scraping rend votre pipeline plus robuste au fil du temps.

Utilisation de proxys avec Selenium pour éviter les blocages

Si vous envoyez trop de requêtes à partir d'une seule adresse IP, le site cible finira par vous bloquer. Les proxys répartissent votre trafic sur différentes adresses IP, réduisant ainsi le risque de blocage et permettant l'accès à des contenus soumis à des restrictions géographiques.

Configuration manuelle des proxys

from selenium import webdriver

options = webdriver.ChromeOptions()
options.add_argument("--proxy-server=http://123.45.67.89:8080")

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

Cette approche fonctionne pour un proxy unique, mais la rotation manuelle au sein d'un pool (lancement d'un nouveau pilote par adresse IP) est fastidieuse et lente. Les proxys de centre de données sont moins chers mais plus faciles à détecter par les sites, tandis que les proxys résidentiels acheminent le trafic via de véritables adresses IP de consommateurs et sont beaucoup plus difficiles à distinguer des visiteurs authentiques.

Proxys authentifiés

De nombreux fournisseurs de proxys exigent une authentification par nom d'utilisateur et mot de passe. Chrome ne prend pas en charge nativement l'authentification proxy via des indicateurs de ligne de commande ; vous devez donc trouver une solution de contournement. Une méthode courante consiste à utiliser une extension de navigateur légère qui injecte automatiquement les identifiants dans la négociation du proxy. Une autre option consiste à exécuter un proxy local (tel que mitmproxy) qui ajoute l'en-tête d'authentification et le transmet au proxy distant.

Quand utiliser un service de proxy géré

La gestion de votre propre pool de proxys, la gestion de la logique de rotation et le traitement des adresses IP bannies constituent une charge opérationnelle qui augmente avec l'échelle. Les services de proxys gérés vous offrent un point de terminaison unique qui gère la rotation, l'authentification et l'intégrité des adresses IP en arrière-plan. Cela est particulièrement utile pour le web scraping avec Selenium à des volumes de production où vous avez besoin de centaines d'adresses IP en rotation dans différentes zones géographiques.

Pour des conseils sur la manière de ne pas se faire bloquer, les stratégies décrites dans les guides sur la prévention des interdictions d'IP lors du web scraping s'appliquent également aux configurations basées sur Selenium. Les pratiques clés incluent la rotation des user agents en plus des adresses IP, l'ajout de délais aléatoires entre les requêtes et le respect des robots.txt directives.

Détecter et éviter les pièges de type « honeypot »

Un honeypot est un élément HTML caché, invisible pour les visiteurs humains mais visible pour les robots. Si votre scraper interagit avec lui (clique sur un lien caché, remplit un champ de formulaire caché), le site marque votre session comme automatisée et peut vous bloquer immédiatement.

Le modèle le plus courant est un champ de formulaire stylisé avec display:none ou visibility:hidden:

hidden_inputs = driver.find_elements(
    By.CSS_SELECTOR, "input[style*='display:none'], input[style*='visibility:hidden']"
)

for inp in hidden_inputs:
    print(f"Honeypot detected: name={inp.get_attribute('name')}")

Règles de codage défensif pour les honeypots :

  • Ne parcourez jamais aveuglément tous les champs de formulaire pour les remplir. Vérifiez d'abord leur visibilité.
  • Utilisez element.is_displayed() avant d'interagir avec un élément que vous n'avez pas explicitement ciblé.
  • Si un lien a opacity: 0 ou est positionné hors de l'écran avec des coordonnées négatives, ignorez-le.
  • Certains honeypots utilisent des classes CSS plutôt que des styles en ligne. Inspectez le style calculé avec execute_script si vous soupçonnez que le site utilise des feuilles de style externes pour masquer des éléments pièges.

La prise en compte des honeypots devient particulièrement importante lorsque votre scraper remplit des formulaires (champs de recherche, champs de connexion, formulaires de contact). Une simple vérification de la visibilité avant chaque interaction n'ajoute qu'une charge minimale et évite une catégorie de détection qui serait autrement difficile à déboguer.

Conseils d'optimisation des performances

Selenium pilote un navigateur complet, donc toute optimisation qui réduit la charge de travail du navigateur se traduit directement par des exécutions de scraping plus rapides et moins coûteuses.

Exécutez en mode headless. La suppression de la couche GUI réduit considérablement le temps de démarrage et l'utilisation de la mémoire. Utilisez --headless=new dans vos options Chrome. Un guide sur les navigateurs sans interface graphique aborde ces nuances en détail.

Bloquez les ressources inutiles. Les images, les polices et les fichiers CSS allongent le temps de chargement sans apporter de données. Vous pouvez utiliser les commandes du protocole Chrome DevTools pour les bloquer :

driver.execute_cdp_cmd("Network.setBlockedURLs", {
    "urls": ["*.jpg", "*.png", "*.gif", "*.svg", "*.woff2", "*.css"]
})
driver.execute_cdp_cmd("Network.enable", {})

Privilégiez les localisateurs rapides. Les recherches basées sur l'ID sont les plus rapides. Les expressions XPath complexes qui parcourent de grands sous-arborescences sont plus coûteuses. Lorsque vous avez le choix, utilisez By.ID ou un sélecteur CSS court.

Ajustez vos délais d'attente. Des délais d'attente implicites trop généreux ralentissent chaque recherche d'élément. Utilisez des délais d'attente explicites ciblés avec WebDriverWait et définissez des délais d'expiration en fonction du comportement spécifique de la page, et non pas un délai de secours standard de 30 secondes.

Réduisez au minimum les redémarrages du navigateur. Réutilisez une seule instance de pilote sur toutes les pages lorsque cela est possible. Chaque webdriver.Chrome() appel lance un processus de navigateur complet.

Désactivez les fonctionnalités dont vous n'avez pas besoin. Les indicateurs tels que --disable-extensions, --disable-infobarset --blink-settings=imagesEnabled=false réduisent encore la charge du navigateur.

Stratégie de chargement des pages. Configurez options.page_load_strategy = "eager" pour ne plus attendre que les images et les feuilles de style aient fini de se charger. Le DOM devient interactif plus rapidement, et vous pouvez commencer l'extraction plus tôt.

Ces optimisations se cumulent. Les appliquer toutes ensemble peut réduire le temps d'exécution d'une tâche de scraping Selenium Headless de 50 % ou plus par rapport à une configuration par défaut.

Problèmes courants et solutions

Même avec un scraper bien configuré, vous rencontrerez des problèmes. Voici les problèmes les plus fréquents et leurs solutions.

Problème

Cause

Solution

NoSuchElementException

L'élément n'a pas encore été chargé

Utilisez WebDriverWait avec une condition attendue appropriée

Référence à un élément obsolète

Le DOM a changé après que vous ayez localisé l'élément

Re-localisez l'élément après toute navigation sur la page ou tout rechargement AJAX

Blocs CAPTCHA

Le site détecte un trafic automatisé

Ralentissez les requêtes, alternez les user agents, utilisez des proxys résidentiels

Données incohérentes

La mise en page varie selon les produits/catégories

Ajoutez des contrôles de sécurité avec find_elements et tester la longueur de la liste avant d'accéder

Fuites de mémoire lors d'exécutions longues

Le navigateur accumule des données d'état sur des centaines de pages

Redémarrez le pilote périodiquement (toutes les N pages) ou effacez les cookies/le cache

Exécution lente

Charge de rendu complète du navigateur

Appliquez les conseils d'optimisation de la section précédente

Modèle de gestion des erreurs : encapsulez votre boucle de scraping dans un try/except qui intercepte WebDriverException comme solution de secours générale. Enregistrez l'URL, sauvegardez une capture d'écran et passez à l'élément suivant plutôt que de bloquer l'ensemble de la tâche.

from selenium.common.exceptions import WebDriverException

for url in urls:
    try:
        driver.get(url)
        # ... extraction logic ...
    except WebDriverException as e:
        driver.save_screenshot(f"error_{url.split('/')[-1]}.png")
        print(f"Failed on {url}: {e}")
        continue

Si vous constatez des échecs intermittents sur un site qui fonctionne normalement, vérifiez si le site sert un contenu différent en fonction de la géolocalisation, de l'agent utilisateur ou de l'heure de la journée. Enregistrer la source complète de la page en cas d'échec (en plus de la capture d'écran) aide à diagnostiquer ce type de problèmes environnementaux. Contourner Cloudflare et les services de protection similaires avec Selenium nécessite des techniques supplémentaires allant au-delà de la configuration de base.

Évolutivité avec Selenium Grid

Une instance Selenium unique est limitée à un seul navigateur à la fois. Lorsque vous devez extraire des données de milliers de pages en parallèle, Selenium Grid répartit les sessions de navigateur sur plusieurs machines.

Fonctionnement de Grid

Selenium Grid utilise une architecture hub-nœud. Le hub est un serveur central qui reçoit les requêtes WebDriver et les achemine vers les nœuds disponibles, chacun exécutant une ou plusieurs instances de navigateur. Vous pointez votre script vers l'URL du hub plutôt que vers un pilote local :

from selenium import webdriver

options = webdriver.ChromeOptions()
driver = webdriver.Remote(
    command_executor="http://grid-hub:4444/wd/hub",
    options=options
)

Vous pouvez exécuter le hub et les nœuds à l'aide de Docker, ce qui facilite la mise à l'échelle :

docker run -d -p 4444:4444 selenium/hub
docker run -d --link selenium-hub:hub selenium/node-chrome

Considérations pratiques

L'exécution de dizaines de sessions de navigateur en parallèle consomme beaucoup de CPU et de RAM. Surveillez vos nœuds et fixez une limite maximale de sessions pour éviter la surcharge. Le débogage est également plus difficile dans une configuration distribuée, car les captures d'écran et les journaux se trouvent sur des machines distantes. Utilisez la fonctionnalité d'enregistrement vidéo intégrée de Grid ou la journalisation centralisée pour garder une bonne visibilité.

Selenium Grid est un choix solide pour les charges de travail de web scraping Selenium Grid nécessitant un parallélisme modéré (5 à 20 sessions simultanées). Au-delà de cela, la charge opérationnelle liée à la gestion des nœuds, à la gestion des pannes et à la surveillance de l'utilisation des ressources augmente rapidement. Pour les équipes qui ne souhaitent pas gérer l'infrastructure Grid, les services de navigateur hébergés dans le cloud ou les outils de scraping basés sur des API offrent le même modèle d'exécution parallèle qu'un service géré.

Selenium, Playwright et Puppeteer : comparaison rapide

Selenium n'est pas le seul outil d'automatisation de navigateur. Playwright et Puppeteer sont des alternatives modernes populaires, chacune présentant des atouts différents.

Fonctionnalité

Selenium

Playwright

Puppeteer

Langages pris en charge

Python, Java, C#, JS, Ruby

Python, Java, .NET, JS/TS

JavaScript/TypeScript uniquement

Moteurs de navigateur

Chrome, Firefox, Edge, Safari

Chromium, Firefox, WebKit

Chromium uniquement

Attente automatique

Manuel (attentes explicites/implicites)

Attente automatique intégrée aux actions

Manuel (waitForSelector)

Vitesse

Plus lent (protocole WebDriver)

Plus rapide (CDP / canaux du navigateur)

Plus rapide (CDP)

Exécution parallèle

Via Selenium Grid

Contextes de navigateur natifs

Via les contextes de page/navigateur

Communauté

La plus grande et la plus mature

En pleine croissance

Important (axé sur Chromium)

Optez pour Selenium si vous avez besoin d'une prise en charge multilingue, d'une compatibilité pour les tests multi-navigateurs, ou si votre équipe connaît déjà l'API Selenium. Choisissez Playwright si vous souhaitez bénéficier d'une fonction d'attente automatique intégrée, de modèles asynchrones modernes et d'une prise en charge multi-navigateurs à partir d'une seule bibliothèque. Optez pour Puppeteer si votre pile est exclusivement en JavaScript et que vous n'avez besoin que de Chromium.

Ces trois outils permettent le scraping via un navigateur, mais la fonction d'attente automatique intégrée de Playwright et ses contextes de navigateur natifs pour le parallélisme lui confèrent un avantage pour les nouveaux projets qui n'ont pas besoin de l'écosystème linguistique plus large de Selenium. Pour approfondir les capacités de scraping de Playwright, les ressources sur le scraping Web avec Playwright fournissent une comparaison détaillée.

Rendu de JavaScript sans navigateur : alternatives basées sur des API

L'exécution d'un navigateur complet fonctionne, mais elle est coûteuse en termes de CPU, de RAM et de maintenance technique. Lorsque votre objectif est simplement d'obtenir le code HTML rendu d'une page riche en JavaScript, une approche basée sur une API peut s'avérer bien plus efficace.

Les API de scraping gérées acceptent une URL et renvoient le code HTML entièrement rendu (voire du JSON pré-parsé). En arrière-plan, elles gèrent le rendu du navigateur, la rotation des proxys, la résolution des CAPTCHA et la logique de réessai. Vous envoyez une seule requête HTTP et obtenez des données propres en retour, ce qui signifie que votre code de scraping reste aussi simple qu'un requests.get() appel.

Ce modèle est particulièrement adapté lorsque :

  • Vous effectuez du scraping à grande échelle et ne souhaitez pas gérer l'infrastructure Selenium Grid.
  • Les systèmes anti-bot sont agressifs et nécessitent une rotation de proxys résidentiels ainsi que la gestion des empreintes de navigateur.
  • Vous souhaitez dissocier entièrement votre logique d'analyse de la couche de rendu.
  • Votre équipe ne dispose pas des capacités DevOps nécessaires pour gérer les versions des navigateurs et des pilotes dans tous les environnements.

Le compromis réside dans le contrôle. Avec Selenium, vous pouvez cliquer sur des boutons, remplir des formulaires et naviguer dans des workflows en plusieurs étapes. Les moteurs de rendu basés sur des API gèrent généralement les requêtes de pages uniques. Pour le scraping interactif complexe (processus de connexion, assistants en plusieurs étapes), Selenium ou un outil d'automatisation de navigateur similaire reste la meilleure solution.

Une approche hybride fonctionne bien dans la pratique : utilisez Selenium localement pendant le développement pour comprendre la structure de la page et créer vos sélecteurs, puis passez à un service basé sur une API en production pour éviter la charge opérationnelle liée à l'exécution de navigateurs à grande échelle. Cela vous offre la flexibilité du scraping Web avec Selenium pendant le prototypage et la fiabilité d'un service géré en production. Vous conservez le même code d'analyse dans les deux cas ; seule la couche de récupération des données change.

Exemple complet : projet de web scraping de bout en bout avec Selenium

Rassemblons tous ces éléments dans un seul script exécutable. Cet exemple extrait les titres et les prix de livres à partir d'un site de démonstration paginé, nettoie les données et les exporte au format CSV.

import csv
import time
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

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

all_books = []
base_url = "https://books.toscrape.com/catalogue/page-{}.html"

# --- Scrape with Pagination ---
for page in range(1, 51):
    driver.get(base_url.format(page))
    try:
        WebDriverWait(driver, 10).until(
            EC.presence_of_element_located((By.CSS_SELECTOR, ".product_pod"))
        )
    except Exception:
        break  # No more pages

    books = driver.find_elements(By.CSS_SELECTOR, ".product_pod")
    if not books:
        break

    for book in books:
        title = book.find_element(By.CSS_SELECTOR, "h3 a").get_attribute("title")
        price = book.find_element(By.CSS_SELECTOR, ".price_color").text
        all_books.append({"title": title, "price": price})

driver.quit()

# --- Clean ---
seen = set()
cleaned = []
for book in all_books:
    key = (book["title"], book["price"])
    if key not in seen:
        seen.add(key)
        price_str = book["price"].replace("\xa3", "").strip()
        try:
            book["price"] = float(price_str)
        except ValueError:
            continue
        cleaned.append(book)

# --- Export ---
with open("books.csv", "w", newline="", encoding="utf-8") as f:
    writer = csv.DictWriter(f, fieldnames=["title", "price"])
    writer.writeheader()
    writer.writerows(cleaned)

print(f"Scraped {len(cleaned)} books to books.csv")

Ce script illustre l'ensemble du cycle de vie couvert dans ce tutoriel : configuration de Chrome sans interface graphique, attentes explicites, localisation d'éléments à l'aide de sélecteurs CSS, pagination avec détection de la dernière page, déduplication, normalisation des types de données et exportation au format CSV. Vous pouvez l'adapter à n'importe quelle cible en remplaçant le modèle d'URL et les sélecteurs CSS.

Les choix de conception clés de cet exemple méritent d'être soulignés. Nous utilisons --headless=new pour gagner en vitesse. Nous encapsulons l'attente dans un try/except pour gérer gracieusement le cas où la page est introuvable. Nous dédupliquons en suivant les paires titre/prix déjà vues dans un ensemble. Et nous normalisons les prix en nombres flottants avant l'exportation afin que les outils en aval puissent trier et filtrer numériquement.

À partir de là, vous pourriez étendre ce script en ajoutant une rotation de proxys, en enregistrant les résultats dans une base de données plutôt qu'au format CSV, ou en le déployant selon un calendrier avec un exécuteur de tâches comme cron ou Airflow. Les modèles que vous avez appris tout au long de ce tutoriel s'assemblent comme des blocs de construction.

Points clés

  • Utilisez Selenium lorsque le rendu JavaScript est nécessaire. Pour les pages HTML statiques, des outils plus légers comme requests BeautifulSoup ou Scrapy sont plus rapides et moins coûteux à exécuter.
  • Utilisez toujours des attentes explicites plutôt que time.sleep(). WebDriverWait avec expected_conditions rende votre scraper à la fois plus rapide et plus fiable sur les pages dynamiques.
  • Bloquez les ressources inutiles et exécutez en mode headless. La désactivation des images, des polices et du CSS en mode headless peut réduire de moitié le temps d'exécution sans perte de données extraites.
  • Nettoyez vos données avant de les exporter. La déduplication, la gestion des valeurs nulles et la normalisation des types permettent de détecter les problèmes à un stade précoce et de gagner du temps lors du débogage en aval.
  • Prévoyez l'évolutivité dès le départ. La rotation de proxys, Selenium Grid et les alternatives de rendu basées sur des API permettent à votre scraper de continuer à fonctionner alors que les sites cibles renforcent leurs défenses.

FAQ

Selenium est-il adapté au scraping web à grande échelle ?

Cela peut fonctionner, mais cela nécessite beaucoup de ressources. Chaque instance de navigateur consomme beaucoup de CPU et de RAM, donc l'exécution de centaines de sessions en parallèle nécessite une infrastructure solide. Selenium Grid aide à répartir la charge entre les machines, et le mode sans interface graphique réduit la charge par session. Pour les tâches véritablement à grande échelle (des millions de pages), les services de rendu basés sur des API ou les frameworks conçus pour l'exploration sont souvent plus rentables.

Selenium peut-il extraire des données sur des sites web nécessitant une authentification ?

Oui. Vous pouvez automatiser l'intégralité du processus de connexion : accéder à la page de connexion, localiser les champs de nom d'utilisateur et de mot de passe à l'aide de find_element, saisir les identifiants avec send_keys, puis cliquer sur le bouton de soumission. Une fois l'authentification effectuée, Selenium conserve les cookies de session afin que les chargements de pages suivants restent authentifiés tout au long de la session.

Comment gérer les CAPTCHA lors du scraping avec Selenium ?

Les CAPTCHA sont conçus pour empêcher l'automatisation, il n'existe donc pas de solution simple au sein du navigateur lui-même. Les stratégies courantes consistent à ralentir le rythme des requêtes pour éviter de déclencher les CAPTCHA dès le départ, à utiliser des proxys résidentiels pour réduire la détection, et à intégrer des services tiers de résolution de CAPTCHA qui renvoient des jetons que vous injectez dans la page via JavaScript.

Le scraping Web avec Selenium est-il légal ?

La légalité dépend de la juridiction, des conditions d'utilisation du site web et du type de données collectées. Aux États-Unis, l'arrêt de la Cour suprême dans l'affaire hiQ c. LinkedIn a précisé que le scraping de données accessibles au public ne constitue pas nécessairement une violation du CFAA. Cependant, le scraping de données personnelles peut relever du RGPD dans l'UE. Vérifiez toujours les robots.txt et les conditions d'utilisation du site cible, et consultez un conseiller juridique pour les projets de scraping à des fins commerciales.

Quelle est la différence entre Selenium et BeautifulSoup ?

Ils répondent à des besoins différents. Selenium contrôle un navigateur réel et peut exécuter du JavaScript, cliquer sur des boutons et naviguer sur des pages interactives. BeautifulSoup est un analyseur syntaxique qui prend une chaîne HTML et vous fournit des méthodes pour interroger et extraire des données de celle-ci, mais il ne peut pas charger de pages ni exécuter de JavaScript. De nombreux scrapers combinent les deux : Selenium affiche la page, puis BeautifulSoup analyse l'instantané HTML pour une extraction plus rapide.

Conclusion

Le web scraping avec Selenium vous permet d'extraire des données de pratiquement n'importe quel site web, y compris des applications riches en JavaScript que les bibliothèques HTTP statiques ne peuvent pas gérer. Tout au long de ce tutoriel, vous avez vu comment mettre en place l'environnement, configurer le navigateur, localiser des éléments et interagir avec eux, gérer le contenu dynamique avec des attentes explicites, paginer les ensembles de résultats et exporter des données propres.

Le principal compromis avec Selenium est le coût en ressources. Un vrai navigateur consomme plus de CPU et de mémoire qu’une requête HTTP légère, et ce coût se multiplie lorsque vous passez à l’échelle. Pour de nombreux scénarios de scraping, la voie pratique à suivre consiste à utiliser Selenium pour le développement et le prototypage, puis à déléguer le rendu et la complexité de la lutte anti-bot à un service dédié lorsque vous passez en production.

Si vous passez plus de temps à gérer des proxys, à lutter contre les CAPTCHA et à entretenir l'infrastructure de navigation qu'à écrire la logique de parsing proprement dite, WebScrapingAPI peut prendre en charge la couche de rendu et de livraison à votre place, afin que vous puissiez vous concentrer sur les données elles-mêmes. Cette séparation des préoccupations permet de garder votre base de code propre et votre pipeline de scraping fiable.

Quel que soit l'outil que vous choisissez, commencez par la solution la plus légère adaptée à votre cas d'utilisation, n'ajoutez de complexité que lorsque vous en avez besoin, et respectez toujours les conditions d'utilisation du site cible.

À propos de l'auteur
Robert Sfichi, Développeur full-stack @ WebScrapingAPI
Robert SfichiDéveloppeur full-stack

Robert Sfichi fait partie de l'équipe de WebScrapingAPI ; il contribue au développement du produit et aide à mettre en place des solutions fiables au service de la plateforme et de ses utilisateurs.

Commencez à créer

Prêt à faire évoluer votre système de collecte de données ?

Rejoignez plus de 2 000 entreprises qui utilisent WebScrapingAPI pour extraire des données Web à l'échelle de l'entreprise, sans aucun coût d'infrastructure.