Python: Selenium

Теория: Механизмы ожиданий

Разница между implicitly_wait и WebDriverWait

Когда Selenium работает с браузером, страница может загружаться не сразу: часть элементов появляется с задержкой, особенно при асинхронной подгрузке через JavaScript. Если тест попытается найти элемент раньше, чем он появится, возникнет ошибка NoSuchElementException. Чтобы этого избежать, в Selenium есть механизмы ожиданий — они позволяют подождать появления элемента перед тем, как к нему обращаться.

Неявное ожидание (implicitly_wait)

Неявное ожидание действует глобально. Его достаточно один раз установить, и Selenium будет автоматически ждать появления каждого элемента до заданного времени. Если элемент появился раньше, выполнение продолжается сразу, без паузы.

from selenium import webdriver

driver = webdriver.Chrome()
driver.implicitly_wait(5)  # ждать до 5 секунд при каждом поиске элемента

driver.get("https://example.com")
driver.find_element("id", "login-button").click()

Если за 5 секунд элемент так и не появился, выбрасывается ошибка. Это простой и удобный вариант, когда все элементы загружаются примерно одинаково. Но если на странице есть разные задержки, неявное ожидание не всегда подходит — оно одинаковое для всех случаев.

Явное ожидание (WebDriverWait)

Явное ожидание используют, когда нужно дождаться конкретного события или элемента. В отличие от implicitly_wait, оно настраивается точечно для каждой ситуации.

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://example.com")

## ждать до 10 секунд, пока кнопка не станет кликабельной
button = WebDriverWait(driver, 10).until(
    EC.element_to_be_clickable((By.ID, "login-button"))
)
button.click()

Метод until() ждёт, пока выполняется переданное условие. Если условие стало истинным раньше — ожидание завершается сразу. Если нет — по истечении времени выбрасывается TimeoutException.

Примеры условий из модуля expected_conditions:

EC.presence_of_element_located((By.ID, "username"))      # элемент появился в DOM
EC.visibility_of_element_located((By.CLASS_NAME, "msg")) # элемент виден
EC.element_to_be_clickable((By.XPATH, "//button"))        # элемент доступен для клика

Разница между implicitly_wait и WebDriverWait

implicitly_wait — это общий таймер на все поиски. Он прост, но не гибок. Selenium просто повторяет попытки поиска, пока не истечёт заданное время. WebDriverWait — это явное ожидание для конкретного действия. Оно используется тогда, когда важно дождаться определённого состояния элемента: появления, кликабельности, исчезновения и т.д.

В одном проекте можно использовать оба механизма, но в большинстве случаев предпочтительнее WebDriverWait. Он даёт полный контроль над поведением теста и снижает риск случайных сбоев из-за нестабильной загрузки страницы.

ExpectedConditions

Когда используется явное ожидание (WebDriverWait), ему нужно передать условие, при котором ожидание завершится. Эти условия находятся в модуле selenium.webdriver.support.expected_conditions, который обычно сокращают до EC. Он содержит готовые проверки — их не нужно писать вручную. Selenium каждые 500 мс проверяет, выполняется ли условие. Как только оно становится истинным — тест идёт дальше.

Импорт выглядит так:

from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By

Рассмотрим самые часто используемые условия.

  1. presence_of_element_located. Ждёт, пока элемент появится в DOM. Видимым он при этом может и не быть.

    element = WebDriverWait(driver, 10).until(
        EC.presence_of_element_located((By.ID, "username"))
    )
  2. visibility_of_element_located. Ждёт, пока элемент станет видимым на странице (присутствует в DOM и имеет ненулевые размеры).

visible_elem = WebDriverWait(driver, 10).until(
    EC.visibility_of_element_located((By.CLASS_NAME, "success-msg"))
)
  1. element_to_be_clickable. Ждёт, пока элемент станет доступен для клика — то есть видим и не заблокирован.
button = WebDriverWait(driver, 10).until(
    EC.element_to_be_clickable((By.XPATH, "//button[text()='Войти']"))
)
button.click()
  1. text_to_be_present_in_element. Ждёт, пока в элементе появится нужный текст. Полезно при проверке сообщений или уведомлений.

    WebDriverWait(driver, 10).until(
        EC.text_to_be_present_in_element((By.ID, "status"), "Успешно")
    )
  2. invisibility_of_element_located. Ждёт, пока элемент исчезнет со страницы. Часто применяют для загрузочных спиннеров.

    WebDriverWait(driver, 10).until(
        EC.invisibility_of_element_located((By.CLASS_NAME, "loading"))
    )
  3. frame_to_be_available_and_switch_to_it Ждёт, пока появится iframe, и автоматически переключается внутрь.

    WebDriverWait(driver, 10).until(
        EC.frame_to_be_available_and_switch_to_it((By.NAME, "main-frame"))
    )
  4. alert_is_present Ждёт появления системного окна (alert, confirm, prompt).

    alert = WebDriverWait(driver, 10).until(EC.alert_is_present())
    print(alert.text)
    alert.accept()
  5. element_located_selection_state_to_be Ждёт, пока чекбокс или радиокнопка перейдёт в нужное состояние.

    checkbox = (By.ID, "terms")
    WebDriverWait(driver, 10).until(
        EC.element_located_selection_state_to_be(checkbox, True)
    )

Все эти функции возвращают найденный элемент (или True в случае условий без элемента), если условие выполнилось, или вызывают TimeoutException, если время истекло.

ExpectedConditions делают тесты устойчивыми. Они позволяют синхронизировать действия Selenium с поведением страницы и исключают случайные ошибки вроде «элемент не найден» или «не кликабелен».

Ожидания видимости, кликабельности и других состояний элементов

Когда страница загружается, элементы не всегда сразу становятся доступны для действий. Они могут быть созданы JavaScript-ом позже, находиться под оверлеем, быть прозрачными или заблокированными. Если попытаться кликнуть слишком рано — Selenium выбросит ошибку ElementNotInteractableException или ElementClickInterceptedException. Чтобы избежать этого, используют ожидания состояний элементов — специальные проверки, которые гарантируют, что элемент готов к взаимодействию.

Ожидание видимости

EC.visibility_of_element_located() ждёт, пока элемент появится в DOM и станет видимым — то есть не имеет display: none и имеет размеры больше нуля. Это одно из самых частых ожиданий при загрузке контента или сообщений.

from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

wait = WebDriverWait(driver, 10)

visible_msg = wait.until(
    EC.visibility_of_element_located((By.CLASS_NAME, "notification"))
)
print(visible_msg.text)

Если элемент появился и стал видим, wait.until() возвращает его. Если нет — по истечении тайм-аута вылетит TimeoutException.

Ожидание кликабельности

EC.element_to_be_clickable() проверяет два условия одновременно: элемент видим и не заблокирован (disabled=False). Это важно для кнопок и ссылок, особенно когда интерфейс сначала рисует кнопку, а потом делает её активной.

login_button = wait.until(
    EC.element_to_be_clickable((By.ID, "login-button"))
)
login_button.click()

Без этого ожидания тест может кликнуть по элементу в момент, когда он ещё не активен — и клик не сработает.

Ожидание присутствия

EC.presence_of_element_located() ждёт, пока элемент появится в DOM. Он не обязан быть видимым, но уже существует в структуре страницы. Это базовое ожидание при поиске динамически подгружаемых элементов.

input_field = wait.until(
    EC.presence_of_element_located((By.NAME, "username"))
)
input_field.send_keys("guest")

Ожидание исчезновения

EC.invisibility_of_element_located() используется, когда нужно дождаться, чтобы элемент пропал со страницы. Часто применяется для загрузочных спиннеров или всплывающих блоков.

wait.until(EC.invisibility_of_element_located((By.CLASS_NAME, "loading")))

Это гарантирует, что страница полностью готова к дальнейшим действиям.

Ожидание появления текста

EC.text_to_be_present_in_element() ждёт, пока в элементе появится конкретный текст. Применяется для проверки уведомлений, статусов или результатов выполнения действий.

wait.until(
    EC.text_to_be_present_in_element((By.ID, "status"), "Успешно")
)

Ожидание алерта

EC.alert_is_present() ждёт появления системного окна. Без этого тест не сможет взаимодействовать с alert, confirm или prompt.

alert = wait.until(EC.alert_is_present())
print(alert.text)
alert.accept()

Ожидание появления и перехода в iframe

EC.frame_to_be_available_and_switch_to_it() дожидается появления iframe и автоматически переключает контекст Selenium внутрь него.

wait.until(EC.frame_to_be_available_and_switch_to_it((By.NAME, "main-frame")))

В итоге

Каждое ожидание решает свою задачу.

  • presence_of_element_located — элемент появился.
  • visibility_of_element_located — элемент виден.
  • element_to_be_clickable — элемент доступен для клика.
  • invisibility_of_element_located — элемент исчез.
  • text_to_be_present_in_element — появился нужный текст.
  • alert_is_present — появилось окно.

Тайм-ауты и обработка ошибок ожиданий

Когда тест ждёт появления элемента или нужного состояния, Selenium проверяет условие каждые полсекунды. Если за заданное время условие не выполнилось — выбрасывается исключение. Это исключение называется TimeoutException. Оно означает, что элемент так и не появился, не стал кликабельным или не исчез, как ожидалось. Работа теста останавливается, если ошибку не перехватить.

Тайм-ауты

Тайм-аут задаётся в секундах при создании ожидания. Например, WebDriverWait(driver, 10) — это 10 секунд. Если элемент появится через 2 секунды, ожидание завершится сразу. Если не появится, Selenium будет проверять условие каждые 0.5 секунды до конца тайм-аута.

from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

wait = WebDriverWait(driver, 10)

button = wait.until(
   EC.element_to_be_clickable((By.ID, "login-button"))
)
button.click()

Тайм-аут можно изменить под конкретный тест. Если страница грузится дольше, значение увеличивают. Если все элементы появляются мгновенно — уменьшают.

Обработка ошибок ожиданий

Чтобы тест не падал при каждом тайм-ауте, ошибки можно перехватывать через try/except. Это особенно важно, когда элемент может появляться не всегда, например, сообщение об ошибке после неудачного логина.

from selenium.common.exceptions import TimeoutException

try:
    message = wait.until(
        EC.visibility_of_element_located((By.CLASS_NAME, "error-message"))
    )
    print("Ошибка:", message.text)
except TimeoutException:
    print("Сообщение об ошибке не появилось")

В этом примере тест не рухнет, если элемент не найден за 10 секунд. Он просто выведет сообщение в консоль. Такой подход помогает сделать тесты устойчивыми: ошибки контролируются, а сценарий может продолжать выполнение.

Тайм-аут при исчезновении элементов

Иногда нужно дождаться, пока элемент пропадёт. Если он не исчез вовремя, тест тоже получит TimeoutException. Например:

try:
    wait.until(EC.invisibility_of_element_located((By.CLASS_NAME, "loading")))
except TimeoutException:
    print("Загрузка не исчезла, страница зависла")

Это полезно для проверки стабильности интерфейса — если спиннер не пропал, значит, страница не готова.

Разница между тайм-аутами WebDriver и WebDriverWait

У браузера есть собственный тайм-аут загрузки страницы (set_page_load_timeout) и ожидания скриптов (set_script_timeout). Они отвечают за общие процессы, а не за элементы. WebDriverWait — это точечное ожидание конкретных условий. Его используют чаще всего в тестах, потому что он даёт полный контроль над проверкой состояний.

driver.set_page_load_timeout(30)     # ждём загрузку страницы до 30 секунд
driver.set_script_timeout(10)        # ждём выполнения JS-скриптов до 10 секунд

Selenium не ждёт автоматически, что всё появится мгновенно. Любое действие с элементом нужно защищать ожиданием. Тайм-аут определяет, сколько секунд тест будет проверять условие. Если время вышло — срабатывает TimeoutException, и дальше всё зависит от того, обрабатываете вы её или нет.

Рекомендуемые программы

+7 800 100 22 47

бесплатно по РФ

+7 495 085 21 62

бесплатно по Москве

108813 г. Москва, вн.тер.г. поселение Московский,
г. Московский, ул. Солнечная, д. 3А, стр. 1, помещ. 20Б/3
ОГРН 1217300010476
ИНН 7325174845