How to send text within an input field with contenteditable="true" within an iframe using Selenium and Python
I am writing a webscraping script that automatically logs into my Email account and sends a message.
I have written the code to the point where the browser has to input the message. I don't know how to access the input field correctly. I have seen that it is an iframe element. Do I have to use the switch_to_frame()
method and how can I do that? How can I switch to the iframe if there is no name attribute? Do I need the switch_to_frame()
method or can I just use the find_element_by_css_selector()
method?
This is the source code of the iframe:
Here is my code:
from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
myPassword = 'xxxxxxxxxxxxxxxx'
browser = webdriver.Firefox() # Opens Firefox webbrowser
browser.get('https://protonmail.com/') # Go to protonmail website
loginButton = WebDriverWait(browser, 10).until(EC.presence_of_element_located((By.CSS_SELECTOR, "a.btn-ghost:nth-child(1)")))
loginButton.click()
usernameElem = WebDriverWait(browser, 10).until(EC.element_to_be_clickable((By.CSS_SELECTOR, "#username")))
usernameElem.send_keys("[email protected]")
passwordElem = browser.find_element_by_css_selector("#password")
passwordElem.send_keys(myPassword)
anmeldenButton = browser.find_element_by_css_selector(".button")
anmeldenButton.click()
newMessage = WebDriverWait(browser, 10).until(EC.element_to_be_clickable((By.XPATH, "/html/body/div[1]/div[3]/div/div/div[1]/div[2]/button")))
newMessage.click()
addressElem = WebDriverWait(browser, 10).until(EC.element_to_be_clickable((By.CSS_SELECTOR, "input[id^='to-composer']")))
addressElem.send_keys('[email protected]')
subjectElem = WebDriverWait(browser, 10).until(EC.element_to_be_clickable((By.CSS_SELECTOR, "input[id^='subject-composer']")))
subjectElem.send_keys('anySubject')
messageElem = WebDriverWait(browser, 10).until(EC.element_to_be_clickable((By.CSS_SELECTOR, "#squire > div > div:nth-child(1)")))
messageElem.send_keys('message')
To access the <input>
field within the iframe so you have to:
-
Induce WebDriverWait for the desired frame to be available and switch to it.
-
Induce WebDriverWait for the desired element to be clickable.
-
You can use either of the following Locator Strategies:
-
Using CSS_SELECTOR:
WebDriverWait(driver, 20).until(EC.frame_to_be_available_and_switch_to_it((By.CSS_SELECTOR,"iframe[title='Editor']"))) WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.CSS_SELECTOR, "div#squire"))).send_keys('message')
-
Using XPATH:
WebDriverWait(driver, 20).until(EC.frame_to_be_available_and_switch_to_it((By.XPATH,"//iframe[@title='Editor']"))) WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.XPATH, "//div[@id='squire']"))).send_keys('message')
-
-
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
PS: As the
<div>
tag is having the attribute contenteditable="true" you can still send text to the element.
Reference
You can find a couple of relevant discussions in:
- Switch to an iframe through Selenium and python
- selenium.common.exceptions.NoSuchElementException: Message: no such element: Unable to locate element while trying to click Next button with selenium
- selenium in python : NoSuchElementException: Message: no such element: Unable to locate element
You have to switch to iframe with driver.switch_to.frame
method.
Like any other web element iframe element can be located by ID
, CLASS
, XPATH
, CSS_SELECTOR
etc.
Looks like here you can use this method:
WebDriverWait(driver, 10).until(EC.frame_to_be_available_and_switch_to_it((By.CSS_SELECTOR,"iframe[title='Editor']")))
Or
WebDriverWait(driver, 10).until(EC.frame_to_be_available_and_switch_to_it((By.CSS_SELECTOR,"iframe[data-testid='squire-iframe']")))
When finished working within the iframe you will have to switch back to the default content with
driver.switch_to.default_content()
you first need to switch to iframe
wait = WebDriverWait(driver, 30)
wait.until(EC.frame_to_be_available_and_switch_to_it((By.CSS_SELECTOR, "iframe[title='Editor']")))
and now here write the code to send the message body. something like this:
wait.until(EC.frame_to_be_available_and_switch_to_it((By.CSS_SELECTOR, "iframe[title='Editor']")))
wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, "div#squire"))).click()
email = wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, "div#squire div:nth-child(2)")))
email.send_keys('write the email here')
also once you are done with iframe interaction, you should switch to default content:
driver.switch_to.default_content()