Як виправити помилку 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 на одному сайті.