Привет!)
Возникали ситуации, когда стандартных методов поиска веб-элементов не хватало для полноценной автоматизации? То кнопка не нажимается, то элемент перекрывается, то не подгружается. У всего этого есть разные причины и самые разные решения.
В этой статье как раз таки рассмотрим некоторые из таких проблем.
Для тех кому не интересна база
Стандартные методы работы с веб-элементами и нестандартные проблемы
Всем известно, что основные методы поиска элементов в Selenium - find_element
и find_elements
, которые находят один или несколько элементов соответственно.
Также, все мы знаем, что есть различные селекторы, такие как id
, name
, xpath
, css
. Но интересным моментом является, что поиск по id
или name
осуществляется быстрее, чем тот же поиск по xpath
или css
.
Почему? Достаточно просто объяснить: id
, как идентификатор, по хорошему, должен быть уникальным в пределах всего HTML-документа. В таком случае, браузер или JS движок может найти элемент не просматривая весь DOM.
DOM (Document Object Model) - иерархическая структура, которая представляет собой внутреннее представление веб-страницы в браузере.
С name
примерно та же история, атрибут может быть не уникальным, однако, зачастую, элементы с одинаковым именем сгруппированы по типу.
Что же касается css
и xpath
- они могут включать в себя различные критерии выбора (классы, атрибуты, вложенность), что делает их обработку более сложной. Зачастую, селекторы составные и требуют более детального анализа дерева документов.
Также не забудем про самые обычные методы для взаимодействия с веб-элементами: click
, send_keys
. Зачастую найдя элемент по селектору, условную кнопку, мы можем с этим элементом взаимодействовать, например - кликнуть.
driver.find_element(By.LINK_TEXT, "More information...").click()
Но бывают и не совсем стандартные ситуации.
ElementClickInterceptedException. Элемент накрыло
На самом деле ошибка встречается довольно таки часто. И имеет пару вариантов решения:
Лучший из них - анализ DOM страницы.
Может быть вариант, что перекрывающий элемент - лоадер и стоит просто немного подождать его исчезновения. Можно использовать следующий код для того, чтобы дождаться лоадера:
WebDriverWait(driver, 20).until(EC.invisibility_of_element_located(By.NAME, "loaderName"))
Или может быть вариант, что элемент всегда перекрывается другим.
В таком случае мы можем попробовать перейти выше или ниже по DOM страницы к родительскому или дочернему элементу, чтобы совершить нужное действие.
Но если перекрывающий элемент полностью блокирует взаимодействие с нужным элементом, то можно имитировать нажатие мышкой на кнопку. Selenium
предлагает нам ActionChains
, который можно использовать следующим образом:
Из документации:
ActionChains
- это способ автоматизации низкоуровневых взаимодействий, таких как перемещения мыши, действия с кнопками мыши, нажатия клавиш и взаимодействия с контекстным меню.
from selenium.webdriver.common.action_chains import ActionChains
filter_field = WebDriverWait(driver, 20).until(EC.element_to_be_clickable(By.ID, "filter"))
ActionChains(driver).move_to_element(filter_field).click().perform()
iframe. 25-й кадр
Иногда ошибка может быть связана с фреймом. Что это?
iframe (inline frame) - HTML-элемент, который позволяет встраивать один документ HTML-документ внутрь другого. Иными словами - позволяет загружать и отображать содержимое другого веб-ресурса.
Вместе с тем, это зачастую вызывает TimeoutException
, хоть и элемент находится в DOM страницы. Пример iframe в DOM:
Зачастую решением данной проблемы является переход на frame посредством следующего кода:
driver.switch_to.frame(WebDriverWait(driver, 20).until(EC.visibility_of_element_located(By.ID, "window_frame")))
Мои мысли
В целом, решение большей части проблем заключается в понимании того, что есть DOM.
Некоторые проблемы могут помочь обойти Chrome-опции: ссылочка на статью.