I'm trying to interact with a button on a page with the following structure. The button of interest is within a div in the body of an iframe, which is inside the main body. I've already read all the StackOverflow questions about how to switch to an iframe - as you can see below I have no issue with that. What I have an issue with is that regardless of that advice I am unable to interact with the iframe I switched to. The goal is to click a button inside a specific iframe.
EDIT: The issue seems to be related to the selenium-wire library. Here is the full code.
from seleniumwire import webdriver
from selenium.webdriver.chrome.options import Options
from webdriver_manager.chrome import ChromeDriverManager
import time
url = 'https://sahkovertailu.fi/'
options = {
'suppress_connection_errors': False,
'connection_timeout': None# Show full tracebacks for any connection errors
}
chrome_options = Options()
chrome_options.add_argument("--start-maximized")
chrome_options.add_argument('--auto-open-devtools-for-tabs')
chrome_options.add_argument('--log-level=2')
driver = webdriver.Chrome(ChromeDriverManager().install(), seleniumwire_options=options, chrome_options=chrome_options)
if driver.requests is not None:
del driver.requests
driver.get(url)
iframe = driver.find_element_by_xpath("//iframe[contains(@src, 'privacy')]")
driver.switch_to.frame(iframe)
iframeUniqueId = iframe.get_attribute('cd_frame_id_')
print(iframeUniqueId)
time.sleep(2)
button = driver.find_element_by_xpath("//button[contains(., 'OK')]")
button.click()
Here is an example of the page layout
<!doctype html>
<html ng-app="someApp" xmlns="http://www.w3.org/1999/html">
<head>
<script>a lot of scripts</script>
</head>
<body class="unwantedBody">
<iframe> some iframes</iframe>
<div> different divs </div>
<main>
some content
<iframe> multiple iframes on different nested levels </iframe>
</main>
<div> more divs </div>
<script> more scripts </script>
<div id='interesting div'>
<iframe src="uniqueString">
<!doctype html>
#document
<html>
<body>
<div>
<button id='bestButton'>
'Nice title'
</button>
</div>
</body>
</html>
</iframe>
</div>
</body>
</html>
Using Jupyter Notebook I've been able to locate the iframe and switch to it. The problem is not related to trying to interact with the iFrame too fast, because I control the speed. I've tried using Expected conditions and waiting until the iframe can be switched to, but it is irrelevant to my problem.
driver.switch_to.default_content # Making sure no other frame is selected
iframe = driver.find_element_by_xpath("//iframe[contains(@src, 'uniqueString')]")
driver.switch_to.frame(iframe)
print(iframe.get_attribute('id'))
The above code prints out "interesting div", so it successfully finds the div where the iframe is and apparently selects the div? When I switch to the iframe, the webpage changes and an unique GUID is given to the iframe:
<iframe src="uniqueString" cd_frame_id_ = "uniqueGuidThatAlwaysChanges"> ... </iframe>
I am able to access the unique id with
iframe.get_attribute('cd_frame_id_')
Then I try to parse the iframe like this:
bestButton = driver.find_element_by_xpath("//button[@id = 'bestButton']")
bestButton.click()
This gives the error:
Message: element not interactable
If I replace the above driver. with iframe.find_element_by_xpath... I get the following error:
Message: no such element: Unable to locate element: {"method":"xpath","selector":"//button[@id="bestButton"]"}
I also tried to interact with the body within the iframe after switching to it with the above switch_to.frame(iframe), so in this example driver is already at the iframe:
document = driver.find_element_by_xpath('//html/body')
info = document.get_attribute('class')
print(info)
This prints out
unwantedBody
So somehow the driver has not switched to the iFrame I specified and instead is still stuck on the main HTML. When loading the webpage on chrome I can find the button I want with just this XPath //button[contains(@id='bestButton')] but in Selenium it doesn't work, because it is split by the #document within the iframe.
What am I missing? If it helps, the iFrame I am interested in is actually a modal window about cookie consent, which I am trying to get rid of to interact with the site.
@qoob, you didn't specified where exactly is the button located on the website and Finnish to English translation was not that good by google. So I assumed you are talking about the first page itself as the DOM structure of the iframe looked familiar to what you have asked in the question. If the button in the below image is the actual point of interest then below code works.
from selenium import webdriver
import time
driver = webdriver.Chrome(r"C:\chromedriver.exe")
driver.maximize_window()
driver.get("https://sahkovertailu.fi/")
driver.switch_to.frame(driver.find_element_by_xpath("//iframe[@id='sp_message_iframe_204788']"))
time.sleep(2) # remove after testing
driver.find_element_by_xpath("//div[@class='message type-modal']//descendant::button[2]").click()
For reference on how to use XPath axes please refer this: https://www.lambdatest.com/blog/complete-guide-for-using-xpath-in-selenium-with-examples/#testid2.8.1
Please note webdriver_manager has some issues as of now, so use of chromedriver/firefox binary is highly suggested.(https://github.com/SergeyPirogov/webdriver_manager/issues/119)
Let me know if that works!