Selenium avec Python : Automatiser le Navigateur
Apprenez à automatiser le navigateur avec Selenium 4 et Python : installation, localisation des éléments, attentes explicites, mode headless et Page Object.
InSkillCoach
Selenium avec Python : Automatiser le Navigateur
Selenium est la bibliothèque de référence pour piloter un navigateur web depuis un programme. Que vous souhaitiez automatiser des tests d’interface, remplir des formulaires répétitifs ou extraire des données de pages générées en JavaScript, Selenium vous donne un contrôle complet sur Chrome, Firefox ou Edge. Ce guide couvre l’essentiel de Selenium 4 avec Python : de l’installation jusqu’au pattern Page Object, en passant par les attentes explicites qui font la différence entre un script fragile et un script fiable.
Installation
Depuis Selenium 4.6, la gestion des drivers est automatique grâce à Selenium Manager, intégré à la bibliothèque. Plus besoin de télécharger manuellement chromedriver ou geckodriver : Selenium détecte votre navigateur et récupère le driver correspondant.
# Installation de Selenium 4
pip install selenium
# Vérification de la version installée
pip show selenium
Assurez-vous simplement d’avoir un navigateur installé sur la machine (Chrome, Firefox ou Edge).
Premier script
Voici un script minimal qui ouvre une page, lit son titre et ferme le navigateur :
from selenium import webdriver
# Selenium Manager télécharge automatiquement le driver adapté
driver = webdriver.Chrome()
# Navigation vers une page
driver.get("https://www.python.org")
# Lecture du titre de la page
print(driver.title)
# Welcome to Python.org
# Lecture de l'URL courante
print(driver.current_url)
# https://www.python.org/
# Fermeture propre du navigateur
driver.quit()
Deux méthodes de fermeture existent : driver.close() ferme l’onglet courant, tandis que driver.quit() ferme toutes les fenêtres et termine le processus du driver. Préférez toujours quit() en fin de script.
Localiser les éléments
Toute interaction commence par la localisation d’un élément dans le DOM. Selenium 4 utilise la classe By combinée à find_element (premier élément trouvé) ou find_elements (liste de tous les éléments).
from selenium import webdriver
from selenium.webdriver.common.by import By
driver = webdriver.Chrome()
driver.get("https://www.python.org")
# Par identifiant
barre_recherche = driver.find_element(By.ID, "id-search-field")
# Par sélecteur CSS
bouton = driver.find_element(By.CSS_SELECTOR, "button.search-button")
# Par XPath
lien = driver.find_element(By.XPATH, "//a[contains(text(), 'Downloads')]")
# Tous les liens de la page
liens = driver.find_elements(By.TAG_NAME, "a")
print(f"Nombre de liens : {len(liens)}")
driver.quit()
Tableau des stratégies de localisation
| Stratégie | Exemple | Quand l’utiliser |
|---|---|---|
By.ID | By.ID, "email" | Toujours en priorité si un id stable existe |
By.NAME | By.NAME, "username" | Formulaires classiques |
By.CSS_SELECTOR | By.CSS_SELECTOR, "div.card > a" | Polyvalent, rapide, lisible |
By.XPATH | By.XPATH, "//button[text()='OK']" | Recherche par texte ou remontée dans le DOM |
By.CLASS_NAME | By.CLASS_NAME, "btn-primary" | Une seule classe, sans espace |
By.TAG_NAME | By.TAG_NAME, "table" | Collections d’éléments |
By.LINK_TEXT | By.LINK_TEXT, "Contact" | Liens dont le texte exact est connu |
By.PARTIAL_LINK_TEXT | By.PARTIAL_LINK_TEXT, "Cont" | Liens dont le texte varie légèrement |
En règle générale : privilégiez By.ID, puis les sélecteurs CSS. Réservez XPath aux cas que CSS ne couvre pas (sélection par texte, navigation vers un parent).
Interagir avec les éléments
Clic et saisie de texte
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
driver = webdriver.Chrome()
driver.get("https://www.python.org")
champ = driver.find_element(By.ID, "id-search-field")
# Saisie de texte
champ.send_keys("selenium")
# Validation avec la touche Entrée
champ.send_keys(Keys.RETURN)
# Effacer un champ avant une nouvelle saisie
champ = driver.find_element(By.ID, "id-search-field")
champ.clear()
champ.send_keys("nouvelle recherche")
driver.quit()
Listes déroulantes
Pour les éléments <select>, Selenium fournit la classe utilitaire Select :
from selenium.webdriver.support.ui import Select
element_select = driver.find_element(By.ID, "pays")
select = Select(element_select)
# Trois façons de sélectionner une option
select.select_by_visible_text("France")
select.select_by_value("fr")
select.select_by_index(2)
# Lire l'option sélectionnée
print(select.first_selected_option.text)
Lire les propriétés d’un élément
element = driver.find_element(By.CSS_SELECTOR, "h1")
print(element.text) # Texte visible
print(element.get_attribute("class")) # Valeur d'un attribut
print(element.is_displayed()) # True si visible
print(element.is_enabled()) # True si actif
Attentes : implicites vs explicites
C’est le point le plus important de ce guide. Les pages modernes chargent leur contenu en JavaScript de façon asynchrone : un élément peut ne pas encore exister au moment où votre script le cherche. Sans attente, vous obtenez des erreurs NoSuchElementException aléatoires.
Attente implicite
L’attente implicite s’applique globalement : à chaque find_element, Selenium réessaie pendant la durée indiquée avant de lever une exception.
driver = webdriver.Chrome()
# Attendre jusqu'à 10 secondes pour chaque recherche d'élément
driver.implicitly_wait(10)
C’est simple, mais insuffisant : l’attente implicite vérifie seulement la présence dans le DOM, pas la visibilité ni la cliquabilité. Un élément peut exister mais être masqué ou désactivé.
Attente explicite (recommandée)
L’attente explicite cible une condition précise sur un élément précis, avec WebDriverWait et le module expected_conditions :
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
driver = webdriver.Chrome()
driver.get("https://www.python.org")
# Attendre jusqu'à 10 secondes que l'élément soit cliquable
wait = WebDriverWait(driver, 10)
bouton = wait.until(
EC.element_to_be_clickable((By.CSS_SELECTOR, "button.search-button"))
)
bouton.click()
driver.quit()
Conditions les plus utiles :
| Condition | Vérifie que |
|---|---|
presence_of_element_located | L’élément existe dans le DOM |
visibility_of_element_located | L’élément existe et est visible |
element_to_be_clickable | L’élément est visible et actif |
text_to_be_present_in_element | Un texte donné est affiché |
invisibility_of_element_located | Un élément (spinner) a disparu |
title_contains | Le titre de la page contient une chaîne |
Règle d’or : n’utilisez jamais time.sleep() pour attendre un élément. Un sleep trop court rend le script instable, un sleep trop long le ralentit inutilement. L’attente explicite s’arrête dès que la condition est remplie. Évitez aussi de mélanger attentes implicites et explicites : les temps d’attente peuvent se cumuler de façon imprévisible.
Mode headless
Le mode headless exécute le navigateur sans interface graphique. Indispensable sur un serveur d’intégration continue :
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
options = Options()
options.add_argument("--headless=new") # Nouveau mode headless de Chrome
options.add_argument("--window-size=1920,1080") # Taille de fenêtre fixe
driver = webdriver.Chrome(options=options)
driver.get("https://www.python.org")
print(driver.title)
# Welcome to Python.org
driver.quit()
Fixez toujours la taille de la fenêtre en headless : par défaut elle est petite et certains éléments peuvent être masqués par un affichage responsive.
Captures d’écran
Les captures sont précieuses pour diagnostiquer les échecs de tests :
# Capture de la page visible
driver.save_screenshot("page.png")
# Capture d'un élément précis
element = driver.find_element(By.ID, "content")
element.screenshot("element.png")
Bonne pratique : capturez systématiquement une image dans le bloc except de vos tests pour voir l’état de la page au moment de l’échec.
Iframes et fenêtres multiples
Iframes
Un élément situé dans une <iframe> est invisible pour Selenium tant que vous n’avez pas basculé le contexte :
# Basculer vers l'iframe (par élément, nom ou index)
iframe = driver.find_element(By.CSS_SELECTOR, "iframe#editeur")
driver.switch_to.frame(iframe)
# Interagir avec le contenu de l'iframe
driver.find_element(By.TAG_NAME, "body").send_keys("Bonjour")
# Revenir au document principal
driver.switch_to.default_content()
Fenêtres et onglets
Chaque fenêtre possède un identifiant (handle). Pour suivre un lien qui ouvre un nouvel onglet :
fenetre_principale = driver.current_window_handle
# Le clic ouvre un nouvel onglet
driver.find_element(By.LINK_TEXT, "Documentation").click()
# Basculer vers le nouvel onglet
for handle in driver.window_handles:
if handle != fenetre_principale:
driver.switch_to.window(handle)
break
print(driver.title) # Titre du nouvel onglet
# Fermer l'onglet et revenir à la fenêtre principale
driver.close()
driver.switch_to.window(fenetre_principale)
Le pattern Page Object
Quand les scripts grossissent, les sélecteurs se répètent partout et chaque changement d’interface casse des dizaines de lignes. Le pattern Page Object résout ce problème : chaque page du site est représentée par une classe qui encapsule ses sélecteurs et ses actions.
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
class PageConnexion:
"""Encapsule la page de connexion : sélecteurs et actions."""
URL = "https://exemple.com/connexion"
CHAMP_EMAIL = (By.ID, "email")
CHAMP_MOT_DE_PASSE = (By.ID, "password")
BOUTON_CONNEXION = (By.CSS_SELECTOR, "button[type='submit']")
MESSAGE_ERREUR = (By.CSS_SELECTOR, ".alert-danger")
def __init__(self, driver):
self.driver = driver
self.wait = WebDriverWait(driver, 10)
def ouvrir(self):
self.driver.get(self.URL)
return self
def se_connecter(self, email, mot_de_passe):
self.wait.until(
EC.visibility_of_element_located(self.CHAMP_EMAIL)
).send_keys(email)
self.driver.find_element(*self.CHAMP_MOT_DE_PASSE).send_keys(mot_de_passe)
self.driver.find_element(*self.BOUTON_CONNEXION).click()
def message_erreur(self):
return self.wait.until(
EC.visibility_of_element_located(self.MESSAGE_ERREUR)
).text
# Utilisation dans un test : le test ne connaît aucun sélecteur
driver = webdriver.Chrome()
page = PageConnexion(driver).ouvrir()
page.se_connecter("utilisateur@exemple.com", "mauvais_mot_de_passe")
assert "identifiants invalides" in page.message_erreur().lower()
driver.quit()
Si l’interface change, vous ne modifiez que la classe PageConnexion : tous les tests qui l’utilisent restent intacts.
Bonnes pratiques anti-flaky
Un test « flaky » échoue de façon aléatoire sans que le code testé ait changé. Voici comment l’éviter :
- Bannissez
time.sleep(): remplacez chaquesleeppar une attente explicite sur une condition observable. - Attendez la bonne condition :
element_to_be_clickableavant un clic,invisibility_of_element_locatedsur le spinner de chargement avant de lire un résultat. - Choisissez des sélecteurs stables : préférez les attributs dédiés aux tests (
data-testid) aux classes CSS générées automatiquement. - Isolez les tests : chaque test crée ses propres données et ne dépend pas de l’ordre d’exécution des autres.
- Fixez la taille de fenêtre : un affichage responsive différent entre votre machine et le serveur CI change la disposition des éléments.
- Capturez l’écran en cas d’échec : une image et le HTML de la page (
driver.page_source) accélèrent énormément le diagnostic. - Utilisez
driver.quit()dans un blocfinallyou un gestionnaire de contexte pour ne jamais laisser de processus orphelins.
Conclusion
Selenium 4 avec Python offre tout le nécessaire pour automatiser un navigateur de manière robuste : la gestion automatique des drivers simplifie l’installation, la classe By et les sélecteurs CSS couvrent la localisation, et les attentes explicites avec WebDriverWait garantissent la fiabilité face aux pages asynchrones. Pour les projets qui dépassent quelques scripts, le pattern Page Object structure le code et le protège des évolutions de l’interface.
Pour aller plus loin, consultez la documentation officielle de Selenium et explorez l’intégration avec pytest pour organiser vos suites de tests.
À propos de InSkillCoach
Expert en formation et technologies
Coach spécialisé dans les technologies avancées et l'IA, porté par GNeurone Inc.
Certifications:
- AWS Certified Solutions Architect – Professional
- Certifications Google Cloud
- Microsoft Certified: DevOps Engineer Expert
- Certified Kubernetes Administrator (CKA)
- CompTIA Security+
Commentaires
Les commentaires sont alimentés par GitHub Discussions
Connectez-vous avec GitHub pour participer à la discussion