seleniumbase / SeleniumBase

📊 Python's all-in-one framework for web crawling, scraping, testing, and reporting. Supports pytest. UC Mode provides stealth. Includes many tools.
https://seleniumbase.io
MIT License
4.45k stars 908 forks source link

uc_switch_to_frame does not work for nested iframes #2841

Closed bogdanblazhkevych closed 3 weeks ago

bogdanblazhkevych commented 3 weeks ago

if you have nested iframes, for example

<html>
    <iframe title="parent">
        <iframe title="child">

        </iframe>
    </iframe>
<html>

to get to the child iframe you would need to access the parent frame first, and your code would look something like this:

driver.uc_switch_to_frame("iframe[title="parent"]", 2)
driver.uc_switch_to_frame("iframe[title="child"]", 2)

However, driver.uc_switch_to_frame uses driver.reconnect for its implementation. The "undetected" feature of uc_switch_to_frame seems to rely entirely on driver.reconnect

def uc_switch_to_frame(driver, frame, reconnect_time=None):
    from selenium.webdriver.remote.webelement import WebElement
    if isinstance(frame, WebElement):
        if not reconnect_time:
            driver.reconnect(0.1)
        else:
            driver.reconnect(reconnect_time)
        driver.switch_to.frame(frame)
    else:
        iframe = driver.locator(frame)
        if not reconnect_time:
            driver.reconnect(0.1)
        else:
            driver.reconnect(reconnect_time)
        driver.switch_to.frame(iframe)

using driver.reconnect() sets the focus back to the parent, backing out of any iframes, as mentioned here, so that when the code block reaches driver.uc_switch_to_frame("iframe[title="child"]", 2) selenium will return a NoSuchElementException because the context is lost.

Is this the intended behavior? Would it be safe to use the normal switch_to_frame after the first use of uc_switch_to_frame, or is there another way to get around this??

mdmintz commented 3 weeks ago

driver.uc_switch_to_frame(frame) is just a one-line shortcut for doing the following two lines:

driver.reconnect()
driver.switch_to_frame(frame)

And as mentioned in https://github.com/seleniumbase/SeleniumBase/discussions/2358#discussioncomment-7824538, driver.reconnect() automatically switches out of any iframes.

So in your scenario, you would just do this:

driver.reconnect()
driver.switch_to_frame(frame1)
driver.switch_to_frame(frame2)

Also, calling driver.reconnect() might not be necessary anymore if you eventually call driver.uc_click(selector) while you're inside an iframe.

bogdanblazhkevych commented 3 weeks ago

I noticed that driver.uc_click(selector) also uses driver.reconnect, which causes the same issue when dealing with multiple iframes. This means I have to wrap every uc call with non-uc switch_to_frame methods. Does this reduce the "undetectable" nature of these actions? Also, at what point in the lifecycle do websites typically detect the presence of ChromeDriver?

mdmintz commented 3 weeks ago

Once you've performed a uc_click(selector) with a long enough reconnect_time, you shouldn't need to keep hiding. By that point you should have already bypassed the CAPTCHA.