I am having an issue with selenium explicit wait for a button to be clickable. When the button webelement is returned by
self.driver_wait.until(
expected_conditions.element_to_be_clickable((By.ID, 'login-btn'))
)
I am getting an obstruction error:
selenium.common.exceptions.ElementClickInterceptedException: Message: Element <a id="login-btn" class="fm-btn fm-btn-ghost-white account"> is not clickable at point (886,96) because another element <div id="ot-pc-content" class="ot-pc-scrollbar"> obscures it
Ironically it explicitely states, that the element is not clickable, although the explicit wait instruction should wait exactly for this to my undestanding ...
I tried to wait until the obstructing element is not visible anymore, but that didn't help:
self.driver_wait.until(
expected_conditions.invisibility_of_element((By.ID, 'ot-pc-content'))
)
button_login_1 = self.driver_wait.until(
expected_conditions.element_to_be_clickable((By.ID, 'login-btn'))
)
I guess this behaviour will be reproducible using this minimum example, that includes everything needed from a class definition:
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions
import time
import datetime as dt
from typing import Union
class Session_FLM:
def __init__(self,
url: str = 'https://www.freelancermap.de/',
browser: str = "FireFox",
session_time: Union[None, dt.datetime] = None,
use_fixed_wait_default: bool = False,
default_wait_time_fixed: float = 1,
default_explicit_wait_max: float = 10,
implicit_wait_max: float = 10
):
self.browser: str = browser
if session_time:
self.session_time = session_time
else:
self.session_time = dt.datetime.now()
self.url: str = url
self.cookies: bool = False
self.default_explicit_wait_max: float = default_explicit_wait_max
self.use_fixed_wait_default: bool = use_fixed_wait_default
self.default_wait_time_fixed: float = default_wait_time_fixed
self.implicit_wait_max: float = implicit_wait_max
self.driver: Union[
webdriver.firefox.webdriver.WebDriver,
webdriver.chrome.webdriver.WebDriver,
None] = None
self.driver_wait: Union[WebDriverWait, None] = None
self.headers: dict = {
'User-Agent': 'Mozilla/5.0'
}
def init_webdriver(
self,
url: Union[str, None] = None,
use_fixed_wait: Union[bool, None] = None,
wait_time_fixed: Union[float, None] = None,
activate_implicit_wait: bool = False,
implicit_wait_max: Union[float, None] = None,
explicit_wait_max: Union[float, None] = None
) -> None:
if not url:
url = self.url
if not use_fixed_wait:
use_fixed_wait = self.use_fixed_wait_default
if not wait_time_fixed:
wait_time_fixed = self.default_wait_time_fixed
if not implicit_wait_max:
implicit_wait_max = self.implicit_wait_max
if not explicit_wait_max:
explicit_wait_max = self.default_explicit_wait_max
if self.browser == 'Chrome':
driver_options = webdriver.ChromeOptions()
driver_options.page_load_strategy = 'normal' # wait for entire page content to be loaded
self.driver = webdriver.Chrome(options=driver_options)
elif self.browser == 'FireFox':
driver_options = webdriver.FirefoxOptions()
driver_options.page_load_strategy = 'normal' # wait for entire page content to be loaded
self.driver = webdriver.Firefox(options=driver_options)
self.driver_wait = WebDriverWait(self.driver, explicit_wait_max)
if activate_implicit_wait:
self.driver.implicitly_wait(implicit_wait_max)
self.driver.get(url=url)
if use_fixed_wait:
time.sleep(wait_time_fixed)
def cookies_handling(
self,
accept_cookies: bool = False,
use_fixed_wait: Union[bool, None] = None,
wait_time_fixed: Union[float, None] = None
) -> None:
"""
Handle freelancermap cookies.
Args:
accept_cookies (bool, optional): True: Accept all cookies; False: Enter cookie details and only accept obigatory cookies.
use_fixed_wait: (Union[bool, None], optional): Only use fixed wait if True
wait_time_fixed (Union[float, None], optional): Fixed waiting time, which is used whenever "Selenium Implicit Wait" is set to be inactive; overrides "Session_FLM" objects default fixed waiting time.
"""
if not use_fixed_wait:
use_fixed_wait = self.use_fixed_wait_default
if not wait_time_fixed:
wait_time_fixed = self.default_wait_time_fixed
if accept_cookies:
button_accept_cookies = self.driver_wait.until(
expected_conditions.element_to_be_clickable((By.ID, 'onetrust-accept-btn-handler'))
)
button_accept_cookies.click()
else:
button_settings_cookies = self.driver_wait.until(
expected_conditions.element_to_be_clickable((By.ID, 'onetrust-pc-btn-handler'))
)
button_settings_cookies.click()
if use_fixed_wait:
time.sleep(wait_time_fixed)
button_accept_selected_cookies = self.driver_wait.until(
expected_conditions.element_to_be_clickable((By.CLASS_NAME, 'save-preference-btn-handler'))
)
button_accept_selected_cookies.click()
if use_fixed_wait:
time.sleep(wait_time_fixed)
def login(
self,
email: str,
password: str,
use_fixed_wait: Union[bool, None] = None,
wait_time_fixed: Union[float, None] = None
) -> None:
"""
Login with an existing freelancermap account.
Args:
email (str): Account email
password (str): Account password
use_fixed_wait: (Union[bool, None], optional): Only use fixed wait if True
wait_time_fixed(Union[float, None], optional): Fixed waiting time, which is used whenever "Selenium Implicit Wait" is set to be inactive; overrides "Session_FLM" objects default fixed waiting time.
"""
if not wait_time_fixed:
wait_time_fixed = self.default_wait_time_fixed
if use_fixed_wait:
time.sleep(wait_time_fixed)
self.driver_wait.until(
expected_conditions.invisibility_of_element((By.ID, 'ot-pc-content'))
)
button_login_1 = self.driver_wait.until(
expected_conditions.element_to_be_clickable((By.ID, 'login-btn'))
)
button_login_1.click()
if use_fixed_wait:
time.sleep(wait_time_fixed)
input_field_email = self.driver.find_element(
By.ID,
"login"
)
input_field_email.send_keys(email)
input_field_pw = self.driver.find_element(
By.ID,
"password"
)
input_field_pw.send_keys(password)
button_login_2 = self.driver_wait.until(
expected_conditions.element_to_be_clickable((By.CSS_SELECTOR, 'button.fm-btn-primary:nth-child(1)'))
)
button_login_2.submit()
self.driver_wait.until(
expected_conditions.url_to_be('https://www.freelancermap.de/mein_account.html')
)
if use_fixed_wait:
time.sleep(wait_time_fixed)
self.driver.back()
if use_fixed_wait:
time.sleep(wait_time_fixed)
if __name__ == '__main__':
flm = Session_FLM(
url='https://www.freelancermap.de/projektboerse.html',
default_wait_time_fixed=2
)
flm.init_webdriver(activate_implicit_wait=False)
flm.cookies_handling(
accept_cookies=False,
use_fixed_wait=False)
flm.login(
email='abc',
password='123',
use_fixed_wait=False
)
- Clicking the button works, when
cookies_handling()is called with the argumentuse_fixed_wait=True. - Clicking the button works, when
login()is called with the argumentuse_fixed_wait=True. - Clicking the button works, when
cookies_handling()is called with the argumentaccept_cookies=False, as in this case the obstructing element won't appear at all. - Clicking the button fails, when all those parameters are set to
False.
I would really love to understand what is the problem with my approach and how to cope with it without adding time.sleep().
This is what you should have posted as an MCVE,
The rest of your code is irrelevant to the error and just creates confusion and obfuscates the issue.
This code works just fine on my machine. From looking at the HTML provided by the click intercepted exception, it looks like the cookie banner is what your code is clicking. My suggestion is to accept or otherwise dismiss the cookie banner first, then proceed with the rest of your code and you shouldn't have any issues.