Как исправить ошибку NotFoundException в Selenium?

Share
Share
Send

Исключение NotFoundException в Selenium — одна из самых распространенных ошибок при написании скриптов автоматизации. Обычно, она возникает при попытке найти тот или иной элемент на странице методом find_element:

element = driver.find_element(By.ID, "buton")

Эта ошибка никогда не возникает, если пользоваться методом find_elements, т. к. он возвращает пустой список [] в том случае, если элементы не найдены на странице.

Selenium WebDriver поддерживает 8 способов, как найти элемент на странице:

  • по ID элемента (By.ID)
  • по XPath (By.XPATH)
  • по тексту ссылки (By.LINK_TEXT)
  • по частичному тексту ссылки (By.PARTIAL_LINK_TEXT)
  • по аттрибуту NAME элемента (By.NAME)
  • по тегу TAG_NAME (By.TAG_NAME)
  • по имени CSS-класса или комбинации (By.CLASS_NAME)
  • по CSS-селектору (By.CSS_SELECTOR)

Комбинация способа поиска и строки называется локатором. Например, локатор By.ID, "button" найдет элемент <a href="..." id="button">Click me</a>, а локатор By.NAME, "login" найдет элемент <input type="text" name="login">.

Почему возникает ошибка NoSuchElementException? Если неправильно указать локатор при поиске элемента или поискать его на неподходящей странице или в неподходящее время, то может возникнуть ошибка NoSuchElementException.

Как выглядит сообщение об ошибке NoSuchElementException в консоли

Обычно, в консоли подобное сообщение имеет три важных фрагмента:

  • класс исключения selenium.common.exceptions.NoSuchElementException
  • сообщение Message: no such element: Unable to locate element
  • и каким локатором искали элемент: {"method":"css selector","selector":"[id="buton"]"} (этот фрагмент будет в каждом отдельном случае разный, в данном примере локтор By.ID, "buton").

Полный текст сообщения (локатор в каждом отдельном случае будет разный):

selenium.common.exceptions.NoSuchElementException: Message: no such element: Unable to locate element: {"method":"css selector","selector":"[id="buton"]"}

Неправильный локатор

Самый первый шаг в исправлении ошибки NotFoundException — это перепроверить локатор.

  1. Нужно убедиться, что используется правильный способ поиска элемента (By.ID, By.NAME, By.XPATH и так далее).
  2. Убедиться, что передается правильная строка вместе со способом.

В этом помогает инструмент Google Chrome DevTools, который может показать HTML-структуру страницы и осуществить поиск по CSS-селектору или XPath-селектору.

Нужно открыть тестируемую страницу и выбрать в контекстном меню пункт "Inspect":

Google Chrome Devtools

Затем выбрать вкладку "Elements", и нажать Ctrl+F (Cmd+F), затем в поисковой строке вписать требуемый CSS-селектор или Xpath и убедиться, что находится как минимум один элемент:

Google Chrome Devtools

Если элемент не найден, то локатор неверный. В этом же окне можно попробовать различные способы поиска элемента (различные селекторы) и подобрать рабочий вариант.

Иногда сложно понять, какой именно оператор find_element вызывает исключение NoSuchElementException, особенное если таких операторов много. Здесь может пригодиться отладчик PyCharm.

Для начала нужно открыть меню Run — View Breakpoints...

PyCharm All Breakpoints menu

Затем поставить галочку напротив Python Exception Breakpoint — Any Exception, как показано на скриншоте ниже:

PyCharm Any Exception Breakpoint

Затем нужно запустить скрипт снова. В момент возникновения исключения NoSuchElementException, отладчик остановится. Нужно выбрать в стеке вызовов имя нашего скрипта и PyCharm покажет строчку кода, в которой происходит исключение:

PyCharm Any Exception Breakpoint

Не та страница

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

Для диагностики такой проблемы рекомендую установить брейкпоинт на том операторе find_element (как показано на скриншоте ниже), который приводит к исключению NoSuchElementException и перезапустить скрипт. В момент остановки обратите внимание, какая открыта страница в браузере, присутствует ли там искомый элемент. Это можно проверить через Google Chrome DevTools (см. предыдущий пункт).

PyCharm Breakpoint in Source code

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

Элемент еще не появился в момент поиска

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

Python-скрипты работают очень быстро, а интернет работает относительно медленно. Рассмотрим пример ниже, в котором мы делаем два действия: кликаем по кнопке "Логин" и сразу же после логина кликаем по ссылке "Profile":

# Находим кнопку входа и логинимся.
driver.find_element(By.ID, "login").click()

# Открываем профиль.
driver.find_element(By.LINK_TEXT, "Profile").click()

С т. з. пользователя все выглядит логично: два последовательных действия: логинимся, кликаем на профиль.

Здесь нужно знать один важный факт: процесс скрипта автоматизации и процесса веб-приложения работают параллельно, и никак не синхронизируются между собой. Поэтому скрипт автоматизации после логина сразу же переходит к следующей инструкции: клик по профилю. Но у всех веб-серверов есть небольшая задержка между действиями, поэтому в момент клика по профилю эта страница еще не загружена полностью.

Типичными симптомами такой ошибки NoSuchElementException явлюется тот факт, что даже под отладчиком отображается правильная страница и что искомый элемент находится на странице.

Решение этой проблемы — это ожидание наличия элемента на странице с помощью класса WebDriverWait. Обратите внимание, что под наличием элемента на странице подразумевается присутствие искомого элемента в HTML DOM, а не визуальное пристуствтие на странице.

Решение, в котором скрипт автоматизации будет ожидать появления ссылки с текстом Profile на странице:

from selenium.webdriver.support import expected_conditions as EC

# Находим кнопку входа и логинимся.
driver.find_element(By.ID, "login").click()

# Ожидаем появления ссылки на странице.
WebDriverWait(driver, timeout=10).until(
     EC.presence_of_element_located((By.LINK_TEXT, "Profile"))
)

# Открываем профиль.
driver.find_element(By.LINK_TEXT, "Profile").click()

В таком случае скрипт автоматизации перед тем, как кликнуть по ссылке, дождется появления ее на странице.

Выводы

Если вы столкнулись с ошибкой NoSuchElementException, то алгоритм проверки следующий:

  1. Проверьте с помощью Google Chrome DevTools что элемент с таким локатором существует на странице.
  2. Убедитесь, что в момент поиска элемента скрипт доходит до требуемой страницы.
  3. Добавьте явное ожидание искомого элемента с помощью WebDriverWait.

Надежный Python

Присоединись к культуре надежного программирования на Python! Новости, события, мнения, обновления библиотек Python на одном сайте.