1

I am running into this issue when trying to click a button using selenium. The button html reads as below:

<button class="Component-button-0-2-65 Component-button-d1-0-2-68">and 5 more</button>

My code is here:

button = EC.element_to_be_clickable((By.XPATH,'.//button[contains(@class,"Component-button-d")]'))
    if button:
        print("TRUE")
        button.click()

My output is:

TRUE
Traceback (most recent call last):
  File "", line 47, in <module>
    button.click()
AttributeError: 'function' object has no attribute 'click'

I am stumped as to why 'button' element is found by selenium (print(True) statement is executed) but then the click() method returns an attribute error.

This is the page I am scraping data from: https://religiondatabase.org/browse/regions I am able to extract all the information I need on the page, so the code leading up to the click is working.

I was expecting the item to be clickable. I'm not sure how to troubleshoot the attribute error (function object has no attribute click). Because it I paste the xpath into the webpage, it highlights the correct element.

undetected Selenium
  • 183,867
  • 41
  • 278
  • 352
aa03
  • 33
  • 4

2 Answers2

1

The element search is always done through the driver object which is the WebDriver instance in it's core form:

driver.find_element(By.XPATH, "element_xpath")

But when you apply WebDriverWait, the wait configurations are applied on the driver object.

WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.XPATH, "element_xpath")))    

So even during WebDriverWait the driver object needs to be present along with the expected_conditions and the locator strategy.


This usecase

This line of code:

button = EC.element_to_be_clickable((By.XPATH,'.//button[contains(@class,"Component-button-d")]'))

button object references to a junk value but on probing returns true and prints TRUE but the click can't be performed as button is not of WebElement type.


Solution

Given the HTML:

<button class="Component-button-0-2-65 Component-button-d1-0-2-68">and 5 more</button>

To click on the element you need to induce WebDriverWait for the element_to_be_clickable() and you can use either of the following locator strategies:

  • Using XPATH with classname:

    WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.CSS_SELECTOR, "//button[contains(@class,'Component-button-d')]"))).click()
    
  • Using XPATH with classname and innerText:

    WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.XPATH, "//button[contains(@class,'Component-button-d') and text()='and 5 more']"))).click()
    
  • Note: You have to add the following imports :

    from selenium.webdriver.support.ui import WebDriverWait
    from selenium.webdriver.common.by import By
    from selenium.webdriver.support import expected_conditions as EC
    
undetected Selenium
  • 183,867
  • 41
  • 278
  • 352
  • 1
    Thank you for such a detailed response. I really appreciate it. I'll choose this answer as accepted since this provides more detail. – aa03 Feb 28 '23 at 21:24
0

Your code is incorrect. You can find below an example of how you can use Expected Conditions (along with WebdriverWait):

from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
## [...define your driver here, other imports etc]

wait = WebDriverWait(driver, 25)

##[open a url, etc]
element = wait.until(EC.presence_of_element_located((By.XPATH, '//div[@class="input-group"]')))
element.click()

Selenium documentation can be found here.

Barry the Platipus
  • 9,594
  • 2
  • 6
  • 30