Урок 14. Selenium Работа с динамическими элементами

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

Импорт и подготовка

Начнем с импорта необходимых модулей и открытия страницы с динамическими элементами:

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

driver = webdriver.Chrome()
driver.get("https://example.com/dynamic-elements")  # URL страницы

Явное ожидание появления элемента

Если элемент появляется с задержкой, использование time.sleep() неэффективно. Лучше использовать WebDriverWait с условием expected_conditions:

wait = WebDriverWait(driver, 10)  # Ждем до 10 секунд
try:
    button = wait.until(
        EC.presence_of_element_located((By.CSS_SELECTOR, "button.load-more"))
    )
    print("Элемент найден")
except:
    print("Элемент не найден")

Клик по элементу после его появления

После того как элемент появится, можно безопасно выполнить по нему действие:

button.click()
print("Клик выполнен")

Использование других условий ожидания

Selenium предоставляет множество условий для динамических элементов:

  • visibility_of_element_located – элемент видим на странице.
  • element_to_be_clickable – элемент кликабелен.
  • text_to_be_present_in_element – проверка наличия текста в элементе.
# Пример ожидания, пока текст появится в элементе
wait.until(
    EC.text_to_be_present_in_element((By.ID, "status"), "Готово")
)

Пример Плавной прокрутки страницы вниз и появление ранее не доступных данных

import time  # Импорт модуля time для создания пауз между действиями
from selenium import webdriver  # Основная библиотека Selenium для работы с браузером
from selenium.webdriver.common.by import By  # Модуль для указания способов поиска элементов (By.CSS_SELECTOR, By.ID и т.д.)
from selenium.webdriver.chrome.service import Service  # Класс для управления сервисом ChromeDriver
from selenium.webdriver.chrome.options import Options  # Класс для настройки опций браузера Chrome


def smooth_scroll(driver, pause=0.3, step=300):
    """
    Функция для плавной прокрутки страницы вниз в Selenium.
    
    Эта функция реализует постепенную прокрутку, чтобы имитировать поведение реального пользователя.
    Полезна для загрузки динамического контента и избежания обнаружения автоматизации.
    
    :param driver: экземпляр webdriver - объект для управления браузером
    :param pause: пауза между шагами прокрутки (в секундах), регулирует скорость движения
    :param step: количество пикселей на шаг прокрутки, определяет гранулярность
    """
    # Получаем полную высоту документа страницы через JavaScript
    last_height = driver.execute_script("return document.body.scrollHeight")
    pos = 0  # Начальная позиция прокрутки (верх страницы)

    # Цикл продолжается, пока текущая позиция меньше полной высоты страницы
    while pos < last_height:
        pos += step  # Увеличиваем позицию на шаг
        # Выполняем JavaScript-команду для прокрутки к новой позиции (0 - по горизонтали, pos - по вертикали)
        driver.execute_script(f"window.scrollTo(0, {pos});")
        time.sleep(pause)  # Пауза для плавности и загрузки контента

    # Финальная прокрутка до самого низа страницы для гарантии полной загрузки
    driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")


def main():
    # URL сайта для тестирования - страница с примерами на parsertools.ru
    url = "https://parsertools.ru/primers"

    # Создание объекта опций для настройки браузера Chrome
    options = Options()
    # Аргумент для запуска браузера в максимально развернутом окне
    options.add_argument("--start-maximized")
    # Создание сервиса для ChromeDriver (предполагается, что он установлен в PATH)
    service = Service()
    # Инициализация драйвера Chrome с указанными опциями и сервисом
    driver = webdriver.Chrome(service=service, options=options)

    try:
        # Загрузка начальной страницы сайта
        driver.get(url)
        # Пауза 2 секунды для полной загрузки контента и JavaScript-элементов
        time.sleep(2)

        # Бесконечный цикл для обработки всех страниц пагинации
        while True:
            # Шаг 1: Плавная прокрутка страницы до конца для загрузки всех элементов
            smooth_scroll(driver, pause=0.3, step=300)
            # Дополнительная пауза внизу страницы для завершения загрузки
            time.sleep(2)

            # Шаг 2: Поиск и определение номера текущей страницы по CSS-селектору
            current_page = driver.find_element(By.CSS_SELECTOR, ".page-numbers.current").text
            # Вывод информации о текущей странице в консоль для мониторинга
            print(f"Сейчас на странице: {current_page}")

            # Шаг 3: Поиск элементов кнопки "Следующая страница" (возвращает список)
            next_button = driver.find_elements(By.CSS_SELECTOR, ".wp-block-query-pagination-next")

            # Проверка наличия кнопки следующей страницы
            if next_button:
                # Вывод сообщения о переходе
                print("Переход на следующую страницу...")
                # Клик по первой найденной кнопке (индекс 0)
                next_button[0].click()
                # Пауза для загрузки новой страницы и её элементов
                time.sleep(2)
            else:
                # Если кнопка отсутствует, это последняя страница - выход из цикла
                print("Достигли последней страницы. Работа завершена.")
                break

    finally:
        # Блок finally гарантирует закрытие браузера даже при ошибках
        # Дополнительная пауза перед закрытием для просмотра результата
        time.sleep(2)
        # Закрытие экземпляра драйвера и освобождение ресурсов
        driver.quit()


# Точка входа в программу - проверка, если скрипт запущен напрямую
if __name__ == "__main__":
    main()  # Вызов основной функции

Советы по работе с динамическими элементами

  • Используйте явное ожидание вместо time.sleep() для ускорения скриптов.
  • Если элементы обновляются через AJAX, проверяйте их состояние через условия из expected_conditions.
  • Для сложных сценариев можно комбинировать ожидания нескольких элементов и условий.

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

Больше уроков по парсингу на parsertools.ru/lessons.