CoderCastrov logo
CoderCastrov
Selenium

Парсинг веб-страниц с использованием Python для ленивых (сочетание BeautifulSoup и Selenium) — Часть 2.

Парсинг веб-страниц с использованием Python для ленивых (сочетание BeautifulSoup и Selenium) — Часть 2.
просмотров
8 мин чтение
#Selenium

Изучение Selenium

**Эта статья является продолжением **https://bit.ly/3hN1LOp

Одним из самых популярных инструментов для парсинга веб-страниц является Selenium. Selenium - это бесплатный фреймворк, предназначенный для автоматизации тестирования веб-приложений через браузер. Помимо использования в качестве инструмента для тестирования, он широко применяется для сбора данных или создания рутин для работы с веб-страницами. Он в основном имитирует поведение пользователя на веб-странице.

В этой статье мы рассмотрим несколько примеров того, как Selenium может быть использован в последнем случае. В конце статьи я укажу на полный проект, который я разработал, используя представленные концепции.

Я также хочу поделиться с вами советом по курсу по автоматизации и парсингу веб-страниц с использованием Python. Там есть скидка ;)


Почему Selenium?

В части 1 мы рассмотрели, как собирать данные с помощью BeautifulSoup, библиотеки Python для парсинга веб-страниц. Несмотря на то, что она легка в использовании, у нее есть некоторые проблемы при работе с страницами, которые отображаются с помощью Javascript, загружаемого динамически.

С Selenium тесты, написанные с использованием WebDriver, очень реалистичны, потому что вместо использования собственного движка JavaScript он напрямую вызывает браузер. Это позволяет (или по крайней мере упрощает) заполнение форм, имитацию кликов по ссылкам, кнопкам, а также сбор текстов и т. д.

Вы можете спросить: "Почему использовать BeautifulSoup, если я могу сделать все с помощью Selenium?". Длинные участки кода с использованием Selenium не всегда работают одинаково. Не рекомендуется использовать его без необходимости. Несколько факторов обосновывают это:

  • Для использования Selenium часто требуется собирать все ресурсы страницы, такие как стили, скрипты, изображения и т. д. Это не всегда необходимо для выполнения вашего действия.
  • Использование процессора и памяти.
  • Selenium немного "хрупкий" и может вызывать проблемы стабильности.

Поэтому идеальным вариантом будет использование Selenium для работы с случаями, когда контент добавляется на страницу с помощью JavaScript.

Кроме того, BeautifulSoup лучше подходит для поиска элементов на странице и сбора данных. Предположим, вы хотите получить доступ к некоторым divs где-то в середине HTML, без идентификатора, класса или текста. Это происходит чаще, чем я бы хотел.

С помощью BeautifulSoup я могу объединить различные логики и найти, например, второй и пятый divs с class = имя_класса, которые являются дочерними элементами элемента с id = id_тега. Такой уровень специфичности возможен только с использованием Selenium, выполняя поиск элемента по одному с помощью XPATH, и мне пришлось бы изменять его содержимое, чтобы достичь желаемого результата. На динамических страницах это неизбежно, но на статических страницах это не требуется.

WebDriver

WebDriver - это исполняемые модули, которые работают вместе с браузером для его управления. Драйвер специфичен для каждого браузера, например, ChromeDriver для Chrome/Chromium и GeckoDriver для Firefox.

Обычно я использую ChromeDriver, который можно скачать здесь. Убедитесь, что вы загружаете правильную версию драйвера для вашего браузера, иначе Selenium не будет работать.


Начало работы

Для демонстрации, вот пример того, как начать проект с использованием Selenium:

**from** **selenium** **import** webdriver
**from** **selenium.webdriver.chrome.options** **import** Options  
chrome_options = Options()## делает браузер невидимым во время процесса
chrome_options.add_argument("--headless") ## путь к вашему драйверу
p = путь_к_вашему_драйверуdriver = webdriver.Chrome(p +'\chromedriver.exe', options=chrome_options)

Опция ‘ — headless’ может быть полезной после проведения некоторых тестов. Вместо того, чтобы открывать браузер каждый раз при запуске кода, все будет происходить без необходимости отображения графической страницы, что экономит ресурсы ЦП и памяти.

Локализация элементов

Selenium предлагает различные методы для локализации элементов на странице. Полный список можно найти в документации. Обратите внимание, что он позволяет выбирать один или несколько элементов одновременно, поэтому обратите внимание на множественное число. Например, find_element_by_class_name возвращает первый элемент с указанным классом 'x' в качестве параметра, тогда как find_elements_by_class_name возвращает список всех элементов с этим классом в качестве параметра. Документация ясно описывает и дает множество примеров, поэтому я продолжу.

Ожидания

Тема обычно запутанная и содержит противоречивую информацию. Но я думаю, что здесь у меня есть хорошее объяснение для каждого типа использования.

Элементы на веб-странице требуют некоторого времени для загрузки в DOM. Если ваш скрипт пытается найти элемент, который еще не видим, Selenium выдает ошибку ElementNotVisibleException. Другие ошибки могут и, вероятно, возникнут из-за отсутствия ожиданий в скрипте. Данный элемент может просто не существовать или быть перекрыт другим элементом. Каждая страница (и каждый элемент) ведет себя по-разному, поэтому все может произойти.

Перейдем к типам ожиданий:

Сон

Этот метод приостанавливает выполнение скрипта на определенное время, независимо от того, был ли найден элемент или нет.

**from** time import sleep...
# Скрипт останавливается на 2 секунды
sleep(2)
# Скрипт останавливается на полсекунды
sleep(0.5)

Перейдем к недостаткам этой практики:

  • Скрипт должен ждать весь заданный период времени, даже если следующий элемент уже доступен, что увеличивает общее время выполнения.
  • Selenium выдает ошибку, если элемент не появляется после _sleep. _В этом случае вы не узнаете, сколько времени требуется для его появления, и вам придется многократно экспериментировать, чтобы подобрать правильное время _sleep, _которое, кстати, может меняться в зависимости от вашего соединения с сайтом.
  • _Sleep _работает только для следующего элемента в скрипте. Если есть несколько элементов, требующих ожидания, вам нужно указать _sleep _для каждого из них, что загромождает код.

Поэтому _sleep _**не **является хорошей практикой. Однако я все еще использую его. Особенно когда я меняю или обновляю страницу. Но я собираюсь перестать. И начать ту диету, которую я обещал себе два года назад... и заниматься больше физическими упражнениями (это все еще пост для ленивых?).

Implicit wait

Оператор implicit wait заставляет драйвер ждать определенное время при попытке найти элементы, которые не доступны немедленно. Преимущества по сравнению с использованием sleep:

  • Оператор implicit wait объявляется только один раз и действует до конца использования WebDriver. Например:
from selenium import webdriver
driver = webdriver.Firefox()
driver.implicitly_wait(10)
  • Драйвер не будет ждать все указанные секунды. Скрипт продолжит выполнение, как только элемент будет найден 🙌. Если элемент не будет найден после указанного времени ожидания, Selenium выдаст ошибку NoSuchElementException.

Недостатки этого метода:

  • Производительность: Можно подумать, что установка разумного времени ожидания (например, 30 секунд) решит все проблемы, так как все компоненты загружаются быстрее этого времени и программа все равно продолжит выполнение. Но эта стратегия может занять много времени, если вам нужно только проверить наличие элемента и продолжить тестирование. Например, вы можете подумать "если открыто всплывающее окно, закройте его и продолжайте". В этом случае драйвер будет ждать 30 секунд, чтобы убедиться, что всплывающего окна нет.
  • Исключения: По истечении указанного времени implicit wait возвращает только NoSuchElementException, а знание, что именно пошло не так, вероятно, является самой важной частью автоматизированного кода. Как мы видели, причина не всегда заключается в отсутствии элемента. Конечно, вы можете использовать конструкцию try/catch вокруг вызова элемента, чтобы увидеть, что возвращается. Но, как мы увидим далее, есть лучшая стратегия.
  • Надежность: Как уже упоминалось, скрипт продолжает выполнение, как только элемент найден в DOM. Но это не всегда то, что нам нужно. При работе с большим количеством JavaScript элемент может быть найден, но временно невидим (вызывая ElementNotVisibleException); он может быть в движении и невозможно на него нажать (WebDriverException: Element is not clickable at point...) или он может быть удален и затем снова добавлен в DOM (StaleElementReferenceException).
  • Условия: Implicit wait применяется только для поиска элементов (find_element_by_...). Но есть множество условий, которые могут быть полезными при ожидании. Уже упомянутые невидимые или некликабельные элементы - некоторые примеры. Если бы мы выполняли действие ТОЛЬКО ЕСЛИ элемент видим или кликабелен, ошибка не возникла бы. Я расскажу о таких условиях позже.

Таким образом, implicit wait более удобен, чем простой sleep, но также имеет свои ограничения. Есть лучшая альтернатива.

Явное ожидание

Большим преимуществом перед другими методами является возможность установить время ожидания до тех пор, пока не будет выполнено определенное условие. Например, "ожидайте 30 секунд, пока элемент X не станет доступным для нажатия". Если условие не выполняется в указанное время, Selenium возвращает ошибку. Если условие выполняется, скрипт продолжает работу и, при желании, можно выполнить действие, такое как нажатие на элемент (если он является ссылкой или кнопкой) или заполнение поля. Список ожидаемых условий (Expected Conditions) можно найти здесь.

**from** **selenium** **import** webdriver 
**from** **selenium.webdriver.support** **import** expected_conditions **as** EC
**from** **selenium.webdriver.common.by** **import** By
**from** **selenium.webdriver.support.ui** **import** WebDriverWait#Устанавливаем время ожидания
wait = WebDriverWait(driver, 10)#ожидаем, пока элемент с определенным идентификатором станет доступным для нажатия
wait.until(EC.element_to_be_clickable((By.ID, 'id_элемента')))

Реализация 'By' принимает эти стратегии для поиска элементов.

Таблица ниже сравнивает неявное и явное ожидание:

таблица сравнения неявного и явного ожидания

Существует еще четвертый способ.

Ожидание с задержкой

Определяет максимальное время ожидания условия и частоту проверки этого условия.

Мы можем настроить такое ожидание, чтобы игнорировать определенные исключения при ожидании поиска элемента на странице. Например:

wait = WebDriverWait(driver, 10, poll_frequency=1, ignored_exceptions=[ElementNotVisibleException, ElementNotSelectableException]) 
element = wait.until(EC.element_to_be_clickable((By.XPATH, "//div")))

Действия

С помощью найденных элементов можно выполнить различные действия. Это позволяет автоматизировать взаимодействия, такие как перемещение мыши, клик, нажатие клавиши, взаимодействие с меню. Также можно выполнять более сложные действия, такие как наведение (hover) и перетаскивание и отпускание (drag and drop). Полный список можно найти здесь.

Одним из наиболее распространенных примеров является .click():

## Мы можем сохранить элемент в переменной
element = wait.until(EC.element_to_be_clickable((By.XPATH, "//div")))
element.click()

## Мы также можем выполнить действие в цепочке
wait.until(EC.element_to_be_clickable((By.XPATH, "//div"))).click()

И BeautifulSoup?

Помните: BS плохо работает с динамическими страницами, то есть теми, которые загружают свое содержимое с помощью JavaScript. Один из способов совместить их - использовать Selenium, когда требуется выполнить множество кликов, заполнить формы и взаимодействовать с элементами, для чего он специализирован. Если вы переходите по другому URL и вам нужно собрать некоторые данные, вы можете продолжать использовать BS с помощью

bs = BeautifulSoup(driver_source), где 'driver_source' - это URL, на который указывает Selenium в этом участке кода.

Проект

У меня есть полный проект для тех, кто заинтересован в том, чтобы увидеть все эти концепции, работающие вместе: Python, Pandas, BeautifulSoup, Selenium, а также загрузка данных в базу данных Mongo.

https://github.com/CaioEstrella/Scraping---Telelistas

Ссылки

  • Отличный курс, короткий, прямой и со скидкой, по созданию роботов с использованием Python. https://go.hotmart.com/T72800154G

Selenium Implicit Wait

Implicit wait - это способ сообщить драйверу Selenium, что он должен опрашивать DOM в течение определенного времени при выполнении...

vnrtech.blogspot.com

Ожидания Selenium: неявные, явные, плавные и задержка

Ожидания загрузки страницы в Selenium играют важную роль в ваших скриптах Selenium. Они помогают сделать их более надежными и...

www.lambdatest.com

https://selenium-python.readthedocs.io/index.html