Reputation: 3517
I have a problem; I am using the Selenium (Firefox) web driver to open a webpage, click a few links, etc., and then capture a screenshot.
My script runs fine from the CLI, but when run via a cron job it is not getting past the first find_element() test. I need to add some debug, or something to help me figure out why it is failing.
Basically, I have to click a 'log in' anchor before going to the login page. The construct of the element is:
<a class="lnk" rel="nofollow" href="/login.jsp?destination=/secure/Dash.jspa">log in</a>
I am using the find_element By LINK_TEXT method:
login = driver.find_element(By.LINK_TEXT, "log in").click()
A) How do I check that the link is actually being picked up by Python? Should I use try/catch block?
B) Is there a better/more reliable way to locate the DOM element than by LINK_TEXT? E.g., in jQuery, you can use a more specific selector, $('a.lnk:contains(log in)').do_something();
I have solved the main problem and it was just finger trouble. I was calling the script with incorrect parameters - a simple mistake.
I'd still like some pointers on how to check whether an element exists. Also, an example/explanation of implicit / explicit Waits instead of using a crappy time.sleep() call.
Upvotes: 133
Views: 366524
Reputation: 11
el = WebDriverWait(driver, timeout=3).until(lambda d: d.find_element(By.TAG_NAME,"p"))
Upvotes: -1
Reputation: 8555
You can grab a list of elements instead of a single element. An empty list in python is falsey. Example:
if driver.find_elements(By.CSS_SELECTOR, '#element'):
print "Element exists!"
You can also use By.ID
and By.NAME
, but that just turns your id or name into a css selector anyway. Source
Upvotes: 125
Reputation: 105
The same as Brian, but add to this answer from tstempko:
I tried and it works quickly:
driver.implicitly_wait(0)
if driver.find_element_by_id("show_reflist"):
driver.find_element_by_id("show_reflist").find_element_by_tag_name("img").click()
After this, I restore my default value:
driver.implicitly_wait(30)
Upvotes: 7
Reputation: 313
A) Use .is_displayed()
as explained at Information about web elements:
# After navigating to the URL,
# get the Boolean value for if this element is displayed
is_button_visible = driver.find_element(By.CSS_SELECTOR, "[name='login']").is_displayed()
Continue your Visual Studio Code logic using "is_button_visible" for branching.
B) Refer to Sam Woods's answer
Upvotes: 0
Reputation: 154
You can check by find_elements. If the result is null, that element does not exist.
if driver.find_elements(By.SOMETHING, "#someselector") == []:
continue # That element does not exist
Upvotes: 0
Reputation: 29
driver.find_element_by_id("some_id").size()
is a class method.
We need:
driver.find_element_by_id("some_id").size
which is a dictionary, so:
if driver.find_element_by_id("some_id").size['width'] != 0:
print 'button exist'
Upvotes: 2
Reputation: 7372
For a):
from selenium.common.exceptions import NoSuchElementException
def check_exists_by_xpath(xpath):
try:
webdriver.find_element_by_xpath(xpath)
except NoSuchElementException:
return False
return True
For b): Moreover, you can take the XPath expression as a standard throughout all your scripts and create functions as above mentions for universal use.
I recommend to use CSS selectors. I recommend not to mix/use "by id", "by name", etc. and use one single approach instead.
Upvotes: 185
Reputation: 1850
A) Yes. The easiest way to check if an element exists is to simply call find_element
inside a try/catch
.
B) Yes, I always try to identify elements without using their text for two reasons:
The solution is either:
For the follow-up questions, using try/catch
is how you can tell if an element exists or not and good examples of waits can be found here: http://seleniumhq.org/docs/04_webdriver_advanced.html
Upvotes: 71
Reputation: 431
You could use is_displayed() like below:
res = driver.find_element_by_id("some_id").is_displayed()
assert res, 'element not displayed!'
Upvotes: 1
Reputation: 939
A solution without try&catch and without new imports:
if len(driver.find_elements_by_id('blah')) > 0: # Pay attention: find_element*s*
driver.find_element_by_id('blah').click # Pay attention: find_element
Upvotes: 29
Reputation: 2212
With the latest Selenium, you can now use you can now use .is_displayed()
:
https://www.selenium.dev/documentation/webdriver/elements/information/
Upvotes: 1
Reputation: 5202
When you know the element could not be there, the implicit wait could be a problem. I've created a simple context manager to avoid those waiting times
class PauseExplicitWait(object):
"""
Example usage:
with PauseExplicitWait(driver, 0):
driver.find_element(By.ID, 'element-that-might-not-be-there')
"""
def __init__(self, driver, new_wait=0):
self.driver = driver
self.original_wait = driver.timeouts.implicit_wait
self.new_wait = new_wait
def __enter__(self):
self.driver.implicitly_wait(self.new_wait)
def __exit__(self, exc_type, exc_value, exc_tb):
self.driver.implicitly_wait(self.original_wait)
Upvotes: 2
Reputation: 19
You can find elements by available methods and check response array length if the length of an array equal the 0 element not exist.
element_exist = False if len(driver.find_elements_by_css_selector('div.eiCW-')) > 0 else True
Upvotes: 1
Reputation: 2368
You could also do it more concisely using
driver.find_element_by_id("some_id").size != 0
Upvotes: 6