Задать вопрос
@DenisProk

Как с помощью Selenium на Python найти элемент html страницы, который расположен в #shadow-root (open)?

Приветствую!
Сразу оговорюсь, я только учусь, хотел для работы написать бота, который будет в автоматическом режиме взаимодействовать с сайтом:
https://eaist.mos.ru/login.html?redirect=https://e...

При нажатии на кнопку "Войти в систему" сразу вылетает 2 окна: принять куки и ввести логин и пароль. Код обоих окон скрыт в разных shadow dom и найти их элементы через Selenium у меня не получается, уже 4-й день борюсь с этой проблемой. Помогите пожалуйста с кодом, как нужно обратиться к кнопке "Понятно", чтобы нажать на нее?

Уже перечитал кучу статей, форумов, смотрел ролики, принцип, как это делается, вроде как понятен, но рабочий вариант для своего случая так и не смог написать :(

Заранее спасибо!

67ee8f6c74c91869472782.png

from selenium import webdriver
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from time import sleep


chrome_options = webdriver.ChromeOptions()
chrome_options.add_argument("--window-size=1000,900")
service = Service(executable_path=ChromeDriverManager().install())
driver = webdriver.Chrome(service=service, options=chrome_options)
wait = WebDriverWait(driver, 15, poll_frequency=1)

driver.get("https://eaist.mos.ru/login.html?redirect=https://eaist.mos.ru/panel.html")

# Кнопка: "Войти в систему"
LOGIN = ("xpath", "/html/body/div[1]/div/button")
# Нажимаю на кнопку "Войти в систему"
wait.until(EC.element_to_be_clickable(LOGIN)).click()

# Тут пытаюсь обратиться к тегу, в котором находится shadow-root, но уже на данном этапе
# получаю ошибку: NoSuchElementException: Message: no such element
# А кнопка о принятии находится еще глубже...
shadow_root = driver.find_element("css selector", '[version="1"]').shadow_root

sleep(10)
driver.close()
  • Вопрос задан
  • 186 просмотров
Подписаться 3 Простой 1 комментарий
Решения вопроса 1
@DenisProk Автор вопроса
Мне наконец удалось написать код, который нажимает на кнопку принять куки. Спасибо всем, кто откликнулся за ваши ответы. Надеюсь этот пост сэкономит кому-то кучу времени :)

from selenium import webdriver
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from time import sleep

def get_shadow_root(element):
    return driver.execute_script(
        'return arguments[0].shadowRoot',
        element
    )

chrome_options = webdriver.ChromeOptions()
# Разрешение не трогать, т.к. есть завязка на pyautogui
chrome_options.add_argument("--window-size=1920,1040")
service = Service(executable_path=ChromeDriverManager().install())
driver = webdriver.Chrome(service=service, options=chrome_options)
wait = WebDriverWait(driver, 15, poll_frequency=1)

driver.get("https://eaist.mos.ru/login.html?redirect=https://eaist.mos.ru/panel.html")

# Кнопка: "Войти в систему"
LOGIN = ("xpath", "/html/body/div[1]/div/button")
# Нажимаю на кнопку "Войти в систему"
wait.until(EC.element_to_be_clickable(LOGIN)).click()

# Получаю дерево 1-го хоста
shadow_host_1 = driver.find_element(
    "css selector",
    '[version="1"]'
)
# Получаю дерево 2-го хоста, который внутри 1-го
shadow_host_2 = get_shadow_root(shadow_host_1).find_element(
    'css selector',
    '[data-test-id="Модальное окно согласия на обработку ПД"]'
)

# Так как кнопка ссылается на тег span, то находим его
button = shadow_host_2.find_element(
    'tag name',
    'span'
)

# Дожидаюсь кликабельности кнопки и нажимаю на нее
wait.until(EC.element_to_be_clickable(button)).click()

sleep(10)
driver.close()
Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 3
Mike_Ro
@Mike_Ro Куратор тега Python
Python, JS, WordPress, SEO, Bots, Adversting
How to automate shadow DOM elements using selenium?

Если коротко, то порядок следующий:
1. Сначала нужно найти в основном dom тот элемент, к которому прикреплен shadow root.
2. У п1. нужно получить свойство .shadow_root.
3. И вот уже у п2. нужно вызывать его собственный метод find_element, чтобы найти нужный элемент внутри него.

Или так:
Локатор не находит элемент на сайте, хотя набран верно, пробовал разные варианты, не помогает, в чём проблема?
Ответ написан
@rPman
Это фича shadow dom

Единственное ее применение, как я понимаю, - это защита от блокировщиков рекламы, ее стали внедрять популярные веб сервисы (например пикабу)

Правда пометка open говорит что элементы внутри должны быть доступны из javascript
Ответ написан
Комментировать
@Delta-Strike
from selenium import webdriver
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from time import sleep

# 1. Настройки и запуск браузера
chrome_options = webdriver.ChromeOptions()
chrome_options.add_argument("--window-size=1000,900")
service = Service(ChromeDriverManager().install())
driver = webdriver.Chrome(service=service, options=chrome_options)
wait = WebDriverWait(driver, 15)

# 2. Открываем сайт
driver.get("https://eaist.mos.ru/login.html?redirect=https://e...")

# 3. Кнопка "Войти в систему"
login_button = wait.until(EC.element_to_be_clickable((By.XPATH, "/html/body/div[1]/div/button")))
login_button.click()

# 4. Функция для доступа к элементу внутри вложенных Shadow DOM
def get_shadow_element(driver, selectors):
script = '''
let el = document;
for (let sel of arguments[0]) {
el = el.querySelector(sel);
if (el && el.shadowRoot) {
el = el.shadowRoot;
}
}
return el;
'''
return driver.execute_script(script, selectors)

# 5. Ждём загрузки Shadow DOM
sleep(2)

# 6. Путь до нужного элемента внутри Shadow DOM
selectors = [
'eaist-app', # Первый Shadow Host
'eaist-panel-view', # Вложенный Shadow Host
'cookie-notification' # Ещё один Shadow Host
]

# 7. Получаем нужный элемент и кликаем по кнопке
shadow = get_shadow_element(driver, selectors)
button = shadow.find_element(By.CSS_SELECTOR, 'button') # Возможно, нужно уточнить селектор!
button.click()

# 8. Закрываем браузер
sleep(3)
driver.quit()
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы