Как исправить ошибку NotFoundException в Selenium?
Исключение 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 — это перепроверить локатор.
- Нужно убедиться, что используется правильный способ поиска элемента (By.ID, By.NAME, By.XPATH и так далее).
- Убедиться, что передается правильная строка вместе со способом.
В этом помогает инструмент Google Chrome DevTools, который может показать HTML-структуру страницы и осуществить поиск по CSS-селектору или XPath-селектору.
Нужно открыть тестируемую страницу и выбрать в контекстном меню пункт "Inspect":
Затем выбрать вкладку "Elements", и нажать Ctrl+F (Cmd+F), затем в поисковой строке вписать требуемый CSS-селектор или Xpath и убедиться, что находится как минимум один элемент:
Если элемент не найден, то локатор неверный. В этом же окне можно попробовать различные способы поиска элемента (различные селекторы) и подобрать рабочий вариант.
Иногда сложно понять, какой именно оператор find_element
вызывает исключение NoSuchElementException, особенное если таких операторов много. Здесь может пригодиться отладчик PyCharm.
Для начала нужно открыть меню Run — View Breakpoints...
Затем поставить галочку напротив Python Exception Breakpoint — Any Exception, как показано на скриншоте ниже:
Затем нужно запустить скрипт снова. В момент возникновения исключения NoSuchElementException, отладчик остановится. Нужно выбрать в стеке вызовов имя нашего скрипта и PyCharm покажет строчку кода, в которой происходит исключение:
Не та страница
Если вы перепроверили локатор и убедились, что он корректный: правильный способ поиска и элемент действительно присутствует на странице, то нужно убедиться, что в момент поиска find_element
открыта требуемая страница. Ведь если мы находимся не на той странице, то конечно же мы ничего не найдем. Очень сложно искать черную кошку в темной комнате, особенно если ее там нет.
Для диагностики такой проблемы рекомендую установить брейкпоинт на том операторе find_element
(как показано на скриншоте ниже), который приводит к исключению NoSuchElementException и перезапустить скрипт. В момент остановки обратите внимание, какая открыта страница в браузере, присутствует ли там искомый элемент. Это можно проверить через Google Chrome DevTools (см. предыдущий пункт).
Если в данный момент в браузере отображается какая-то неправильная страница, то нужно перепроверить предыдущие шаги скрипта и разобраться, почему эти шаги не приводят к открытию желаемой страницы.
Элемент еще не появился в момент поиска
Если на предыдущих шагах вы убедились, что локатор правильный и что в момент поиска вы находитесь на требуемой странице, то вероятно, что нужно дождаться появления искомого элемента в коде страницы.
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, то алгоритм проверки следующий:
- Проверьте с помощью Google Chrome DevTools что элемент с таким локатором существует на странице.
- Убедитесь, что в момент поиска элемента скрипт доходит до требуемой страницы.
- Добавьте явное ожидание искомого элемента с помощью WebDriverWait.