Select iframe using Python + Selenium

109,890

Solution 1

What finally worked for me was:

        sel.run_script("$('#upload_file_frame').contents().find('img[alt=\"Humana\"]').click();")

Basically, don't use selenium to find the link in the iframe and click on it; use jQuery. Selenium has the capability to run an arbitrary piece of javascript apparently (this is python-selenium, I am guessing the original selenium command is runScript or something), and once I can use jQuery I can do something like this: Selecting a form which is in an iframe using jQuery

Solution 2

This worked for me with Python (v. 2.7), webdriver & Selenium when testing with iframes and trying to insert data within an iframe:

self.driver = webdriver.Firefox()

## Give time for iframe to load ##
time.sleep(3)
## You have to switch to the iframe like so: ##
driver.switch_to.frame(driver.find_element_by_tag_name("iframe"))
## Insert text via xpath ##
elem = driver.find_element_by_xpath("/html/body/p")
elem.send_keys("Lorem Ipsum")
## Switch back to the "default content" (that is, out of the iframes) ##
driver.switch_to.default_content()

Solution 3

If iframe is dynamic node, it's also possible to wait for iframe appearence explicitly and then switch to it using ExpectedConditions:

from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait as wait

driver = webdriver.Chrome()
driver.get(URL)
wait(driver, 10).until(EC.frame_to_be_available_and_switch_to_it("iframe_name_or_id"))

If iframe doesn't have @id or @name it can be found as common WebElement using driver.find_element_by_xpath(), driver.find_element_by_tag_name(), etc..:

wait(driver, 10).until(EC.frame_to_be_available_and_switch_to_it(driver.find_element_by_xpath("//iframe[@class='iframe_class']")))

To switch back from iframe:

driver.switch_to.default_content()

Solution 4

You don't need to use JavascriptExecutor. All you needed to do was switch into the frame and then switch back out, like so:

// do stuff on main window
driver.switch_to.frame(frame_reference)
// then do stuff in the frame
driver.switch_to.default_content()
// then do stuff on main window again

As long as you are careful with this, you will never have a problem. The only time I always use a JavascriptExecutor is to get window focus since I think using Javascript is more reliable in that case.

Solution 5

To shift Selenium's focus within the <iframe> you can use either of the following Locator Strategies:

  • Using ID:

    driver.switch_to.frame("upload_file_frame") 
    
  • Using CSS_SELECTOR:

    driver.switch_to.frame(driver.find_element(By.CSS_SELECTOR, "iframe#upload_file_frame"))
    
  • Using XPATH:

    driver.switch_to.frame(driver.find_element(By.XPATH, "//iframe[@id='upload_file_frame']"))
    

Ideally, you have to induce WebDriverWait for the desired frame to be available and switch to it and you can use either of the following Locator Strategies:

  • Using ID:

    WebDriverWait(driver, 20).until(EC.frame_to_be_available_and_switch_to_it((By.ID,"upload_file_frame")))  
    
  • Using CSS_SELECTOR:

    WebDriverWait(driver, 20).until(EC.frame_to_be_available_and_switch_to_it((By.CSS_SELECTOR,"iframe#upload_file_frame")))
    
  • Using XPATH:

    WebDriverWait(driver, 20).until(EC.frame_to_be_available_and_switch_to_it((By.XPATH,"//iframe[@id='upload_file_frame']")))
    
  • 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
    

Reference

You can find a couple of relevant discussions in:

Share:
109,890
rossdavidh
Author by

rossdavidh

At various times a user of python (esp. django, tornado, and numpy/scipy), javascript (esp. jquery, d3), Cognos, and statistical analysis packages RS1,RS/Discover, RS/Explore. Master's in EE thesis was on software simulation of neural networks. Also did a few years in a mainframe gig. Nowadays I do contract work in ETL and client or server side web programming.

Updated on July 08, 2022

Comments

  • rossdavidh
    rossdavidh almost 2 years

    So, I was absolutely baffled as to how to do this in Selenium, and couldn't find the answer anywhere, so I'm sharing my experience.

    I was trying to select an iframe and having no luck (or not repeatably anyway). The HTML is:

    <iframe id="upload_file_frame" width="100%" height="465px" frameborder="0" framemargin="0" name="upload_file_frame" src="/blah/import/">
    <html>
        <body>
            <div class="import_devices">
                <div class="import_type">
                    <a class="secondary_button" href="/blah/blah/?source=blah">
                        <div class="import_choice_image">
                            <img alt="blah" src="/public/images/blah/import/blah.png">
                        </div>
                        <div class="import_choice_text">Blah Blah</div>
                    </a>
                </div>
            </div>
        </body>
    </html>
    

    The Python code (using the selenium library) was trying to find this iframe using this:

        @timed(650)
    def test_pedometer(self):
        sel = self.selenium
        ...
        time.sleep(10)
        for i in range(5):
            try:
                if sel.select_frame("css=#upload_file_frame"): break
            except: pass
            time.sleep(10)
        else: self.fail("Cannot find upload_file_frame, the iframe for the device upload image buttons")
    

    Repeated fails with every combination of Selenium commands I could find.

    The occasional success would not be reproducible, so perhaps it was some sort of race condition or something? Never did find the right way to get it in selenium proper.

  • Ross Patterson
    Ross Patterson almost 13 years
    You'd be better off using Selenium's getEval command (probably "get_eval" in the Python bindings) instead of its runScript command. getEval won't return until the JavaScript code completes, whereas runScript simple inserts a <SCRIPT> element and returns, resulting in the browser running the JavaScript code after the call returns.
  • rossdavidh
    rossdavidh almost 13 years
    Yeah, selectFrame didn't work for me. Doesn't mean it won't work for anyone, but I found it to actually select the frame about one time in 5.
  • GOstrowsky
    GOstrowsky over 9 years
    Somehow the WebDriver finds the elements into the iframe when searching with xpath. I've tried finding elements by CSS, ID but it always gave me an error of not existing element (timeout failure actually because I used the waitForElement command). I didn't think searching element by xpath would make the difference, but I fortunately gave it a try when I was running out of ideas. Thanks a lot!
  • Wesley Smith
    Wesley Smith over 7 years
    what exactly is sel.run_script? I can find no mention of this function in the docs or with google. While this may have worked for you, its a poor answer because you dont explain what self.selenium is or how to get run_script That said, driver.execute_script worked for me
  • rossdavidh
    rossdavidh over 7 years
    Well, to be honest, it was five years ago, I have no idea by now. Glad that driver.execute_script worked for you, though, good that you posted that here for the future readers.
  • Tom Bush
    Tom Bush over 4 years
    Especially handy when working with iframes that seem to have names and URLs that are being generated dynamically. Really useful, thank you
  • rain01
    rain01 about 4 years
    This is probably the best solution for switching to frame but frame_to_be_available_and_switch_to_it expects either a tuple (By.ID,"frameID") or WebElement, not plain string.