Cómo manejar elementos dentro de Shadow DOM de Selenium

Quiero automatizar la finalización de la descarga de archivos en chromedriver . HTML de cada entrada en la lista de descargas se ve como

 DownloadedFile#1 

Así que uso el siguiente código para encontrar elementos de destino:

 driver.get('chrome://downloads/') # This page should be available for everyone who use Chrome browser driver.find_elements_by_tag_name('a') 

Esto devuelve una lista vacía mientras hay 3 nuevas descargas.

Como descubrí, solo se pueden manejar los elementos primarios de la etiqueta #shadow-root (open) . Entonces, ¿cómo puedo encontrar elementos dentro de este elemento #shadow-root ?

    Puede usar el método driver.executeScript() para acceder a los elementos HTML y objetos JavaScript en su página web.

    En el siguiente ejemplo, executeScript devolverá en una Promise la lista de nodos de todos los elementos presentes en el árbol de elementos de la sombra cuyo id es el host . Entonces puedes realizar tu prueba de aserción:

     it( 'check shadow root content', function () { return driver.executeScript( function () { return host.shadowRoot.querySelectorAll( 'a' ).then( function ( n ) { return expect( n ).to.have.length( 3 ) } } ) } ) 

    Nota: no sé Python, así que he usado la syntax de JavaScript, pero debería funcionar de la misma manera.

    A veces, los elementos raíz raíz están nesteds y la segunda raíz sombra no es visible en la raíz del documento, pero está disponible en su raíz sombra local a la que se accede. Creo que es mejor usar los selectores de selenium e inyectar el script solo para tomar la raíz de la sombra:

     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() 

    Para poner esto en perspectiva, acabo de agregar un ejemplo comprobable con la página de descarga de Chrome, al hacer clic en el botón de búsqueda se necesitan abrir 3 elementos de raíz de sombra nesteds: introduzca la descripción de la imagen aquí

     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() 

    Hacer el mismo enfoque sugerido en las otras respuestas tiene el inconveniente de que codifica las consultas, es menos legible y no puede usar las selecciones intermedias para otras acciones:

     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() 

    Edición posterior:

    Recientemente traté de acceder a la configuración de contenido (ver código a continuación) y tiene más de un elemento raíz raíz imbricado ahora, no puede acceder a uno sin expandir primero el otro, cuando por lo general también tiene contenido dynamic y más de 3 elementos sombra uno a otro. hace imposible la automatización. La respuesta anterior se usa para trabajar hace un tiempo, pero es suficiente para que un solo elemento cambie de posición y siempre debe ir con el elemento inspeccionar y ver si se encuentra en una raíz de sombra, la pesadilla de la automatización.

    No solo era difícil encontrar la configuración del contenido debido a las sombras y el cambio dynamic cuando se encuentra que el botón no se puede hacer clic en este punto.

     driver = webdriver.Chrome() def expand_shadow_element(element): shadow_root = driver.execute_script('return arguments[0].shadowRoot', element) return shadow_root driver.get("chrome://settings") root1 = driver.find_element_by_tag_name('settings-ui') shadow_root1 = expand_shadow_element(root1) root2 = shadow_root1.find_element_by_css_selector('[page-name="Settings"]') shadow_root2 = expand_shadow_element(root2) root3 = shadow_root2.find_element_by_id('search') shadow_root3 = expand_shadow_element(root3) search_button = shadow_root3.find_element_by_id("searchTerm") search_button.click() text_area = shadow_root3.find_element_by_id('searchInput') text_area.send_keys("content settings") root0 = shadow_root1.find_element_by_id('main') shadow_root0_s = expand_shadow_element(root0) root1_p = shadow_root0_s.find_element_by_css_selector('settings-basic-page') shadow_root1_p = expand_shadow_element(root1_p) root1_s = shadow_root1_p.find_element_by_css_selector('settings-privacy-page') shadow_root1_s = expand_shadow_element(root1_s) content_settings_div = shadow_root1_s.find_element_by_css_selector('#site-settings-subpage-trigger') content_settings = content_settings_div.find_element_by_css_selector("button") content_settings.click()