12

I am having a trouble trying to figure out how to continue the script if an element is not found using selenium in Python.

This code cycles through reports, finds the refresh button and then clicks on the download the button. The only problem is, that some reports do not have a refresh button. So, I would like the script to continue if the button is not found.

I am still new to python/selenium so that is why I am posting the entire code. I need to know what to slip in to make this work! Thanks in advance for the help with this head banging problem

This is where selenium will try to click the refresh button

browser.find_element_by_css_selector("#ctl00_PlaceHolderMain_ReportViewer1_HtmlOutputReportResults2_updateFilters_TitleAnchor").click()

The complete code:

import time
import os
import os.path
import glob
import shutil
from selenium.webdriver.common.action_chains import ActionChains
from selenium.common.exceptions import MoveTargetOutOfBoundsException
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.common.exceptions import NoSuchElementException
from selenium.common.exceptions import NoAlertPresentException

files = glob.glob('/Users/me/Desktop/AUTOREPORTS/*')
for f in files:
    os.remove(f)        

open('/Users/me/Desktop/AUTOREPORTS/report.csv', "w")

for x in range(1, 73):      
    while True:    
        try:
            fp = webdriver.FirefoxProfile('C:/Users/me/Documents/FirefoxProfile')
            browser = webdriver.Firefox(fp)
            browser.get('https://websitething.com/')

            time.sleep(8)

            browser.find_element_by_id("ctl00_PlaceHolderMain_login_UserName").clear()
            browser.find_element_by_id("ctl00_PlaceHolderMain_login_UserName").send_keys("usr")
            browser.find_element_by_id("ctl00_PlaceHolderMain_login_password").clear()
            browser.find_element_by_id("ctl00_PlaceHolderMain_login_password").send_keys("paswd")
            browser.find_element_by_id("ctl00_PlaceHolderMain_login_login").click()

#gets user to reporting front end

            ReportMgr= browser.find_element_by_partial_link_text('Report Manager')
            ReportMgr.click()

            time.sleep(5)

            CustomReport= browser.find_element_by_partial_link_text('Custom Report')
            CustomReport.click()

            time.sleep(5)

            ProgramManagement= browser.find_element_by_partial_link_text('Program Management')
            ProgramManagement.click()
            ProgramManagement= browser.find_element_by_partial_link_text('Program Management').send_keys(Keys.ARROW_LEFT)

#pulls reports

            browser.find_element_by_partial_link_text('Program Management').click()
            time.sleep(60)
            browser.find_element_by_partial_link_text('Program Management').send_keys(Keys.ARROW_DOWN * x, Keys.ENTER)
            time.sleep(60)
            browser.find_element_by_css_selector("#ctl00_PlaceHolderMain_ReportViewer1_HtmlOutputReportResults2_updateFilters_TitleAnchor").click()            
            time.sleep(60)            
            browser.find_element_by_css_selector("#ctl00_PlaceHolderMain_ReportViewer1_HtmlOutputReportResults2_CSVButton_ImageAnchor > img").click()
            fname = "Report(%s).csv" % (x)
            os.chdir('/Users/me/Desktop/AUTOREPORTS')
            time.sleep(60)
            #browser.find_element_by_partial_link_text('Program Management').click()
            #time.sleep(30)
            browser.quit()

        except:
               browser.quit()           
               continue
        else:
               break

3 Answers 3

8

Use a try / except block to handle the exception and continue.

try:
    browser.find_element_by_css_selector("#ctl00_PlaceHolderMain_ReportViewer1_HtmlOutputReportResults2_updateFilters_TitleAnchor").click()
except NoSuchElementException:
    # do stuff

Note that Florent B.'s answer will find multiple elements on the page instead of one. So depending on what you need to find, and how many of them there are, it may cause minor performance issues. If you actually need to find multiple elements, his solution will work just fine, but for OP's question best practices dictate that we use the method designed to handle the task at hand instead of the method designed to handle multiples of the same task. It's like greedy matching vs lazy matching with regex. If you only need the first match, write a lazy pattern and it will be more efficient, even if writing the greedy pattern still works out technically.

Sign up to request clarification or add additional context in comments.

7 Comments

How should I offset the script using this method?
What do you mean by offset?
@Yogwhatup, yeah. If you only want it to skip clicking that specific button, but it still needs to do the actions scripted after clicking that specific button. If you need to skip the other actions, handle it all in the single try / except block.
@DuckPuncher: -1, while the code is correct, the comment is not. First, find_elements would not return multiple elements, but one or zero element. A unique selector cannot return more than one element. Second there will be no performance issue since the time difference would be at worst a few microsecond higher, which is insignificant and without performance impact whatsoever. You didn't even consider that raising an exception could be more expensive. Moreover the tests I performed show that handling the error is more expensive that using find_elements.
@DuckPuncher: Third, handling an exception to find whether an element is present or not is from a functional point of view not a best practice, but a workaround to fix the lack of a proper method. So counting if there is one element makes as much sense if not more. And fourth, if the efficiency between greedy matching vs lazy with a regex were of any concern, you'd be using a tokenizer and not a regex. So make yourself a favor and don't spread incorrect information.
|
3

You could check that at least one item is returned by find_elements:

elements = driver.find_elements_by_css_selector("...")
if elements :
  elements[0].click()

And if you are concerned that it is more expensive than a try/catch, well it's not, so both methods are valid.

Chrome 49.0.2623 Windows 8.1 (1000 samples) :

        | find_element | find_elements |
Sum     | 10077.4544ms |  9895.2903ms  | diff: -182.1641ms
Mean    |    10.0775ms |     9.8953ms  | diff:  -0.1822ms
Mediam  |     9.7563ms |     9.5361ms  | diff:  -0.2202ms
Min     |     9.0264ms |     9.0220ms  | diff:  -0.0044ms
Std     |     2.2050ms |     2.4411ms  |

Firefox 44.0.2 Windows 8.1 (1000 samples) :

        | find_element | find_elements |
Sum     |  9146.3389ms |  8860.2409ms  | diff: -286.0981ms
Mean    |     9.1463ms |     8.8602ms  | diff:  -0.2861ms
Mediam  |     8.0231ms |     7.8351ms  | diff:  -0.1880ms
Min     |     6.3442ms |     6.1565ms  | diff:  -0.1877ms
Std     |     5.3030ms |     6.2566ms  |

The code to compare find_element vs find_elements:

from selenium import webdriver
from statistics import mode, mean, pstdev, median
import time

driver = webdriver.Chrome()
driver.get("http://stackoverflow.com")
time.sleep(1)

selectors = [
  '#nav-questions',         # present
  '#abcdef',                # not present
  'div.nav:nth-child(2)',   # present
  'div.nav:nth-child(10)',  # not present
  '.youarehere',            # present
  '.abcdef'                 # not present
];

timesA = []
timesB = []

samples = 1000
for i in range(samples):

  start = time.clock()
  try:
    element = driver.find_element_by_css_selector(selectors[i % 6])
  except:
    pass
  timesA.append((time.clock()-start) * 1000.0)

  start = time.clock()
  elements = driver.find_elements_by_css_selector(selectors[i % 6])
  if not elements :
    pass
  timesB.append((time.clock()-start) * 1000.0)

print("\n".join([
  "        | find_element vs find_elements",
  "Sum     | {:10.4f}ms | {:10.4f}ms | diff: {:10.4f}ms".format(sum(timesA), sum(timesB), sum(timesB)-sum(timesA)),
  "Mean    | {:10.4f}ms | {:10.4f}ms | diff: {:10.4f}ms".format(mean(timesA), mean(timesB), mean(timesB)-mean(timesA)),
  "Mediam  | {:10.4f}ms | {:10.4f}ms | diff: {:10.4f}ms".format(median(timesA), median(timesB), median(timesB)-median(timesA)),
  "Min     | {:10.4f}ms | {:10.4f}ms | diff: {:10.4f}ms".format(min(timesA), min(timesB), min(timesB)-min(timesA)),
  "Std     | {:10.4f}ms | {:10.4f}ms |                 ".format(pstdev(timesA), pstdev(timesB))
]))

13 Comments

If you are only looking for 1 element, or the first element, this is excessive. Especially if there are more than 1 element that can be found with the selector on the page. driver.find_element_by_css_selector will finish once it finds the first element. For that, you need a try / except block.
@DuckPuncher, It will return 0 or 1 elements. If there is more, then the selector is incorrect to begin with. And saying that it's excessive without measurable facts is not a valid argument.
I guess it just doesn't make sense to use a method designed for doing something different when there is a method for finding 1 element, and a clear way of handling it if it isn't found. Your solution adds unneeded complexity, even if it is still really simple. If I'm reading this code without looking at the website, I assume there are multiple elements based on the fact that the find_elements_by_css_selector method was used. It just makes way more sense in this situation to use a try / except block.
@DuckPuncher, your arguments are subjective and pure speculation. I could also defend my point by saying that try/catch were designed to handled something that went wrong, but it would be pointless since both methods are valid and used in different frameworks.
Read up on selenium. Not finding an element throws an error, which clearly means something went wrong. I'm not trying to offend, only trying to provide the best solution for the question posed. Both methods work, I'm not arguing that. What I'm arguing is that one solution makes more sense for the question posed. If the question required the OP to not use try / catch, then it would make sense. Using your solution is the programming equivalent of finding an item at the grocery store and continuing to check every shelf to see if it is there instead of just checking out once it's found
|
1

As known whenever element is not available then it will throw exception. In this situation we know element is some times not available so we need to handle this exception.

In Java, we can handle it by using try/catch.

 try{
    driver.findElement(By.xpath(".//*[@id='menu-item-92']/a")).click(); //your element here
    }catch(Exception e){
        System.out.println(e.getMessage()); //printing exception
    }

Please make a note: if any case this element is to display but not, then still execution goes to next line as we handled exception.

Thank You, Murali

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.