0%
Selenium avec Python : Automatiser le Navigateur

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.

I

InSkillCoach

· min

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égieExempleQuand l’utiliser
By.IDBy.ID, "email"Toujours en priorité si un id stable existe
By.NAMEBy.NAME, "username"Formulaires classiques
By.CSS_SELECTORBy.CSS_SELECTOR, "div.card > a"Polyvalent, rapide, lisible
By.XPATHBy.XPATH, "//button[text()='OK']"Recherche par texte ou remontée dans le DOM
By.CLASS_NAMEBy.CLASS_NAME, "btn-primary"Une seule classe, sans espace
By.TAG_NAMEBy.TAG_NAME, "table"Collections d’éléments
By.LINK_TEXTBy.LINK_TEXT, "Contact"Liens dont le texte exact est connu
By.PARTIAL_LINK_TEXTBy.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 :

ConditionVérifie que
presence_of_element_locatedL’élément existe dans le DOM
visibility_of_element_locatedL’élément existe et est visible
element_to_be_clickableL’élément est visible et actif
text_to_be_present_in_elementUn texte donné est affiché
invisibility_of_element_locatedUn élément (spinner) a disparu
title_containsLe 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 chaque sleep par une attente explicite sur une condition observable.
  • Attendez la bonne condition : element_to_be_clickable avant un clic, invisibility_of_element_located sur 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 bloc finally ou 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.

InSkillCoach

À 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+
1.5k
236

Commentaires

Les commentaires sont alimentés par GitHub Discussions

Connectez-vous avec GitHub pour participer à la discussion

Lien copié !