Привет всем! У меня отличные новости: работа с Selenium стала еще проще. Больше никаких танцев с вебдрайверами — теперь всё работает "из коробки"!
В честь этих изменений я хочу поделиться своим опытом работы с этой замечательной библиотекой.
Краткий ликбез
Selenium — это библиотека Python, предназначенная для тестирования браузеров, что в данном контексте можно сравнить с автоматизацией любого действия в браузере, которое вы можете выполнить вручную.
Selenium используется для:
Парсинга (хотя это не лучший выбор, но об этом позже)
Автокликов
Автозаполнения форм
Любого действия, которое можно выполнить вручную.
Основные плюсы:
Удобство: Легко получать доступ к любому элементу страницы через CSS, ID, классы, теги и XPath.
Низкий порог входа: Достаточно 2-3 часов даже для абсолютного новичка, чтобы начать писать скрипты с использованием Selenium.
Минусы:
Медленный для задач парсинга
Не всегда стабильный
Тяжелый для системы (особенно при параллельном использовании нескольких драйверов)
Что мы сегодня сделаем
Установим Selenium и ряд полезных библиотек, которые позволят нам работать в стелс-режиме и без плашки «Браузером управляет автоматизированное ПО».
Научимся использовать профиль браузера, позволяющий сохранять личную историю каждого экземпляра браузера (авторизации, историю запросов, историю ввода и т.д.).
Настроим рандомный User-Agent для каждого подключения, что позволит нам быть еще более незаметными в сети.
Научимся использовать опции при инициации драйвера, чтобы повысить его скрытность и функциональность.
Установка необходимых библиотек
pip install selenium selenium-stealth fake-useragent
Или используйте requirements.txt
(пример на скрине ниже).
Импортируем необходимые модули:
import os
from fake_useragent import UserAgent
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium_stealth import stealth
import time
Создаем функцию для генерации рандомного User-Agent:
def get_random_chrome_user_agent():
user_agent = UserAgent(browsers='chrome', os='windows', platforms='pc')
return user_agent.random
Эта функция возвращает случайный User-Agent для браузера Chrome под Windows.
Инициация драйвера
Не забудьте создать папку users в корне вашего проекта!
Теперь начинается самая важная часть. Инициацию драйвера (браузера под селениум) я привык делать в виде функции. Давайте я сейчас дам пример такой функции «как есть», а далее подробно поясню за что отвечает каждая строка кода.
def create_driver(user_id=1):
options = Options()
options.add_argument("start-maximized")
options.add_experimental_option("excludeSwitches", ["enable-automation"])
options.add_experimental_option('useAutomationExtension', False)
script_dir = os.path.dirname(os.path.abspath(__file__))
base_directory = os.path.join(script_dir, 'users')
user_directory = os.path.join(base_directory, f'user_{user_id}')
options.add_argument(f'user-data-dir={user_directory}')
options.add_argument('--disable-gpu')
options.add_argument('--disable-dev-shm-usage')
options.add_argument("--disable-notifications")
options.add_argument("--disable-popup-blocking")
options.add_argument('--no-sandbox')
# options.add_argument('--headless')
driver = webdriver.Chrome(options=options)
ua = get_random_chrome_user_agent()
stealth(driver=driver,
user_agent=ua,
languages=["ru-RU", "ru"],
vendor="Google Inc.",
platform="Win32",
webgl_vendor="Intel Inc.",
renderer="Intel Iris OpenGL Engine",
fix_hairline=True,
run_on_insecure_origins=True
)
driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {
'source': '''
delete window.cdc_adoQpoasnfa76pfcZLmcfl_Array;
delete window.cdc_adoQpoasnfa76pfcZLmcfl_Promise;
delete window.cdc_adoQpoasnfa76pfcZLmcfl_Symbol;
'''
})
return driver
А теперь к разбору полетов. Как вы уже поняли функция вернет некий экземпляр драйвера. Он, в дальнейшем, будет выполнять роль нашего браузера:
- переход по ссылкам
- сбор данных
- клики и прочие действия, которые можно только представить.
Функция принимает один параметр — ID пользователя, который используется для создания уникального профиля браузера. На примере браузера которым вы пользуетесь сейчас поясню.
Вы зашли в браузер, авторизовались на одном сайте, потом на другом и так далее. Затем закрыли браузер, выключили компьютер. Вернувшись на следующий день, открыли браузер и обнаружили, что все авторизации, кэш и история сохранились. Аналогичный процесс мы реализуем с помощью Selenium (подробнее на коде ниже).
options = Options()
Здесь создается экземпляр класса Options
, который используется для настройки поведения Chrome WebDriver.
options.add_argument("start-maximized")
Добавляется аргумент для запуска браузера в режиме максимального окна.
options.add_experimental_option("excludeSwitches", ["enable-automation"])
Добавляется экспериментальная опция, исключающая переключатель "enable-automation", чтобы предотвратить автоматическое обнаружение, что браузер управляется WebDriver.
options.add_experimental_option('useAutomationExtension', False)
Добавляется экспериментальная опция, отключающая расширение автоматизации в браузере, что также помогает скрыть использование WebDriver.
script_dir = os.path.dirname(os.path.abspath(__file__))
Эта строка получает абсолютный путь к текущему скрипту и извлекает директорию, в которой он находится. file содержит путь к текущему файлу.
base_directory = os.path.join(script_dir, 'users')
Создается путь к базовой директории users
, которая находится в той же директории, что и текущий скрипт. (если не находится - исправьте). В этой дирректории мы будем хранить папки с пользователями. Далее достаточно будет повторно указать айдишник пользователя и вы сможете «вернуться в профиль».
user_directory = os.path.join(base_directory, f'user_{user_id}')
Создается путь к директории конкретного пользователя, используя значение user_id
. Например, если user_id
равно 1
, будет создан путь users/user_1
.
options.add_argument(f'user-data-dir={user_directory}')
Добавляется аргумент для Chrome, указывающий на директорию с данными пользователя. Это позволяет использовать разные профили Chrome для разных пользователей.
options.add_argument('—disable-gpu')
Отключается использование GPU в браузере. Это иногда необходимо для правильной работы в средах без графического интерфейса. Аргумент не обязательный, но можете с ним поиграться.
options.add_argument('--disable-dev-shm-usage')
Отключается использование разделяемой памяти /dev/shm
. Это может быть полезно для предотвращения проблем с памятью в контейнеризованных средах.
options.add_argument("—disable-notifications")
Отключаются уведомления в браузере.
options.add_argument("—disable-popup-blocking")
Отключается блокировка всплывающих окон.
options.add_argument('—no-sandbox')
Отключается песочница (sandbox) для повышения стабильности в некоторых средах, но это уменьшает безопасность. Иногда без этого аргумента не запускается вебдрайвер на средах без GUI, но тоже указывать не обязательно.
options.add_argument('—headless')
Запускает браузер в безголовом режиме (без графического интерфейса, фоном), что полезно для автоматизации и тестирования. С этим аргументом бывают интересные штуки. Иногда драйвер начинает работать быстрее, многда медленнее, а иногда и вовсе сайт это вычисляет и закрывает доступ брузеру. Так что с этим будьте аккуратны. На примере аргумент закомментирован.
driver = webdriver.Chrome(options=options)
Создается экземпляр WebDriver для Chrome с заданными параметрами options
.
ua = get_random_chrome_user_agent()
Получает случайный user-agent для Chrome. Это может быть функцией, которую мы написали выше или ваш собственный user-agent.
stealth(driver=driver,
user_agent=ua,
languages=["ru-RU", "ru"],
vendor="Google Inc.",
platform="Win32",
webgl_vendor="Intel Inc.",
renderer="Intel Iris OpenGL Engine",
fix_hairline=True,
run_on_insecure_origins=True
)
Вызывает функцию stealth
для маскировки использования WebDriver. Здесь устанавливаются различные параметры, такие как user_agent
, languages
, vendor
, platform
, webgl_vendor
, renderer
, и другие, чтобы сделать браузер менее распознаваемым как управляемый WebDriver.
driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {
'source': '''
delete window.cdc_adoQpoasnfa76pfcZLmcfl_Array;
delete window.cdc_adoQpoasnfa76pfcZLmcfl_Promise;
delete window.cdc_adoQpoasnfa76pfcZLmcfl_Symbol;
'''
})
Выполняет команду CDP (Chrome DevTools Protocol) для удаления определенных свойств из объекта window, которые могут использоваться для обнаружения WebDriver. К примеру с Озон вы не сможете работать без удаления этих свойств с объекта window.
return driver
Возвращает созданный и настроенный объект WebDriver.
Ну что, давайте теперь к простенькому примеру? Сейчас мы с вами зайдем на какой то сайт, выполним авторизацию и закроем браузер. После этого зайдем на этот сайт повторно через вебдрайвер и посмотрим остались ли мы авторизованными.
def main_login(user_id=1):
driver = create_driver(user_id)
driver.get('Ccылка на любой сайт)
time.sleep(350)
Думаю тут все ясно:
инициируем браузер
переходим по ссылке
ставим паузу в 350 секунд чтоб я успел сделать авторизацию (конечно это можно делать автоматически с привязкой к форме, кнопкам, но это не тема данной статьи).
Делаю авторизацию руками
А теперь давайте посмотрим что у нас получилось после входа в папке пользователя с user_id — 1.
Как вы видите там большое количество разных папок и файлов. Похожая картинка будет в корневых папках вашего браузера. Так рабоает профиль браузера. Тяжеловат он конечно, но открывает много возможностей.
Ну что, остается зайти в созданный профиль. Сейчас мы выполним ту же функцию, так как мы передавали айдишник 1 на запуске (перед авторизацией) и он же нам нужен для входа.
Прекрасно. Мы по прежнему в аккаунте и браузер по прежнему не говорит, что «Браузером управляет автоматическое ПО».
На этом все. Кому интересны фишки, связанные с селениумом — пишите. Есть много чего вам рассказать, так как опыт работы с автоматизацией браузера максимально обширный.
Кому было полезно — комментируйте и ставьте лайки. Ваша поддержка для меня очень важна.
До скорого!
kompilainenn2
а в чем новизна селениума по вашей статье? хайпануть?
yakvenalex Автор
Новизна в том что больше не нужно скачивать специальный драйвер. Кроме того. Сам принцип селениум изменился. Теперь в формате driwer.find_element встроено ожидание. Ну и как бы есть документация в которой описано это все "новый". Прежде чем писать негативный комментарий, пожалуйста, изучайте матчасть. Спасибо.
kompilainenn2
Если бы заголовок звучал, как "Новое в Selenium 4.21...", то вопрос бы не возник
yakvenalex Автор
https://www.selenium.dev/blog/2024/selenium-4-21-released/ - релиз новой версии состоялся 16 мая 2024 года. Переводить вам не буду, думаю разберетесь.
JastixXXX
А по-моему очень круто, что автор рассказал про такие фишки, как стелс режим и сохранение данных пользователя. Я не сомневаюсь, что для многих это не стало откровением, но мне, например, как человеку, использующему селениум 1-2 раза в год максимум, это было весьма интересно.
Lord_Alzov
Этому всему куча лет, адекватные юзают undetectedchromdriver и там это тоже давно уже и драйвер не надо скачивать руками.