The accepted answer is no longer valid and some of the other answers have some drawbacks or are not practical (the /deep/ selector doesn't work and is deprecated, document.querySelector('').shadowRoot works only with the first shadow element when shadow elements are nested), sometimes the shadow root elements are nested and the second shadow root is not visible in document root, but is available in its parent accessed shadow root. I think is better to use the selenium selectors and inject the script just to take the shadow root:

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

To put this into perspective I just added a testable example with Chrome's download page, clicking the search button needs open 3 nested shadow root elements:

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

Doing the same approach suggested in the other answers has the drawback that it hard-codes the queries, is less readable and you cannot use the intermediary selections for other actions: