Django-Admin.ru

Selenium на Python: автоматизация браузера

Python Selenium 4: установка без ручного скачивания драйвера, find_element и By, клики и ввод текста, ожидания WebDriverWait, headless, скриншоты и ошибки.

Редакция Django-Admin.ru
· обновлено · 9 мин чтения

Содержание · 11 разделов
  1. Установка Selenium
  2. Драйвер скачивать не нужно: Selenium Manager
  3. Открыть страницу и закрыть браузер
  4. Поиск элементов: find_element и By
  5. Действия с элементами: клик, ввод текста, атрибуты
  6. Явные ожидания: WebDriverWait
  7. Headless-режим: браузер без окна
  8. Скриншоты
  9. Selenium или requests: что выбрать
  10. Частые ошибки
  11. NoSuchElementException: Unable to locate element
  12. TimeoutException
  13. ElementClickInterceptedException
  14. StaleElementReferenceException
  15. AttributeError: ‘WebDriver’ object has no attribute ‘find_element_by_id’
  16. TypeError: got an unexpected keyword argument ‘executable_path’
  17. SessionNotCreatedException
  18. ModuleNotFoundError: No module named ‘selenium’
  19. Частые вопросы
  20. Как выбрать пункт в выпадающем списке?
  21. Как загрузить файл через форму?
  22. Как сохранить cookies и не входить в аккаунт каждый раз?
  23. Как переключиться на новую вкладку?
  24. Как запустить браузер через прокси?
  25. Итог

Selenium управляет настоящим браузером из кода: открывает страницы, нажимает кнопки, заполняет формы, ждёт загрузки контента и делает скриншоты. Связку Python Selenium используют для автотестов веб-интерфейсов и для работы с сайтами, где данные подгружаются JavaScript. Минимальный пример:

from selenium import webdriver
from selenium.webdriver.common.by import By

driver = webdriver.Chrome()  # драйвер браузера Selenium скачает сам
driver.get("https://www.selenium.dev/selenium/web/web-form.html")
print(driver.title)  # → Web form

driver.find_element(By.NAME, "my-text").send_keys("Selenium")
driver.find_element(By.CSS_SELECTOR, "button").click()
driver.quit()

Ниже — установка, поиск элементов, действия, ожидания, headless-режим, скриншоты и частые ошибки. Примеры проверены на Selenium 4.50, Python 3.12, Chrome и Edge в Windows; для экспериментов используется официальная тестовая страница проекта Selenium.

Установка Selenium

Поставьте пакет в виртуальное окружение:

# Windows
py -m pip install selenium

# macOS / Linux
python3 -m pip install selenium

Нужен Python 3.10 или новее (требование актуальных версий пакета). Обычно Selenium управляет уже установленным браузером: Chrome, Edge, Firefox или Safari. Если Chrome или Firefox на компьютере нет, Selenium Manager (начиная с версии 4.11) скачает браузер сам — для Chrome это сборка Chrome for Testing.

Драйвер скачивать не нужно: Selenium Manager

Браузером Selenium управляет через отдельную программу-драйвер: chromedriver для Chrome, msedgedriver для Edge, geckodriver для Firefox. Раньше драйвер нужно было скачивать вручную и следить, чтобы его версия совпадала с версией браузера.

Начиная с Selenium 4.6 в пакет встроен Selenium Manager. При вызове webdriver.Chrome() он сам определяет версию установленного браузера, скачивает подходящий драйвер и кеширует его в папке .cache/selenium в домашнем каталоге пользователя. Поэтому старые инструкции со скачиванием chromedriver.exe и прописыванием пути больше не нужны — первый запуск просто занимает чуть дольше, а для него требуется доступ в интернет.

Для других браузеров меняется только класс:

driver = webdriver.Chrome()
driver = webdriver.Edge()
driver = webdriver.Firefox()
driver = webdriver.Safari()   # только macOS, нужно включить удалённое управление в Safari

Открыть страницу и закрыть браузер

from selenium import webdriver

driver = webdriver.Chrome()
try:
    driver.get("https://www.selenium.dev/selenium/web/web-form.html")
    print(driver.title)        # → Web form
    print(driver.current_url)  # адрес текущей страницы
finally:
    driver.quit()              # закрыть браузер и драйвер

driver.get() ждёт окончания загрузки страницы (события load), но не ждёт контента, который скрипты подгружают позже, — об этом ниже, в разделе про ожидания.

Всегда вызывайте driver.quit(), лучше в блоке finally: иначе при ошибке в фоне останутся процессы браузера и драйвера. Метод close() закрывает только текущую вкладку. Про try/finally подробнее — в статье о try-except.

Поиск элементов: find_element и By

В Selenium 4 элементы ищут двумя методами с классом By:

  • driver.find_element(By..., "...") — первый подходящий элемент; если его нет — исключение NoSuchElementException;
  • driver.find_elements(By..., "...") — список всех подходящих элементов; если их нет — пустой список.
from selenium.webdriver.common.by import By

driver.find_element(By.ID, "my-text-id")
driver.find_element(By.NAME, "my-text")
driver.find_element(By.CSS_SELECTOR, "form button[type='submit']")
driver.find_element(By.XPATH, "//button[text()='Submit']")
driver.find_element(By.LINK_TEXT, "Return to index")

links = driver.find_elements(By.TAG_NAME, "a")
print(len(links))
СтратегияЧто ищет
By.IDатрибут id
By.NAMEатрибут name (поля форм)
By.CLASS_NAMEодин CSS-класс
By.CSS_SELECTORлюбой CSS-селектор
By.XPATHвыражение XPath
By.TAG_NAMEимя тега: a, input, h1
By.LINK_TEXTссылку по точному тексту
By.PARTIAL_LINK_TEXTссылку по части текста

На практике чаще всего хватает By.ID и By.CSS_SELECTOR. Искать можно и внутри найденного элемента: form.find_element(By.TAG_NAME, "button").

Важно: методы вида find_element_by_id() и find_element_by_css_selector() из Selenium 3 удалены. Если в старом примере они встречаются, замените их на find_element(By.ID, ...) и find_element(By.CSS_SELECTOR, ...).

Действия с элементами: клик, ввод текста, атрибуты

from selenium.webdriver.common.keys import Keys

field = driver.find_element(By.NAME, "my-text")
field.clear()                    # очистить поле
field.send_keys("Selenium")      # ввести текст
field.send_keys(Keys.ENTER)      # нажать Enter

button = driver.find_element(By.CSS_SELECTOR, "button")
button.click()

Что можно прочитать у элемента:

Свойство / методЧто возвращает
el.textвидимый текст элемента
el.get_attribute("href")значение атрибута или свойства (value, href…)
el.tag_nameимя тега
el.is_displayed()виден ли элемент
el.is_enabled()доступен ли (не disabled)
el.is_selected()отмечен ли чекбокс или пункт списка

Для произвольного JavaScript есть driver.execute_script() — например, прокрутка страницы вниз: driver.execute_script("window.scrollTo(0, document.body.scrollHeight)").

Явные ожидания: WebDriverWait

Главная причина нестабильных скриптов — попытка найти элемент, который ещё не появился. Решение — явное ожидание: ждать конкретного условия, но не дольше заданного времени. Полный рабочий пример на тестовой форме:

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait

driver = webdriver.Chrome()
try:
    driver.get("https://www.selenium.dev/selenium/web/web-form.html")
    driver.find_element(By.NAME, "my-text").send_keys("Selenium")
    driver.find_element(By.CSS_SELECTOR, "button").click()

    message = WebDriverWait(driver, 10).until(
        EC.visibility_of_element_located((By.ID, "message"))
    )
    print(message.text)  # → Received!
finally:
    driver.quit()

WebDriverWait(driver, 10) проверяет условие каждые полсекунды. Как только оно выполнилось, until() возвращает результат (здесь — найденный элемент). Если за 10 секунд условие так и не выполнилось, будет TimeoutException. Обратите внимание: локатор передаётся кортежем (By.ID, "message").

Полезные условия из expected_conditions:

УсловиеЖдёт, пока…
presence_of_element_located(loc)элемент появится в DOM (может быть невидим)
visibility_of_element_located(loc)элемент появится и станет видимым
element_to_be_clickable(loc)элемент станет видимым и доступным для клика
presence_of_all_elements_located(loc)появится хотя бы один элемент; вернёт список
invisibility_of_element_located(loc)элемент исчезнет (например, спиннер)
text_to_be_present_in_element(loc, text)в элементе появится текст
title_contains(text) / url_contains(text)заголовок или URL будет содержать текст

Есть ещё неявное ожидание driver.implicitly_wait(5): оно применяется ко всем вызовам find_element. Смешивать неявные и явные ожидания не стоит: время ожидания становится непредсказуемым. Выберите явные. И не используйте time.sleep() для ожидания контента: на быстрой сети он тратит время зря, а на медленной всё равно не спасает.

Headless-режим: браузер без окна

На сервере или в CI окна браузера не нужно. Включите headless-режим через опции:

from selenium import webdriver

options = webdriver.ChromeOptions()
options.add_argument("--headless=new")
options.add_argument("--window-size=1280,800")

driver = webdriver.Chrome(options=options)

Для Edge то же самое делается через webdriver.EdgeOptions(), для Firefox — webdriver.FirefoxOptions() с аргументом -headless. Размер окна в headless-режиме задавайте явно: от него зависит вёрстка страницы и то, какие элементы окажутся видимыми.

Скриншоты

driver.save_screenshot("page.png")  # видимая область страницы, вернёт True при успехе

element = driver.find_element(By.TAG_NAME, "form")
element.screenshot("form.png")      # только один элемент

Скриншоты удобно сохранять при падении теста, чтобы потом увидеть, что было на экране в момент ошибки.

Selenium или requests: что выбрать

Selenium запускает полноценный браузер, поэтому он медленный и требует ресурсов. Если нужные данные есть в HTML-коде страницы или сайт отдаёт их через API, быстрее и проще взять requests. Selenium нужен, когда:

  • контент появляется только после выполнения JavaScript;
  • нужно вести себя как пользователь: войти в аккаунт, нажимать кнопки, прокручивать;
  • вы пишете автотесты интерфейса и проверяете, что видит человек в браузере.

Перед автоматизацией чужого сайта проверьте его правила использования и не создавайте избыточную нагрузку. Другие сценарии автоматизации рутины собраны в статье «Автоматизация на Python».

Частые ошибки

NoSuchElementException: Unable to locate element

Элемент не найден. Причины: он ещё не загрузился (используйте WebDriverWait), селектор неверен (проверьте его в инструментах разработчика браузера), элемент находится внутри iframe (сначала переключитесь: driver.switch_to.frame(...)), или он открыт в другой вкладке (driver.switch_to.window(...)).

TimeoutException

Условие ожидания не выполнилось за отведённое время. Проверьте локатор и само условие: например, visibility_of_element_located не дождётся элемента, который есть в DOM, но скрыт стилями.

ElementClickInterceptedException

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

StaleElementReferenceException

Вы нашли элемент, а потом страница перерисовала эту часть DOM, и ссылка «устарела». Найдите элемент заново после действия, которое меняет страницу.

AttributeError: ‘WebDriver’ object has no attribute ‘find_element_by_id’

Код написан для Selenium 3. Используйте find_element(By.ID, "...").

TypeError: got an unexpected keyword argument ‘executable_path’

Параметр executable_path удалён из Selenium 4. Обычно путь к драйверу вообще не нужен — его найдёт Selenium Manager. Если всё же нужен свой драйвер, передайте путь через Service:

from selenium.webdriver.chrome.service import Service

driver = webdriver.Chrome(service=Service(executable_path="путь/к/chromedriver"))

SessionNotCreatedException

Драйвер не смог запустить браузер. Частые причины: версия вручную указанного драйвера не совпадает с версией браузера; нет доступа в интернет при первом запуске, и Selenium Manager не смог скачать драйвер или браузер. Уберите ручной путь к драйверу и обновите пакет: pip install -U selenium.

ModuleNotFoundError: No module named ‘selenium’

Пакет установлен в другое окружение. Решение — в статье про ModuleNotFoundError.

Частые вопросы

Как выбрать пункт в выпадающем списке?

Для тега select есть вспомогательный класс Select:

from selenium.webdriver.support.ui import Select

select = Select(driver.find_element(By.NAME, "my-select"))
select.select_by_visible_text("Two")   # по тексту пункта
select.select_by_value("3")            # по атрибуту value
print(select.first_selected_option.text)

Выпадающие списки, свёрстанные на div без select, открывают обычным click() и затем кликают по нужному пункту.

Как загрузить файл через форму?

Найдите поле input type="file" и передайте в send_keys() абсолютный путь к файлу — системное окно выбора файла при этом не открывается:

import os

driver.find_element(By.NAME, "my-file").send_keys(os.path.abspath("upload.txt"))

Как сохранить cookies и не входить в аккаунт каждый раз?

Сохраните cookies после входа в JSON, а при следующем запуске сначала откройте страницу того же сайта и только потом добавьте их — иначе будет InvalidCookieDomainException: invalid cookie domain:

import json

with open("cookies.json", "w") as f:
    json.dump(driver.get_cookies(), f)

# при следующем запуске
driver.get("https://example.com/")
with open("cookies.json") as f:
    for cookie in json.load(f):
        driver.add_cookie(cookie)
driver.refresh()

Другой способ — запускать Chrome с отдельным профилем: options.add_argument("--user-data-dir=путь/к/профилю"). В профиле сохраняются cookies, localStorage и вход на сайты.

Как переключиться на новую вкладку?

Все открытые вкладки перечислены в driver.window_handles. Если ссылка открылась в новой вкладке, переключитесь на последнюю; новую пустую вкладку открывает switch_to.new_window():

driver.switch_to.window(driver.window_handles[-1])  # последняя открытая вкладка

driver.switch_to.new_window("tab")                  # новая вкладка
driver.get("https://www.selenium.dev/")

Перед закрытием вкладки через driver.close() переключитесь обратно, иначе следующие команды упадут с NoSuchWindowException.

Как запустить браузер через прокси?

Для Chrome и Edge прокси задаётся аргументом запуска:

options = webdriver.ChromeOptions()
options.add_argument("--proxy-server=http://10.10.1.10:3128")
driver = webdriver.Chrome(options=options)

Логин и пароль в этом аргументе Chrome не принимает. Для прокси с авторизацией используют прокси без пароля с доступом по IP-адресу или отдельные инструменты, которые перехватывают запросы браузера.

Итог

Для работы с Selenium на Python достаточно pip install selenium и установленного браузера: драйвер скачает Selenium Manager. Элементы ищите через find_element(By..., ...), появления контента ждите через WebDriverWait с expected_conditions, на сервере включайте headless-режим и всегда закрывайте браузер через driver.quit(). Если браузер для задачи не обязателен, начните с requests; обзор других пакетов — в статье о библиотеках Python, полный справочник — в документации Selenium.

  • #Python
  • #Selenium
  • #Автоматизация
  • #Парсинг
  • #Тестирование
↑ ↓ выборEnter открытьEsc закрыть