In Playwright for Python, how do I retrieve a handle for elements from within an frame (iframe)?

Viewed 2543

I have successfully used Playwright in python to get elements from a page. I now ran into to challenge of getting elements from a document embedded within an iframe. As an example, I used the w3schools page explaining the <option> element, which displays the result in an iframe. I am trying to retrieve a handle for this <option> element from the iframe.

The 'normal' way of getting the an element on the page with page.querySelector() fails to get an elementHandle, this just prints <class 'NoneType'>:

with sync_playwright() as p:
    for browser_type in [p.chromium, p.firefox, p.webkit]:
        browser = browser_type.launch(headless=False)
        page = browser.newPage()
        page.goto('https://www.w3schools.com/tags/tryit.asp?filename=tryhtml_option')
        element = page.querySelector('select')
        print(type(element))
        browser.close()

I tried explicitly getting a handle for the iframe first, but this yields the same result (<class 'NoneType'>):

with sync_playwright() as p:
    for browser_type in [p.chromium, p.firefox, p.webkit]:
        browser = browser_type.launch(headless=False)
        page = browser.newPage()
        page.goto('https://www.w3schools.com/tags/tryit.asp?filename=tryhtml_option')      
        iframe = page.querySelector('iframe')
        element = iframe.querySelector('select')
        print(type(element))
        browser.close()

How can I get content from within the iframe?

3 Answers

Turns out I was close, but to get the iframe correctly, I needed to call the contentFrame() method.

Returns the content frame for element handles referencing iframe nodes, or null otherwise

Then, querySelector() will return the respective elementHandle just fine:

with sync_playwright() as p:
    for browser_type in [p.chromium, p.firefox, p.webkit]:
        browser = browser_type.launch(headless=False)
        page = browser.newPage()
        page.goto('https://www.w3schools.com/tags/tryit.asp?filename=tryhtml_option')
        iframe = page.querySelector('iframe').contentFrame()
        element = iframe.querySelector('select')
        print(type(element))
        print(element.innerHTML())
        browser.close()

successfully prints

<class 'playwright.sync_api.ElementHandle'>

  <option value="volvo">Volvo</option>
  <option value="saab">Saab</option>
  <option value="opel">Opel</option>
  <option value="audi">Audi</option>

Note: if there are multiple iframes, you can just use an attribute when retrieving the handle. To get the iframe by its id in the above example, e.g. use

iframe = page.querySelector('iframe[id=\"iframeResult\"]').contentFrame()

querySelector will return an ElementHandle. In that case, it will be the iFrame but as a page element. If you want to get the frame from that element you need to call contentFrame, and from there work on that frame.

iframe = page.querySelector('#iframeResult').contentFrame()
element = iframe.querySelector('select')
print(type(element))

Version 1.22

page.frame_locator("iframe#iframeResult").locator("select#cars")

https://playwright.dev/python/docs/api/class-elementhandle#element-handle-content-frame

The use of ElementHandle is discouraged, use Locator objects and web-first assertions instead.

https://playwright.dev/python/docs/api/class-framelocator

Code example using frame_locator

from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch(headless=False)
    page = browser.new_page()
    page.goto(
        "https://www.w3schools.com/tags/tryit.asp?filename=tryhtml_option", timeout=0
    )
    page.wait_for_selector("iframe#iframeResult")

    select = page.frame_locator("iframe#iframeResult").locator("select#cars")

    print(select.inner_html())
    # <option value="volvo">Volvo</option>
    # <option value="saab">Saab</option>
    # <option value="opel">Opel</option>
    # <option value="audi">Audi</option>

    print(select.inner_text())
    # Volvo
    # Saab
    # Opel
    # Audi

    browser.close()
Related