Selenium
Введение
Selenium — это не просто инструмент для тестирования, это ваш личный робот-помощник, который готов выполнять за вас любые задачи в браузере, такие как клики по элементам, ввод текста, переход по ссылкам, создание скриншотов, и многое другое. Рассмотрим основные понятия и компоненты Selenium.
Selenium поддерживает все основные браузеры, но примеры будут только про браузер Chrome, т.к. он является самым популярным браузером. К тому же использование других браузеров отличается только способом установки webdriver.

Парсеры на Selenium могут быть написаны на нескольких языках программирования: Java, C#, Python, JavaScript, Ruby и Kotlin. Нас же, конечно, интересует язык Python, с которым мы умеем обращаться.
Сценарные языки программирования, такие как Ruby и Python, больше подходят для скриптов, чем компилируемые языки, такие как C# и Java.
Итак, Selenium - это программная библиотека для управления браузерами. Часто употребляется также более короткое название WebDriver. На самом деле, это целое семейство драйверов для различных браузеров, а также набор библиотек на разных языках, позволяющих работать с этими драйверами.
Webdriver - Это основной продукт, разрабатываемый в рамках проекта Selenium.

В семейство Selenium входят такие вещи как:
-
Selenium RC – это предыдущая версия библиотеки для управления браузерами.
-
Selenium Server – это сервер, который позволяет управлять браузером с удалённой машины, по сети.
-
Selenium Grid – это кластер, состоящий из нескольких Selenium-серверов.
-
Selenium IDE – плагин к браузеру Firefox, который может записывать действия пользователя.
-
Selenium WebDriver — библиотека для управления браузерами.
Основные понятия в Selenium Webdriver
Webdriver является основным компонентом в Selenium для автоматизации действий в веб-браузере. Это интерфейс, который позволяет отправлять команды напрямую в браузер и контролировать его. Создание экземпляра WebDriver инициирует сессию браузера, которая используется для выполнения операций, таких как открытие веб-страниц, клик по элементам, ввод текста и многое другое.
Пример создания экземпляра WebDriver для Google Chrome:
from selenium import webdriver
browser = webdriver.Chrome()
browser.get("http://example.com")
WebElement представляет собой элемент на веб-странице, с которым Selenium может взаимодействовать. Это может быть кнопка, ссылка, текстовое поле и любой другой элемент на странице. Каждый WebElement обладает методами для взаимодействия, такими как клик click(), ввод текста send_keys(), получение текста элемента text и многими другими.
Пример взаимодействия с WebElement:
element = browser.find_element(By.ID, 'q') # Находим элемент по его имени
element.send_keys('Selenium') # Вводим текст в текстовое поле
element.submit() # Отправляем форму
By - это класс в Selenium, который предоставляет набор методов для нахождения элементов на странице. Эти методы используются вместе с методами find_element(By.TAG_NAME) и find_elements.(By.ID). Существуют различные стратегии поиска, такие как поиск по идентификатору (ID), имени (NAME), классу (CLASS_NAME), CSS-селектору (CSS_SELECTOR), XPath (XPATH) и другие.
Пример использования класса By:
from selenium.webdriver.common.by import By
element = browser.find_element(By.NAME, 'q') # Находим элемент по имени
Локаторы используются в Selenium для поиска элементов на веб-странице. В этом курсе мы ещё вернёмся к локаторам и подробнее изучим каждый, а так же потренируемся их использовать. Существует несколько типов локаторов:
ID: Находит элемент по его уникальному идентификатору.
Name: Ищет элементы по атрибуту name.
XPath: Позволяет находить элементы с использованием XPath выражений.
CSS Selector: Использует CSS-селекторы для поиска элементов.
Link Text: Находит ссылку по её тексту.
Partial Link Text: Поиск ссылки по части её текста.
Tag Name: Ищет элементы по имени тега.
Class Name: Находит элементы по имени класса.
Ожидания в Selenium используются для управления временем ожидания появления элементов на веб-странице. Вас так же ждёт подробный разбор данной темы в этом курсе, наберитесь терпения, а мы продолжаем. Существует два типа ожиданий:
Неявные ожидания (Implicit Waits): Устанавливают общее время ожидания появления элемента перед тем, как выбросить исключение.
Явные ожидания (Explicit Waits): Позволяют задать условие для ожидания конкретного элемента, что обеспечивает большую гибкость по сравнению с неявными ожиданиями.
После того как элемент был найден с помощью локатора, Selenium позволяет выполнять с ним различные действия, такие как:
Ввод текста send_keys()
Клики click()
Получение текста элемента .text
Проверка видимости элемента is_displayed()
Selenium IDE — это плагин для браузеров Firefox и Chrome, который позволяет записывать, редактировать и воспроизводить тесты. IDE хорошо подходит для быстрого создания тестов без необходимости писать код, однако его возможности ограничены по сравнению с WebDriver.
Selenium Grid позволяет одновременно запускать тесты на разных машинах и в разных браузерах. Это обеспечивает возможность параллельного тестирования, что значительно ускоряет процесс тестирования и помогает проверить работу веб-приложения в различных условиях. Мы так же не будем разговаривать в этом курсе про Selenium Grid.
Вот как можно оформить ваш текст в формате Markdown с удобной структурой, заголовками и списками:
Преимущества Selenium
Преимущества Selenium являются ключевыми факторами, объясняющими его широкое применение и популярность в области автоматизации тестирования веб-приложений. Ниже приведены дополнительные преимущества, которые делают этот инструмент таким востребованным:
1) Поддержка всех основных браузеров Selenium поддерживает такие браузеры, как Google Chrome, Mozilla Firefox, Internet Explorer, Safari и Opera. Это позволяет легко проводить кроссбраузерное тестирование, что критически важно для проверки совместимости веб-приложений.
2) Биндинги на разных языках программирования Selenium предоставляет API для множества популярных языков программирования: Java C# Python Ruby JavaScript
Это обеспечивает гибкость в выборе технологий и возможность использовать уже знакомые языки.
3) Интеграция с инструментами и фреймворками Selenium легко интегрируется с такими инструментами, как: - TestNG / JUnit — для управления тестами - Maven / Gradle — для автоматизации сборки - Jenkins / Travis CI — для непрерывной интеграции
Такая гибкая интеграция расширяет возможности автоматизации и упрощает внедрение тестов в процессы CI/CD.
4) Масштабируемость с помощью Selenium Grid С помощью Selenium Grid можно запускать тесты параллельно на нескольких машинах и браузерах, что значительно сокращает время выполнения тестов. Особенно полезно при работе над большими проектами.
5) Поддержка сложных сценариев Selenium позволяет реализовывать тестовые сценарии, включающие: - Тестирование Ajax-запросов - Работу с всплывающими окнами и фреймами - Drag-and-drop операции - И другие сложные действия
Это делает его мощным инструментом для комплексного тестирования.
6) Активное сообщество и документация Благодаря активному сообществу и обширной документации пользователи могут быстро находить решения возникающих проблем, а также делиться опытом и лучшими практиками.
7) Бесплатный и с открытым исходным кодом Selenium является бесплатным инструментом с открытым исходным кодом, что делает его доступным для компаний любого размера и снижает затраты на лицензирование.
Недостатки Selenium
Недостатки Selenium могут влиять на выбор инструментов автоматизации, особенно в специфических условиях или при определенных требованиях к проекту.
1) Ограниченная область применения Selenium предназначен исключительно для веб-браузеров. Для тестирования мобильных или десктоп-приложений, а также API, потребуются дополнительные инструменты, такие как Appium или Postman.
2) Высокие требования к ресурсам Работа с реальными браузерами требует значительных системных ресурсов (особенно RAM). Например, Google Chrome известен высоким потреблением памяти, что может вызвать проблемы на слабых устройствах.
3) Нестабильность работы скриптов Тесты могут работать нестабильно из-за: - Изменений во внешнем виде страницы - Задержек в загрузке - Обновления браузера или драйвера
Однако такие случаи становятся всё реже благодаря улучшению инструментов и подходов к ожиданиям.
4) Зависимость от браузерных драйверов Для каждого браузера нужен свой драйвер (например, ChromeDriver), который должен быть совместим с версией браузера и Selenium. При каждом обновлении браузера может понадобиться обновление драйвера.
5) Отсутствие встроенной поддержки отчетности Selenium не предоставляет встроенные средства отчетности. Для создания отчетов требуется интеграция с другими фреймворками, например, TestNG или JUnit.
6) Трудности с тестированием всплывающих окон и нативных компонентов Selenium работает в контексте веб-страницы, поэтому взаимодействовать с: - Всплывающими окнами аутентификации - Файловыми диалогами - Другими нативными элементами ОС
7) Сложность работы с динамическими элементами Современные веб-приложения часто используют AJAX и динамическую подгрузку данных. Для корректной работы с такими элементами необходимо использовать явные ожидания, что увеличивает сложность тестовых сценариев.
Установка Selenium: два подхода
При работе с Python и Selenium у вас есть два основных пути установки: без виртуального окружения и с виртуальным окружением. Каждый из них имеет свои преимущества и подходит для разных сценариев. Давайте разберем оба варианта. Если у вас пока что мало опыта, однозначно выбирайте 1 вариант.
- Установка Selenium без виртуального окружения
Этот подход подходит для тех, кто только начинает изучать Python или Selenium и не хочет усложнять процесс настройки. Однако он может привести к конфликтам зависимостей, если вы работаете над несколькими проектами с разными версиями библиотек. Для установки Selenium используйте менеджер пакетов pip, который входит в состав Python. Введите в консоль: pip install selenium эта команда установит последнюю версию Selenium.Чтобы убедиться, что всё установлено корректно, выполните команду pip show selenium

- Установка Selenium с виртуальным окружением
Рекомендуется использовать виртуальное окружение (venv), это позволяет изолировать зависимости для разных проектов, своего рода контейнер, в который мы складываем только то, что нам нужно и не зависимый от внешней среды. Это особенно полезно, если у вас несколько проектов с разными версиями библиотек.
Создание и активация виртуального окружения:
python -m venv myenv # Создание виртуального окружения
source myenv/bin/activate # Активация (Linux/Mac)
myenv\Scripts\activate # Активация (Windows)
И уже после активации окружения установите Selenium.
Поиск элементов Selenium
Локатор - это способ идентификации элементов на странице. Это аргумент, передаваемый методам поиска элементов.
Локаторы играют очень важную роль при работе с Selenium. Они обеспечивают путь к веб-элементам, которые необходимы для автоматизации определенных действий, таких как клик, ввод, установка флага и др.
Рекомендация — делать локаторы максимально компактными и читабельными. Просить WebDriver обойти структуру DOM — это дорогостоящая операция, и чем больше вы сможете сузить область поиска, тем лучше.
Для начала, импортируем класс By из модуля selenium.webdriver.common.by:
from selenium.webdriver.common.by import By
Выглядит Класс By следующим образом
Класс By — это, по сути, перечисление, которое содержит различные стратегии поиска элементов на веб-странице. Использование By позволяет сделать код более читаемым и поддерживаемым, так как это стандартизирует способы поиска элементов.
1) By.ID – Поиск элемента по уникальному идентификатору (id). Этот метод очень быстрый и надежный, но требует, чтобы у элемента был атрибут id.
element = browser.find_element(By.ID, "some_id")
2) By.CSS_SELECTOR – Поиск элемента или элементов, используя селекторы CSS. Это гибкий и мощный метод, который может выразить сложные критерии поиска.
elements = browser.find_elements(By.CSS_SELECTOR, ".some_class")
3) By.XPATH – Поиск элемента с помощью языка XPath. XPath позволяет создать более сложные запросы, но он менее читаемый и, возможно, будет работать медленнее, чем другие методы.
element = browser.find_element(By.XPATH, "//div[@attribute='value']")
3) By.NAME – Поиск элемента по атрибуту name. Этот метод хорошо подходит для форм.
element = browser.find_element(By.NAME, "username")
4) By.TAG_NAME – Поиск элемента по названию HTML-тега. Этот метод полезен, если нужно выбрать, например, все изображения на странице.
images = browser.find_elements(By.TAG_NAME, "img")
5) By.CLASS_NAME – Поиск элемента или элементов по классу. Этот метод полезен, если у элементов есть общий класс.
buttons = browser.find_elements(By.CLASS_NAME, "btn")
6) By.LINK_TEXT – Поиск элемента по точному тексту ссылки. Очень удобно, если текст уникален.
element = browser.find_element(By.LINK_TEXT, "Continue")
7) By.PARTIAL_LINK_TEXT – Поиск элемента по частичному тексту ссылки. Удобно, когда точный текст ссылки неизвестен или динамичен.
element = browser.find_element(By.PARTIAL_LINK_TEXT, "Cont")
Локаторы используются с помощью двух универсальных методов - find_element(), который возвращает ровно один элемент, найденный первым, и find_elements(), который возвращает список найденных элементов. Первый параметр является позиционным, его так же можете передавать строкой. Например:
find_element(By.ID, "some_id") = find_element("id", "some_id")
find_element(By.XPATH, "some_id") = find_element("xpath", "some_id")
.find_element()
Метод find_element() используется, когда вам нужно найти один конкретный элемент на странице. Он возвращает первый элемент, который соответствует заданным критериям поиска. Если элемент не найден, Selenium сгенерирует исключение NoSuchElementException.
# Ищем элемент с тегом img
element = browser.find_element(By.TAG_NAME, 'img')
.find_elements()
Метод find_elements() полезен, когда вы хотите получить список всех элементов, которые соответствуют заданным критериям. В отличие от find_element(), этот метод вернёт пустой список, если ничего не найдено, вместо того чтобы генерировать исключение.
# Ищем все элементы с классом some_class
elements = browser.find_elements(By.CLASS_NAME, 'some_class')
Пример кода с поиском элемента и клика по нему..find_element(By.ID, "sale_button")
from selenium import webdriver
from selenium.webdriver.common.by import By
import time
browser = webdriver.Chrome()
browser.get('http://parsinger.ru/html/watch/1/1_1.html')
button = browser.find_element(By.ID, "sale_button").click()
time.sleep(10)
browser.quit()
Работаем с браузером
Когда скрипт отработает, мы бы хотели, чтобы он закрылся сам и тем самым корректно завершил свою работу. Но это может не произойти по некоторым причинам. Поэтому мы должны указать браузеру, что он должен закрыть окно после завершения работы, командой browser.quit(). Важно закрывать окно, потому что при создании webdriver.Chrome() создается процесс в ОС, который продолжит висеть. Команда quit() проще, чем закрывать окно браузера вручную, к тому же вы не будете засорять оперативную память.
Расставим таймауты в коде, чтобы видеть процесс выполнения кода и чтобы браузер не закрывался за мгновение.
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
browser = webdriver.Chrome()
browser.get('http://parsinger.ru/html/watch/1/1_1.html')
button = browser.find_element(By.ID, "sale_button")
time.sleep(2)
button.click()
time.sleep(2)
# Закрываем окно браузера
browser.quit()
Если ошибка произойдет во время выполнения кода до команды .quit(), сеанс WebDriver не будет закрыт должным образом, и файлы не будут удалены из памяти.
Для того, чтобы код гарантированно завершил свою работу командой quit(), используем конструкцию try/finally. Весь код после finally: будет гарантированно выполнен.
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
try:
browser = webdriver.Chrome()
browser.get('http://parsinger.ru/html/watch/1/1_1.html')
button = browser.find_element(By.ID, "sale_button")
time.sleep(2)
button.click()
time.sleep(2)
finally:
# Окно браузера закроется в любом случае
browser.quit()
Но есть еще третий способ, — это менеджер контекста with/as. С этим способом нам вообще не нужно думать о том, когда закрывать браузер, менеджер контекста делает это за нас в тот момент, когда это нужно
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
with webdriver.Chrome() as browser:
browser.get('http://parsinger.ru/html/watch/1/1_1.html')
button = browser.find_element(By.ID, "sale_button")
time.sleep(2)
button.click()
time.sleep(2)
Самое время сказать о двух похожих методах, которые часто путают новички: это .close() и .quit().
browser.close() — закрывает текущее окно браузера, если во время работы вы открыли новое окно или вкладку.
browser.quit() — закрывает все окна, вкладки, процессы веб-драйвера, которые были запущены во время сессии.
Некоторые проблемы WebDriver (из сети и личного опыта):
Поведение Selenium может отличаться в разных браузерах;
Иногда возникают сложности с поиском элементов (XPath и другие методы иногда просто не работают, хотя должны);
Необъяснимые падения драйвера прямо посреди работы скрипта;
Взаимодействие возможно только с активной вкладкой браузера, хотя драйвер позволяет открывать новые вкладки и новые окна, но не позволяет одновременно в них работать, только поочерёдно.
Объект WebElement
Когда вы работаете с Selenium, одной из самых основных задач является поиск и взаимодействие с элементами на веб-странице. Когда вы ищете элемент через методы вроде find_element() или find_elements(), возвращаемым типом данных является объект WebElement.
Сам же объект WebElement в Selenium представляет собой DOM-дерево которое находится на странице. Этот объект предоставляет методы и атрибуты для взаимодействия с элементом, такие как клик, ввод текста или извлечение атрибутов и др.
from selenium import webdriver
from selenium.webdriver.common.by import By
url = 'http://parsinger.ru/selenium/3/3.html'
browser = webdriver.Chrome()
browser.get(url)
elem = browser.find_element(By.CLASS_NAME, 'text')
print(elem)
browser.quit()
Вывод:
<selenium.webdriver.remote.webelement.WebElement (session="a86f9c5223a7fa5ac8d6c1911f5bfc16", element="9F9569F9515A0E022F0E665284FFB19D_element_2")>
Давайте разберём каждую часть подробнее:
selenium.webdriver.remote.webelement.WebElement: Это путь к классу в исходном коде Selenium, который представляет элемент на веб-странице.
session="a86f9c5223a7fa5ac8d6c1911f5bfc16": Это идентификатор сессии браузера, который используется WebDriver для отслеживания вашего взаимодействия с браузером. Каждая сессия уникальна и связывает ваш код с одним конкретным открытым окном браузера.
element="9F9569F9515A0E022F0E665284FFB19D_element_2: Это уникальный идентификатор элемента на странице в рамках текущей сессии. WebDriver использует этот ID для определения, какой именно элемент должен быть манипулирован.
element_2: Это просто часть уникального идентификатора, который скорее всего генерируется автоматически. Он не несет много информации для нас как разработчиков.
Объект WebElement — ваш ключ к манипуляциям с элементом на странице. Вы можете применять к нему различные методы, такие как:
1) .click() для симуляции клика мышью. browser.find_element(By.ID, "some_button_id").click()
2) .send_keys() для ввода текста (полезно для текстовых полей). browser.find_element(By.NAME, "some_textbox_name").send_keys("Hello, World!")
3) .get_attribute('some_attribute') для получения атрибутов, например, href у ссылок. browser.find_element(By.TAG_NAME, "a").get_attribute("href")
4) .text для получения видимого текста элемента. browser.find_element(By.CLASS_NAME, "some_class_name").text
.find_element()и find_elements()
Методы .find_element() и find_elements() вы будете использовать всегда при написании скриптов с помощью Selenium. Поэтому их нужно хорошо понимать.
Рассмотрим пример на тестовой странице. На ней есть 100 блоков с одинаковой структурой - каждый блок <div class="text"> содержит три тега <p> без дополнительных атрибутов. Интересная задача: как получить только первые элементы <p> из каждого блока?. Мы могли бы пройти в цикле и использовать срезы, как мы делаем с простыми списками, наверняка подумали вы. Давайте разбираться, почему срезы не сработают.

Метод .find_elements() возвращает объект веб-драйвера, который работает не так, как обычные Python-списки.
Выполним следующий код:
from selenium import webdriver
from selenium.webdriver.common.by import By
url = 'http://parsinger.ru/selenium/3/3.html'
with webdriver.Chrome() as browser:
browser.get(url)
link = browser.find_element(By.CLASS_NAME, 'text')
print(type(link))
>>> <class 'selenium.webdriver.remote.webelement.WebElement'>
Мы видим, что возвращаемый тип объекта - это экземпляр класса, который содержит в себе список элементов <p> в количестве трех штук, как показано на первом скриншоте. Это не просто список, а целый набор данных о веб-элементе!
Давайте заглянем внутрь этого объекта:
from selenium import webdriver
from selenium.webdriver.common.by import By
url = 'http://parsinger.ru/selenium/3/3.html'
with webdriver.Chrome() as browser:
browser.get(url)
link = browser.find_element(By.CLASS_NAME, 'text')
print(link)
результат
<selenium.webdriver.remote.webelement.WebElement (session="67761109be77b893aa1625e9d9ddafd4", element="5B0B853EFAAA1FE058CCA69B4278A2CE_element_2")>
Это объект WebElement, который не поддерживает срезы и работу с индексами. Давайте попробуем получить элемент с индексом [1] у этого объекта и посмотрим на результат. Напоминаю, мы пытаемся обратиться к элементу с индексом [1] в теге
.

from selenium import webdriver
from selenium.webdriver.common.by import By
url = 'http://parsinger.ru/selenium/3/3.html'
with webdriver.Chrome() as browser:
browser.get(url)
link = browser.find_element(By.CLASS_NAME, 'text')
print(link[0])
Получим ошибку:
Traceback (most recent call last):
File "E:\Async course\aiohttp\requests-html.py", line 8, in <module>
print(link[1])
~~~~^^^
TypeError: 'WebElement' object is not subscriptable
Эта ошибка говорит о том, что объект типа WebElement не поддерживает индексацию или "срезы". Т.е., мы пытаемся использовать объект WebElement так, как если бы это был список или массив, но Python вам сообщает, что это недопустимо.
Так происходит, потому что все элементы <p>, которые мы храним в этом объекте, являются как бы одним целым. А вот извлечь из этого объекта текст очень просто, достаточно применить к нему метод .text.
from selenium import webdriver
from selenium.webdriver.common.by import By
url = 'http://parsinger.ru/selenium/3/3.html'
with webdriver.Chrome() as browser:
browser.get(url)
link = browser.find_element(By.CLASS_NAME, 'text')
print(link.text)
#output
191817
121314
151715
.find_element() — Возвращает первый найденный элемент, соответствующий нашим критериям поиска (имеется в виду элемент веб-драйвера, который содержит внутри себя элемент/тег DOM).
.find_elements() — Возвращает все найденные элементы, соответствующие критериям поиска, и сохраняет результат в список <class 'list'>. Но список будет наполнен не элементами <p>, а элементами веб-драйвера, которые будут содержать в себе элементы DOM.
Проверим
from selenium import webdriver
from selenium.webdriver.common.by import By
url = 'http://parsinger.ru/selenium/3/3.html'
with webdriver.Chrome() as browser:
browser.get(url)
links = browser.find_elements(By.CLASS_NAME, 'text')
print(links)
#output
[
<selenium.webdriver.remote.webelement.WebElement (session="554a7888dc124cb3b362a68cf39904ca", element="DBD4B74A1E4D93D18E8DE83F9F3BC071_element_2")>,
<selenium.webdriver.remote.webelement.WebElement (session="554a7888dc124cb3b362a68cf39904ca", element="DBD4B74A1E4D93D18E8DE83F9F3BC071_element_3")>,
...
...
...
<selenium.webdriver.remote.webelement.WebElement (session="554a7888dc124cb3b362a68cf39904ca", element="DBD4B74A1E4D93D18E8DE83F9F3BC071_element_100")>,
<selenium.webdriver.remote.webelement.WebElement (session="554a7888dc124cb3b362a68cf39904ca", element="DBD4B74A1E4D93D18E8DE83F9F3BC071_element_101")>
]