I'm trying to click on the loging button using Selenium, but it's not working somehow. I checked other repos and also previous threads but cannot find the solution. Here is my code:
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
class Bot:
def __init__(self):
self.driver = webdriver.Chrome()
def interact(self, selector: str, text: str=None, wait=2, by=By.CSS_SELECTOR):
element = self.driver.find_element(by=by, value=selector)
if text is None:
element.click()
else:
element.send_keys(text)
time.sleep(wait)
class RedditBot(Bot):
def __init__(self):
super().__init__()
self.driver.get("https://www.reddit.com/login/")
if __name__ == "__main__":
rb = RedditBot()
rb.interact("input[id='login-username']", text="Brave_Primary1797")
rb.interact("input[id='login-password']", text="EcOjllIrz3M87q9S08M")
rb.interact("#login > auth-flow-modal > div.w-100 > faceplate-tracker > button") # this line errors
1 Answer 1
Problem is because it uses shadow-root to make some elements protected against accidental changes by other elements. It allows to make templates and slots - and web developer may have simpler work.
But it makes problem to access these elements during web scraping.
You have to find out which elements use shadow-root (you can see it in HTML in DevTools in Chrome/Firefox) and use find_element(...).shadow_root.find_element(...) to get elements inside shadow-root.
I found all elements with shadow-root in DevTools and I started checking which one needs to use .shadow_root - so finally I created something like this to test it manually:
(a it works for me)
def get_item(driver, selector, shadow=True):
print('[DEBUG] ##### selector:', selector, shadow)
item = driver.find_element(*selector)
for child in item.find_elements(By.CSS_SELECTOR, '*'):
print(f'[DEBUG] child: <{child.tag_name}>')
if shadow:
print('[DEBUG] !!! getting shadow root')
item = item.shadow_root
return item
if __name__ == "__main__":
try:
rb = RedditBot()
time.sleep(2)
rb.interact("input[id='login-username']", text="Brave_Primary1797")
rb.interact("input[id='login-password']", text="EcOjllIrz3M87q9S08M")
time.sleep(1)
#rb.interact("#login > auth-flow-modal > div.w-100 > faceplate-tracker > button") # this line errors
item = rb.driver
item = get_item(item, (By.CSS_SELECTOR, 'shreddit-overlay-display'))
item = get_item(item, (By.CSS_SELECTOR, 'shreddit-signup-drawer'))
item = get_item(item, (By.CSS_SELECTOR, 'shreddit-drawer'), False)
item = get_item(item, (By.CSS_SELECTOR, 'div'), False)
item = get_item(item, (By.CSS_SELECTOR, 'shreddit-async-loader'), False)
item = get_item(item, (By.CSS_SELECTOR, 'div'), False)
item = get_item(item, (By.CSS_SELECTOR, 'shreddit-slotter'))
item = get_item(item, (By.CSS_SELECTOR, 'span'), False)
item = get_item(item, (By.CSS_SELECTOR, 'shreddit-async-loader'), False)
item = get_item(item, (By.CSS_SELECTOR, 'auth-flow-login'), False)
item = get_item(item, (By.CSS_SELECTOR, 'faceplate-tabpanel'), False)
item = get_item(item, (By.CSS_SELECTOR, 'faceplate-form'), False)
item = get_item(item, (By.CSS_SELECTOR, 'auth-flow-modal'), False)
# item = get_item(item, (By.CSS_SELECTOR, 'div.w-100'), False) # it finds different element
item = get_item(item, (By.CSS_SELECTOR, 'div[slot]'), False) # OK
# item = get_item(item, (By.CSS_SELECTOR, 'div[slot="primaryButton"]'), False) # OK
item = get_item(item, (By.CSS_SELECTOR, 'faceplate-tracker'), False)
item = item.find_element(By.CSS_SELECTOR, 'button')
item.click()
except Exception as ex:
print("Exception", ex)
Because in function get_item() I display children so I could see that I can reduce it to
if __name__ == "__main__":
try:
rb = RedditBot()
time.sleep(2)
rb.interact("input[id='login-username']", text="Brave_Primary1797")
rb.interact("input[id='login-password']", text="EcOjllIrz3M87q9S08M")
time.sleep(1)
(
rb.driver
.find_element(By.CSS_SELECTOR, 'shreddit-overlay-display').shadow_root
.find_element(By.CSS_SELECTOR, 'shreddit-signup-drawer').shadow_root
.find_element(By.CSS_SELECTOR, 'shreddit-slotter').shadow_root
.find_element(By.CSS_SELECTOR, "button")
).click()
except Exception as ex:
print("Exception", ex)
Full code which I was using:
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
class Bot:
def __init__(self):
self.driver = webdriver.Chrome()
#self.driver = webdriver.Firefox()
def interact(self, selector: str, text: str=None, wait=2, by=By.CSS_SELECTOR):
element = self.driver.find_element(by=by, value=selector)
if text is None:
element.click()
else:
element.send_keys(text)
time.sleep(wait)
class RedditBot(Bot):
def __init__(self):
super().__init__()
self.driver.get("https://www.reddit.com/login/")
def get_item(driver, selector, shadow=True, attributes=['id', 'class']):
print('[DEBUG] ##### selector:', selector, shadow)
item = driver.find_element(*selector)
for attr in attributes:
print(f'[DEBUG] {attr}:', item.get_attribute(attr))
for child in item.find_elements(By.CSS_SELECTOR, '*'):
print(f'[DEBUG] child: <{child.tag_name}>')
if shadow:
print('[DEBUG] !!! getting shadow root')
item = item.shadow_root
return item
def click_button_long_version():
item = rb.driver
item = get_item(item, (By.CSS_SELECTOR, 'shreddit-overlay-display'))
item = get_item(item, (By.CSS_SELECTOR, 'shreddit-signup-drawer'))
item = get_item(item, (By.CSS_SELECTOR, 'shreddit-drawer'), False)
item = get_item(item, (By.CSS_SELECTOR, 'div'), False)
item = get_item(item, (By.CSS_SELECTOR, 'shreddit-async-loader'), False)
item = get_item(item, (By.CSS_SELECTOR, 'div'), False)
item = get_item(item, (By.CSS_SELECTOR, 'shreddit-slotter'))
item = get_item(item, (By.CSS_SELECTOR, 'span'), False)
item = get_item(item, (By.CSS_SELECTOR, 'shreddit-async-loader'), False)
item = get_item(item, (By.CSS_SELECTOR, 'auth-flow-login'), False)
item = get_item(item, (By.CSS_SELECTOR, 'faceplate-tabpanel'), False)
item = get_item(item, (By.CSS_SELECTOR, 'faceplate-form'), False)
item = get_item(item, (By.CSS_SELECTOR, 'auth-flow-modal'), False)
# item = get_item(item, (By.CSS_SELECTOR, 'div.w-100'), False) # it finds different element
item = get_item(item, (By.CSS_SELECTOR, 'div[slot]'), False) # OK
# item = get_item(item, (By.CSS_SELECTOR, 'div[slot="primaryButton"]'), False) # OK
item = get_item(item, (By.CSS_SELECTOR, 'faceplate-tracker'), False)
item = item.find_element(By.CSS_SELECTOR, 'button')
item.click()
def click_button():
item = (
rb.driver
.find_element(By.CSS_SELECTOR, 'shreddit-overlay-display').shadow_root
.find_element(By.CSS_SELECTOR, 'shreddit-signup-drawer').shadow_root
.find_element(By.CSS_SELECTOR, 'shreddit-slotter').shadow_root
#.find_element(By.CSS_SELECTOR, "#login auth-flow-modal div.w-100 faceplate-tracker button")
.find_element(By.CSS_SELECTOR, "button")
#.find_element(By.CSS_SELECTOR, "#login auth-flow-modal div[slot] faceplate-tracker button")
)
item.click()
if __name__ == "__main__":
try:
rb = RedditBot()
time.sleep(2)
rb.interact("input[id='login-username']", text="Brave_Primary1797")
rb.interact("input[id='login-password']", text="EcOjllIrz3M87q9S08M")
time.sleep(1)
#rb.interact("#login > auth-flow-modal > div.w-100 > faceplate-tracker > button") # this line errors
click_button_long_version()
#click_button()
except Exception as ex:
print("Exception", ex)
input("Press ENTER to exit")
Comments
Explore related questions
See similar questions with these tags.
shadow-rootwhich is in anothershadow-root,etc. It may need something likefind_element('shreddit-async-loader').shadow_root.find_element('auth-flow-login').shadow_root.....