2

I've a dynamically generated iframe element (input type) and need to store it as a variable. The iframe looks like:

<iframe id="iframe39993" class="green tea" src="...foo"> 
 #document
 <!doctype html>
  <html>
   <head>...</head>
    <body style>
      <div id="layout" class=container>
        <div class="row">
          <input class="required" type="number">
         </div>
       </div>
     </body>
    </html>

I'm using Selenium/Webdriver to do this:

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

driver = webdriver.Chrome()
driver.switch_to.frame(driver.find_element_by_class_name('green tea'))
content = WebDriverWait(driver, 10).until(EC.visibility_of_element_located((By.XPATH, "//* [@id='layout']/div[1]/input")))
print(content.text)
driver.switch_to.default_content()
driver.quit()

For some reason I get only a newline (empty string) as output. I'm pretty sure, that the xpath should be correct, but can't print (or store) the iframe element. The iframe element contains already user input data, which I need to access. Someone any ideas?

  • what's the error? – antont Jan 04 '20 at 05:34
  • Updated the question. Had a typo error, after re-checking the code I get just a newline without any errors. –  Jan 04 '20 at 05:38
  • I can also copy the iframe content with ActionChains, but only paste them inside the browser. Need the input as a variable, unfortunately. –  Jan 04 '20 at 05:42

2 Answers2

2

Your locator .find_element_by_class_name('green tea') will not work because .find_element_by_class_name() takes only a single class name and you have passed it two, "green" and "tea". If you want to locate the element using both classes, a better choice would be a CSS selector, .green.tea.

The other issue is that INPUT elements don't contain text, they contain values. To get the value from an INPUT, you will need to use .get_attribute('value') instead of .text.

Updating your code based on the two items above,

driver = webdriver.Chrome()
driver.switch_to.frame(driver.find_element_by_css_selector('.green.tea'))
content = WebDriverWait(driver, 10).until(EC.visibility_of_element_located((By.XPATH, "//*[@id='layout']/div[1]/input")))
print(content.get_attribute('value'))
driver.switch_to.default_content()
driver.quit()

Some CSS selector references:
W3C
Selenium Tips: CSS Selectors
Taming Advanced CSS Selectors

JeffC
  • 22,180
  • 5
  • 32
  • 55
  • 1
    Thank you so much! That totally solves my problem. I've copy+pasted the input with keystrokes over key_send and used it with clipboard to paste as value, but your way is more elegant. –  Jan 06 '20 at 04:10
0

The <input> tag which you are trying to locate:

<input class="required" type="number">

Doesn't have any text or innerHTML as such. So the line of code:

print(content.text)

will always print an empty string.

Here you can find a relevant discussion on Ways to deal with #document under iframe

undetected Selenium
  • 183,867
  • 41
  • 278
  • 352
  • 1
    Thank you, sir. That totally explains it! The iframe textbox itself isn't empty. Is there a way to access it? –  Jan 04 '20 at 07:57
  • 1
    @robscure As it is an `` tag, of coarse you can `click()` and `send_keys()` in it. – undetected Selenium Jan 04 '20 at 07:59
  • 1
    That's a nice idea, I played a bit around with that. I can copy the input of the iframe with send_keys(). But how can I store that as a variable? I need to validate it inside the python code. Thanks for your time. –  Jan 04 '20 at 08:01
  • 1
    @robscure You don't copy the `text` or `innerHTML` of an `` tag, rather you send _text_ within the element. As you are sending the _text_ yourself, you can validate it when the text (which you have sent) is processed by the Web Application and gets reflected somewhere within the webpage. – undetected Selenium Jan 04 '20 at 08:04
  • 1
    I see. I thought I could store it directly as a variable. Thank you!! –  Jan 04 '20 at 08:06