Attente explicite avec Selenium
L'attente explicite (ou explicit wait) permet d'attendre qu'une condition spécifique soit satisfaite avant de poursuivre l'exécution du script. Contrairement à l'attente implicite qui s'applique globalement à tous les éléments du DOM, l'attente explicite cible un élément ou une condition particulière. Si la condition est remplie dans le délai imparti, le script continue. Sinon, une exception TimeoutException est levée.
Implémentation via la classe WebDriverWait
Dans Selenium, cette fonctionnalité est assurée par la classe WebDriverWait, située dans le module selenium/webdriver/support/wait.py. Elle repose sur une boucle itérative qui évalue régulièrement une condition donnée jusqu'à son succès ou l'expiration du temps maximal défini.
Signature du constructeur
class WebDriverWait:
def __init__(self, driver, timeout, poll_frequency=0.5, ignored_exceptions=None):
self._driver = driver
self._timeout = timeout
self._poll = poll_frequency
self._ignored_exceptions = ignored_exceptions or ()
- driver : Instance du pilote WebDriver (Chrome, Firefox, etc.) utilisée pour interagir avec le navigateur.
- timeout : Durée maximale en secondes pendant laquelle la condition sera testée.
- poll_frequency : Intervalle en secondes entre chaque tentative de vérification de la condition. Par défaut : 0.5 seconde.
- ignored_exceptions : Tuple d'exceptions à ignorer pendant les tentatives (ex:
NoSuchElementException). Permet d'éviter des erreurs prématurées.
Méthodes principales
La classe expose deux méthodes clés pour contrôler le flux d'attente :
until(method, message="")
Exécute method de manière répétée jusqu'à ce que celle-ci retourne une valeur évaluée comme vraie (truthy). Dès que la condition est satisfaite, la méthode renvoie cette valeur.
until_not(method, message="")
Fonctionne inversement : elle attend que method retourne une valeur fausse (falsy).
Le paramètre method doit être un callable prenant le driver comme unique argument — souvent implémenté via une fonction lambda ou une fonction personnalisée.
Implémentation interne de until()
Voici une version simplifiée du mécanisme interne de until() :
def until(self, method, message=""):
end_time = time.time() + self._timeout
while True:
try:
result = method(self._driver)
if result:
return result
except self._ignored_exceptions as exc:
# Capturer les données utiles pour le débogage
screen = getattr(exc, 'screen', None)
stacktrace = getattr(exc, 'stacktrace', None)
# Pause avant la prochaine tentative
time.sleep(self._poll)
# Vérifier si le temps imparti est écoulé
if time.time() > end_time:
break
# Lever une exception après expiration
raise TimeoutException(message, screen, stacktrace)
Exemple d'utilisation pratique
Le cas typique consiste à attendre qu’un élément soit présent et interactif dans le DOM :
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
# Attendre jusqu'à 10 secondes qu'un élément avec l'ID 'someId' soit cliquable
element = WebDriverWait(driver, 10).until(
EC.element_to_be_clickable((By.ID, "someId"))
)
Ici, EC.element_to_be_clickable est une condition prédéfinie, mais on peut aussi utiliser une lambda :
element = WebDriverWait(driver, 10).until(
lambda d: d.find_element(By.ID, "someId")
)
Cette approche offre une grande flexibilité pour définir des conditions personnalisées.