Python 3.5 - Selenium - 如何处理新窗口并等到它完全加载?

Posted

技术标签:

【中文标题】Python 3.5 - Selenium - 如何处理新窗口并等到它完全加载?【英文标题】:Python 3.5 - Selenium - How to handle a new window and wait until it is fully loaded? 【发布时间】:2017-05-25 02:00:15 【问题描述】:

我正在做浏览器自动化,但我在某个时刻被阻止:有一刻,我要求浏览器点击一个按钮,然后打开一个新窗口。但有时 Internet 速度太慢,因此这个新窗口需要一些时间才能加载。我想知道如何让 Selenium 等到这个新的新窗口完全加载。

这是我的代码:

driver.switch_to.default_content()
Button = WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.ID, 'addPicturesBtn')))
Button.click()
newWindow = driver.window_handles
time.sleep(5)
newNewWindow = newWindow[1]
driver.switch_to.window(newNewWindow)
newButtonToLoad = driver.find_element_by_id('d')
newButtonToLoad.send_keys('pic.jpg')
uploadButton = WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.ID, 'uploadPics')))
uploadButton.click()
driver.switch_to.window(newWindow[0])

我不时收到此错误:

newNewWindow = newWindow[1]

IndexError: 列表索引超出范围

这让我觉得简单的 'time.sleep(5)' 不起作用。

那么,我的问题是,我如何才能等到这个新窗口完全加载后再与之交互?

谢谢

【问题讨论】:

【参考方案1】:

您可以尝试使用以下代码等待新窗口出现:

WebDriverWait(driver, 20).until(EC.number_of_windows_to_be(2))

你的代码应该是这样的

Button.click()

WebDriverWait(driver, 20).until(EC.number_of_windows_to_be(2))

newWindow = driver.window_handles
newNewWindow = newWindow[1]
driver.switch_to.window(newNewWindow)

考虑到@JimEvans 的评论,请尝试以下操作:

current = driver.window_handles[0]
Button.click()

WebDriverWait(driver, 20).until(EC.number_of_windows_to_be(2))

newWindow = [window for window in driver.window_handles if window != current][0]
driver.switch_to.window(newWindow)

【讨论】:

我要在这里指出,这段代码存在假设window_handles 返回的窗口句柄列表是有序的常见谬误。它不是。换句话说,有两个窗口句柄,window_handles[0] 可能不是第一个打开的,window_handles[1] 可能不是最后一个打开的。 @JimEvans 你能详细说明一下吗?我的意思是,这可能会导致未来的问题,那么如何解决您指出的问题? @JimEvans,请不要将使用 cmets 的 OP 与 this code 混淆 :) 提到的问题中提供的初始代码中存在缺陷,我重新使用它只是为了指出要插入我的解决方案。 . 但是,更新答案以解决window_handles的列表顺序问题 搜索了很长时间,只有这个答案特别有效,@JimEvans 的评论是救命稻草。 @Andersson,我收到了WebDriverWait(self.driver, 20).until(EC.number_of_windows_to_be(1)) AttributeError: 'bytes' object has no attribute 'number_of_windows_to_be'。有什么技巧吗?【参考方案2】:

我正在使用什么:

windows = set(driver.window_handles)
driver.execute_script("window.open()")
WebDriverWait(driver, 20).until(EC.new_window_is_opened(windows))
hwnd = list(set(driver.window_handles) - windows)[0]

如果你认为有必要,你可以为它创建一个函数或上下文管理器like this answer,但基本上只有 3 行(不计算实际打开窗口的那一行,这可能因每种情况而有很大不同),我更喜欢只是复制粘贴。

为了彻底,这里有一个功能丰富的替代方案:

@contextmanager
def wait_for_new_window(driver, timeout=10, switch=True):
    """Waits for a new window to open

    Parameters:
        driver: WebDriver.
            Selenium WebDriver object.
        timeout: int
            Maximum waiting time
        switch: bool
            If `True` (default), switches to the window.


    Exemplo:
        >>> with wait_for_new_window(driver) as hwnds:
        ...     driver.execute_script("window.open()");
        ... print("Window opened:", hwnds[0])
    """
    windows = set(driver.window_handles)
    handle = []
    yield handle
    WebDriverWait(driver, timeout).until(EC.new_window_is_opened(windows))
    hwnds = list(set(driver.window_handles) - windows)
    handle.extend(hwnds)
    if switch:
        driver.switch_to.window(hwnds[0])

【讨论】:

【参考方案3】:

等待新窗口完全加载的确切答案是:

使用来自 expected_conditionsurl_changes 和旧 URL 参数作为 "about:blank"

WebDriverWait(Driver, 15).until(EC.url_changes("about:blank"))

示例代码可能如下所示:

        ...
        # Wait till a new window opened (2nd window in this example)
        WebDriverWait(Driver, 15).until(EC.number_of_windows_to_be(2))

        # Switch to second window
        window_after = Driver.window_handles[1]
        Driver.switch_to.window(window_after)

        # Wait till **new window fully loaded**:
        WebDriverWait(Driver, 15).until(EC.url_changes("about:blank"))
        ...

【讨论】:

以上是关于Python 3.5 - Selenium - 如何处理新窗口并等到它完全加载?的主要内容,如果未能解决你的问题,请参考以下文章

python 3.5 关于sys问题总结

python+selenium 环境配置

使用 Python/PhantomJS/Selenium 滚动无限页面

你如何用 Mingw 编译 python 3.5 代码?

在新标签Selenium + Python中打开网页

虫师Selenium2+Python_2测试环境搭建