Select iframe using Python + Selenium

PythonSeleniumXpathIframeCss Selectors

Python Problem Overview


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.

Python Solutions


Solution 1 - Python

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 2 - Python

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 3 - Python

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 4 - Python

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 - Python

Selenium's selectFrame command accepts all the standard locators like css=, but it also has a an extra set of locators that work specifically with FRAME and IFRAME elements.

As the doc says:

> selectFrame ( locator ) Selects a frame within the current window. > (You may invoke this command multiple times to select nested frames.) > To select the parent frame, use "relative=parent" as a locator; to > select the top frame, use "relative=top". You can also select a frame > by its 0-based index number; select the first frame with "index=0", or > the third frame with "index=2". > > You may also use a DOM expression to identify the frame you want > directly, like this: dom=frames["main"].frames["subframe"] > > Arguments: locator - an element locator identifying a frame or iframe

In general, you'll have better luck using the specialized locators, especially if you establish the right context first (e.g., select_frame("relative=top"); select_frame("id=upload_file_frame");).

Solution 6 - Python

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:

Solution 7 - Python

you can use this simple code to look for the iframe using xpath

sample use set_iframe("/html/body/div[2]/table/")

def set_iframe(xpath):
    print('set_iframe')
    driver.switch_to.default_content()
    seq = driver.find_elements_by_tag_name('iframe')
    for x in range(0, len(seq)):
        driver.switch_to.default_content()
        print ("iframe-"+str(x))
        try:
            driver.switch_to.frame(int(x))
            driver.find_element_by_xpath(xpath)
            return str(x)
        except:
            continue

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionrossdavidhView Question on Stackoverflow
Solution 1 - PythonMichael WhitmanView Answer on Stackoverflow
Solution 2 - PythonAnderssonView Answer on Stackoverflow
Solution 3 - PythonrossdavidhView Answer on Stackoverflow
Solution 4 - PythondjangofanView Answer on Stackoverflow
Solution 5 - PythonRoss PattersonView Answer on Stackoverflow
Solution 6 - Pythonundetected SeleniumView Answer on Stackoverflow
Solution 7 - PythoneunickView Answer on Stackoverflow