Подтвердить что ты не робот

Доступ к дереву темной DOM с селеном

Можно ли получить доступ к элементам в Теневой DOM с помощью веб-сервера Selenium/Chrome?

Использование методов поиска обычных элементов не работает, как и следовало ожидать. Я видел ссылки на switchToSubTree на w3c, но не смог найти какие-либо фактические документы, примеры и т.д.

Кто-нибудь имел успех с этим?

4b9b3361

Ответ 2

Принятый ответ более недействителен, а некоторые другие ответы имеют некоторые недостатки или непрактичны (селектор /deep/ не работает и устарел, document.querySelector('').shadowRoot работает только с первым теневым элементом, когда теневые элементы вложенные), иногда теневые корневые элементы вложены, а второй теневой корень не отображается в корневом каталоге документа, но доступен в его корневом корневом корневом доступе. Я считаю, что лучше использовать селеуторы селена и ввести script только для того, чтобы взять теневой корень:

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

outer = expand_shadow_element(driver.find_element_by_css_selector("#test_button"))
inner = outer.find_element_by_id("inner_button")
inner.click()

Чтобы представить это в перспективе, я просто добавил тестовый пример на странице загрузки Chrome, нажав кнопку поиска, нужно открыть 3 вложенных теневых элемента: введите описание изображения здесь

import selenium
from selenium import webdriver
driver = webdriver.Chrome()


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

driver.get("chrome://downloads")
root1 = driver.find_element_by_tag_name('downloads-manager')
shadow_root1 = expand_shadow_element(root1)

root2 = shadow_root1.find_element_by_css_selector('downloads-toolbar')
shadow_root2 = expand_shadow_element(root2)

root3 = shadow_root2.find_element_by_css_selector('cr-search-field')
shadow_root3 = expand_shadow_element(root3)

search_button = shadow_root3.find_element_by_css_selector("#search-button")
search_button.click()

Выполнение того же подхода, предложенного в других ответах, имеет недостаток, заключающийся в том, что он жестко кодирует запросы, менее читабельен и вы не можете использовать промежуточные выборы для других действий:

search_button = driver.execute_script('return document.querySelector("downloads-manager").shadowRoot.querySelector("downloads-toolbar").shadowRoot.querySelector("cr-search-field").shadowRoot.querySelector("#search-button")')
search_button.click()

Ответ 4

Обычно вы делаете это:

element = webdriver.find_element_by_css_selector(
    'my-custom-element /deep/ .this-is-inside-my-custom-element')

И, надеюсь, это будет продолжаться.


Однако обратите внимание, что /deep/ и ::shadow устарели (и не реализованы в браузерах, отличных от Opera и Chrome). Там много разговоров о разрешении их в статическом профиле. Значение, запрос для них будет работать, но не стиль.

Если вы не хотите полагаться на /deep/ или ::shadow, потому что их фьючерсы немного неопределенны или потому, что вы хотите работать лучше, перекрестно-браузер или потому, что вы ненавидите предупреждения об отказе, радуйтесь, как и по-другому:

# Get the shadowRoot of the element you want to intrude in on,
# and then use that as your root selector.
shadow_root = webdriver.execute_script('''
    return document.querySelector(
        'my-custom-element').shadowRoot;
    ''')
element = shadow_root.find_element_by_css_selector(
    '.this-is-inside-my-custom-element')

Подробнее об этом:

Ответ 5

Я использую С# и Selenium и сумел найти элемент внутри скрытой теневой DOM с помощью java script. Это мое дерево html:

html tree

Я хочу URL-адрес на последней строке и, чтобы получить его, я сначала выбираю тег "downloads-manager", а затем первый теневой корень прямо под ним. Однажды внутри корня тени я хочу найти элемент, ближайший к следующему теневому корню. Этот элемент - "элемент загрузки". С выбранным мной я могу ввести второй теневой корень. Оттуда я выбираю элемент img, содержащий url, по id = "file-icon". Наконец, я могу получить атрибут "src", который содержит URL-адрес, который я ищу.

Две строки кода С#, которые делают трюк:

IJavaScriptExecutor jse2 = (IJavaScriptExecutor)_driver;
var pdfUrl = jse2.ExecuteScript("return document.querySelector('downloads-manager').shadowRoot.querySelector('downloads-item').shadowRoot.getElementById('file-icon').getAttribute('src')");

Ответ 6

Это сработало для меня (используя привязки javacript selenium):

driver.executeScript("return $('body /deep/ <#selector>')")

Это возвращает элемент (ы), который вы ищете.

Ответ 7

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

IWebDriver driver;

public IWebElement getUIObject(params By[] shadowRoots)
        {
            IWebElement currentElement = null;
            IWebElement parentElement = null;
            int i = 0;
            foreach (var item in shadowRoots)
            {
                if (parentElement == null)
                {
                    currentElement = driver.FindElement(item);
                }
                else
                {
                    currentElement = parentElement.FindElement(item);
                }
                if(i !=(shadowRoots.Length-1))
                {
                    parentElement = expandRootElement(currentElement);
                }
                i++;
            }
            return currentElement;
        }

 public IWebElement expandRootElement(IWebElement element)
        {
            IWebElement rootElement = (IWebElement)((IJavaScriptExecutor)driver)
        .ExecuteScript("return arguments[0].shadowRoot", element);
            return rootElement;
        }

Страница загрузки Google Chrome

Теперь, как показано на рисунке, мы должны развернуть три теневых корневых элемента, чтобы получить наш значок поиска. Чтобы щелкнуть по значку, все, что нам нужно сделать, это: -

  [TestMethod]
        public void test()
        {
           IWebElement searchButton= getUIObject(By.CssSelector("downloads-manager"),By.CssSelector("downloads-toolbar"),By.Id("search-input"),By.Id("search-buton"));
            searchButton.Click();
        }

Таким образом, только одна строка предоставит вам ваш веб-элемент, просто нужно убедиться, что вы передаете первый теневой корневой элемент в качестве первого аргумента функции второго элемента shadow getOIObject в качестве второго аргумента функции и т.д., последним аргументом для функции будет идентификатор вашего фактического элемента (для этого случая его "кнопка поиска)