切换的概念
Selenium 启动浏览器时,“焦点”在打开的首个窗口/标签页。但是,操作过程中,可能会需要切换“焦点”。主要包括以下几种切换场景:
- 页面弹出新窗口/标签页,需要操作新窗口/标签页中的内容
- 页面包括“子页面”(iframe),需要在子页面中查找
- 页面是框架集结构,需要在框架中查找
- 页面弹出对话框——包括:警告框(Alert)、确认框(Confirm)、提示框(Prompt)等——需要操作对话框
切换到新窗口/标签页
Selenium 不区分窗口和标签页(下文统一称“窗口”),要切换到新打开的窗口,使用如下代码:
1 | driver.switch_to.window(name_or_handle) |
【Java 版】
1 driver.switchTo().window(nameOrHandle);
需要注意的是,参数有两种选择,可以是窗口的名称,也可以是窗口的句柄。
窗口的名称
怎样知道窗口的名称呢?
通常,这种窗口是通过链接打开的,页面源码可能类似这样:
1 | <a href="somewhere.html" target="windowName">Click here to open a new window</a> |
其中 <a>
的 target
属性值就是点击链接后打开窗口的名称。
获取窗口句柄
如果不是上述打开场景,就不能获知窗口名称,所以需要通过句柄来切换。获取句柄的代码如下:
1 | # 当前焦点所在窗口句柄 |
【Java 版】
1
2 driver.getWindowHandle();
driver.getWindowHandles();
句柄从字面上是不具有“可读性”的,所以不可能通过句柄值确定与窗口的对应关系。
通常的做法是,获取当前窗口句柄,然后从所有窗口句柄中排除当前窗口句柄,再确定要切换到的窗口句柄。
【最佳实践】
应尽量保持最多有两个窗口,以方便区分。
如果确实需要打开多个窗口,则需要每打开一个窗口就使用“排除法”获取其句柄并记录下来。
切换不会自动发生
页面操作导致在新窗口中打开新页面时,Selenium 的焦点不会自动切换,即使从浏览器行为看新窗口已获得焦点。
所以,要记得先切换焦点到新窗口,才能在新窗口进行操作,否则操作将仍在原窗口进行。对应的,使用 driver.close()
关闭新窗口时,要记得将焦点切换回原窗口,否则将试图在一个已销毁的窗口中进行操作——当然会报错啦。
新建窗口
上面所述的新窗口都是通过页面操作打开的。Selenium 4 新增了一个纯代码新建“空白”窗口的方法:
1 | # 打开新标签页并切换到新标签页 |
【Java 版】
1
2
3
4 // 新建并转到新标签页
WebDriver window2 = driver.switchTo().newWindow(WindowType.TAB);
// 新建并转到新窗口
WebDriver window1 = driver.switchTo().newWindow(WindowType.WINDOW);
注意,焦点会切换到新建窗口。
切换到“子文档”
涉及“子文档”的结构包括“框架集(frameset)”和“内嵌框架(iframe)”,前者已弃用很少见了,后者仍经常使用,因此将以 iframe 为例说明。
向“内”切换到“子文档”
切换到“子文档”的方法为:
1 | driver.switch_to.frame(frame_reference) |
注意参数可以有三种类型:
- 查找到的 iframe 元素
- iframe 元素的 id 或 name
- iframe 元素的索引(来自
window.frames
)
【Java 版】
由于 Java 是强类型语言,所以对应方法有三个重载形式。
1
2
3 driver.switchTo().frame(element); // WebElement
driver.switchTo().frame(index); // int
driver.switchTo().frame(nameOrId); // String
向“外”切换回“父文档”
切换进了“子文档”后,要想返回,使用如下方法:
1 | driver.switch_to.parent_frame() |
【Java 版】
1 driver.switchTo().parentFrame();
返回“主文档”
从“子文档”返回默认“主文档”的方法为:
1 | driver.switch_to.default_content() |
【Java 版】
1 driver.switchTo().defaultContent();
该方法使得可以不关注文档的结构,即使嵌套很深,也不需要一层层向外切换,直接一步返回顶层主文档。
处理对话框
说实话,目前网页开发大多基于各种 UI 库,弹出对话框通常都不用原生的了。操作原生对话框的方法如下:
1 | # 点击“确认”按钮 |
【共享的方法】
注意,Selenium 没有区别对待三种对话框:警告框(Alert)、确认框(Confirm)、提示框(Prompt)。
accept()
可以操作三种对话框的“确认”按钮text
可以获取三种对话框的文本消息dismiss()
可以操作确认框和提示框的“取消”按钮(警告框没有“取消”按钮)send_keys(keysToSend)
只能向提示框输入内容(因为只有提示框有输入框)【Java 版】
1
2
3
4 driver.switchTo().alert().accept();
driver.switchTo().alert().dismiss();
driver.switchTo().alert().getText();
driver.switchTo().alert().sendKeys(keysToSend);