5

Below is a snippet of code from my python script that is reading an excel file and assigning cells in a row to a variable that is then used to be typed into a field in the browser. it works great ... for the most part. what i would like to do is setup some sort of loop after the browser loads the page to do the following:

find the element by some ID. if this fails, wait 5 seconds then try again. if it succeeds carry on with the rest of the script. now go easy on me, this is my first real attempt. i have tried to nest try/except statements but that got really messy fast.

from selenium import webdriver
from selenium.common.exceptions import NoSuchElementException
from selenium.webdriver.common.keys import Keys
import time
import xlrd

workbook = xlrd.open_workbook("my_excel_file.xlsx")
worksheet = workbook.sheet_by_name('Sheet1')
x = 0
for current_row in range(worksheet.nrows):
    try:
        cmt = worksheet.row(current_row)[2].value
        browser = webdriver.Firefox() # Get local session of firefox
        browser.get("http://www.somewebsite.com") # Load page
        time.sleep(5)
        #this timer is the issue, if the field takes 6 seconds to be ready, script fails
        comment = browser.find_element_by_id("slow_comment_box") # Find the comment box
        comment.send_keys(str(cmt) + Keys.RETURN)
        x += 1
    except:
        print ("Error on " + str(x))
        quit ()

is there a way to set this to behave the way i stated above? i know selenium waits for the page to load but the text box is not a normal one and appears to have its own loading, spinning wheel.

summary and solution the answer is below. my dumb stuff had some syntax errors. this page was extremely useful as well.

dalearyous
  • 169
  • 1
  • 4
  • 15

2 Answers2

12

You want to use WebDriverWait

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

element = WebDriverWait(driver, 60).until(EC.visibility_of_element_located((By.Id, 'id')))
Andersson
  • 51,635
  • 17
  • 77
  • 129
Erki M.
  • 5,022
  • 1
  • 48
  • 74
  • so in my example, do i still need the comment = browser.find_element_by_id("slow_comment_box")? or does the WebDriverWait do this for me if its available? – dalearyous Feb 24 '14 at 23:52
  • nothing i am trying works, is there a library i am missing? no syntax or compiling errors. – dalearyous Feb 25 '14 at 00:14
  • The webdriverwait will poll the DOM for given period of time (in this case 60 sec) and will return you the webElement once it is found (and in this case is also visible, meaning having size > 1px) or will throw timeout if timeout is exeeded. – Erki M. Feb 25 '14 at 11:30
  • [code]wait = WebDriverWait(browser, 30) element = wait.until(EC.element_to_be_clickable((By.ID, "someID")))[/code] this was the only way i found that my script would run. however i do not think its working properly. i never could get your example to work with my example. – dalearyous Feb 25 '14 at 14:08
  • if i make a super basic example with your code, and pick any website and type in a bogus ID name it errors or stops as soon as the site is loaded. which means it never found the ID and did not wait the 60 seconds. if it was waiting, it should not error until the 60 seconds is up. – dalearyous Feb 25 '14 at 14:21
  • I'm sorry it is not working out for you, but I do doubt that the WebDriverWait class itself is not working properly. I very rarely need to use the Python bindings, so it is hard for me to be of any more help, but look into the API docs, also look into this thread http://stackoverflow.com/questions/11539930/webdriver-wait-for-one-of-a-multiple-elements-to-appear – Erki M. Feb 25 '14 at 17:32
  • I get the error `AttributeError: type object 'By' has no attribute 'Id'` – Robert Johnstone Mar 18 '20 at 10:02
0

After trying Erki M.'s response I went here to learn more about waiting in Selenium:

http://selenium-python.readthedocs.org/en/latest/waits.html

The article mentions another alternative that is nice and simple:

from selenium import webdriver

browser = webdriver.Firefox() 
browser.implicitly_wait(30)
browser.get("http://www.somewebsite.com")

From the time you specify an implicit wait time onward, the browser will be patient and poll the site to see if it can find the element you are looking for. This method avoids the need for extra imports and also the necessity of specifying what element you are waiting for ahead of time.

One thing that I should mention is that sometimes I still fall back to time.sleep(1), for example, after keying in an input. Implicitly_wait will give time for an element to appear, but it will not cause commands to wait long enough for the UI to be responsive.

Doug Bradshaw
  • 1,452
  • 1
  • 16
  • 20