窗⼝的切换

在网页上操作的时候,我们经常遇到,点击一个链接 或者 按钮,就会打开一个 新窗口

保存sample1.html到本地

后面代码针对此简单页面写小案例

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

        <title>selenium窗口的切换测试</title>

    <script>
            function appendEle(info) {
                var node = document.createElement("LI");
                var textnode = document.createTextNode(info);
                node.appendChild(textnode);
                document.getElementById("add").appendChild(node);
            }
            function clickbutton() {
                appendEle("你点击了外部按钮");
            }
            </script>
            <style class="mpa-style-fix ImageGatherer">.FotorFrame{position:fixed!important}</style>
            <style class="mpa-style-fix SideFunctionPanel">.weui-desktop-online-faq__wrp{top:304px!important;bottom:unset!important}.weui-desktop-online-faq__wrp .weui-desktop-online-faq__switch{width:38px!important}</style>
        </head>

    <body>

        <a href="http://www.bing.com/" target="_blank">访问bing网站</a>

        <div><button id="outerbutton" onclick="clickbutton()">功能按钮</button></div>
        <br>
        <div id="add"><li>你点击了外部按钮</li></div>


    </body>
</html>

在打开的网页中,点击 链接 “访问bing网站” , 就会弹出一个新窗口,访问bing网址。

如果我们用Selenium写自动化程序 在新窗口里面 打开一个新网址, 并且去自动化操作新窗口里面的元素,会有什么问题呢?

问题就在于,即使新窗口打开了, 这时候,我们的 WebDriver对象对应的 还是老窗口,自动化操作也还是在老窗口进行,

我们可以运行如下代码验证一下

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2022/1/6 20:50
# @Author  : shisuiyi
# @File    : windows_switch.py
# @Software: win10 Tensorflow1.13.1 python3.9
from selenium import webdriver


wd = webdriver.Chrome()
wd.implicitly_wait(10)

wd.get('D:\Lemon\py45\webatuo\selenium切换测试.html')

# 点击打开新窗口的链接
link = wd.find_element("xpath", "//a")
link.click()

# wd.title属性是当前窗口的标题栏 文本
print(wd.title)

运行完程序后,最后一行 打印当前窗口的标题栏 文本, 输出内容是

selenium窗口的切换测试

说明, 我们的 WebDriver 对象指向的还是老窗口,否则的话,运行结果就应该新窗口的标题栏 “微软Bing搜索”


如果我们要到新的窗口里面操作,该怎么做呢?

可以使用Webdriver对象的switch_to属性的 window方法,如下所示:

wd.switch_to.window(handle)

其中,参数handle需要传入什么呢?

WebDriver对象有window_handles 属性,这是一个列表对象, 里面包括了当前浏览器里面所有的窗口句柄

所谓句柄,大家可以想象成对应网页窗口的一个ID,

那么我们就可以通过 类似下面的代码,

# 获取当前所有的窗口
print(wd.window_handles)
for handle in wd.window_handles:
    # 先切换到该窗口
    wd.switch_to.window(handle)
    # 得到该窗口的标题栏字符串,判断是不是我们要操作的那个窗口
    if '必应' in wd.title:
        # 如果是,那么这时候WebDriver对象就是对应的该该窗口,正好,跳出循环,
        break
print(wd.title)
wd.quit()

输出

['CDwindow-42AA7086CC3137F8598456F402B9CB88', 'CDwindow-3C19EE398970DB8D348DB5AA0AA786CD']
必应

上面代码的用意就是:

我们依次获取 wd.window_handles 里面的所有 句柄 对象, 并且调用 wd.switch_to.window(handle) 方法,切入到每个窗口,

然后检查里面该窗口对象的属性(可以是标题栏,地址栏),判断是不是我们要操作的那个窗口,如果是,就跳出循环。


同样的,如果我们在新窗口 操作结束后, 还要回到原来的窗口,该怎么办?

我们可以仍然使用上面的方法,依次切入窗口,然后根据 标题栏 之类的属性值判断。

还有更省事的方法。

因为我们一开始就在 原来的窗口里面,我们知道 进入新窗口操作完后,还要回来,可以事先 保存该老窗口的 句柄,使用如下方法

# mainWindow变量保存当前窗口的句柄
mainWindow = wd.current_window_handle

wd.switch_to.window(wd.window_handles[-1])
print(wd.current_url)
# 通过前面保存的老窗口的句柄,自己切换到老窗口
wd.switch_to.window(mainWindow)
print(wd.title)

输出

https://cn.bing.com/
selenium窗口的切换测试

切换到新窗口操作完后,就可以直接像下面这样,将driver对应的对象返回到原来的窗口

iframe切换

iframe操作有哪些?

  • 切换到iframe

  • 切换回主页面

  • 切换回上一级

切换到frame

请大家点击这里,打开这个链接

如果我们要 选择 下图方框中 所有的 蔬菜,使用css选择,怎么写表达式?

当然,要先查看到它们的html元素特征

大家可能会照旧写出如下代码:

from selenium import webdriver
from selenium.webdriver.common.by import By

wd = webdriver.Chrome()

wd.get('http://cdn1.python3.vip/files/selenium/sample2.html')

# 根据 class name 选择元素,返回的是 一个列表
elements = wd.find_elements(By.CLASS_NAME, 'plant')

for element in elements:
    print(element.text)

运行一下,你就会发现,运行结果打印内容为空白,说明没有选择到 class 属性值为 plant 的元素。

为什么呢?

因为仔细看,你可以发现, 这些元素是在一个叫 iframe的 元素中的。
image-1650432471565

这个 iframe 元素非常的特殊, 在html语法中,frame 元素 或者iframe元素的内部 会包含一个 被嵌入的 另一份html文档。

在我们使用selenium打开一个网页是, 我们的操作范围 缺省是当前的 html , 并不包含被嵌入的html文档里面的内容。

如果我们要 操作 被嵌入的 html 文档 中的元素, 就必须 切换操作范围 到 被嵌入的文档中。

怎么切换呢?

使用WebDriver 对象的 switch_to 属性,像这样

wd.switch_to.frame(frame_reference)

其中, frame_reference 可以是 frame 元素的属性 name 或者 ID 。

比如这里,就可以填写 iframe元素的id ‘frame1’ 或者 name属性值 ‘innerFrame’。

像这样

wd.switch_to.frame('frame1')

或者

wd.switch_to.frame('innerFrame')

也可以填写frame 所对应的 WebElement 对象。

我们可以根据frame的元素位置或者属性特性,使用find系列的方法,选择到该元素,得到对应的WebElement对象

比如,这里就可以写

wd.switch_to.frame(wd.find_element(By.TAG_NAME, "iframe"))

然后,就可以进行后续操作frame里面的元素了。

上面的例子的正确代码如下

from selenium import webdriver
from selenium.webdriver.common.by import By

wd = webdriver.Chrome()

wd.get('http://cdn1.python3.vip/files/selenium/sample2.html')


# 先根据name属性值 'innerFrame',切换到iframe中
wd.switch_to.frame('innerFrame')

# 根据 class name 选择元素,返回的是 一个列表
elements = wd.find_elements(By.CLASS_NAME, 'plant')

for element in elements:
    print(element.text)
    
# 输出
土豆
洋葱
白菜

如果我们已经切换到某个iframe里面进行操作了,那么后续选择和操作界面元素 就都是在这个frame里面进行的。

这时候,如果我们又需要操作 主html(我们把最外部的html称之为主html) 里面的元素了呢?

怎么切换回原来的主html呢?

很简单,写如下代码即可

wd.switch_to.default_content()

例如,在上面 代码 操作完 frame里面的元素后, 需要 点击 主html 里面的按钮,就可以这样写

from selenium import webdriver
from selenium.webdriver.common.by import By

wd = webdriver.Chrome()

wd.get('http://cdn1.python3.vip/files/selenium/sample2.html')


# 先根据name属性值 'innerFrame',切换到iframe中
wd.switch_to.frame('innerFrame')

# 根据 class name 选择元素,返回的是 一个列表
elements = wd.find_elements(By.CLASS_NAME, 'plant')

for element in elements:
    print(element.text)

# 切换回 最外部的 HTML 中
wd.switch_to.default_content()

# 然后再 选择操作 外部的 HTML 中 的元素
wd.find_element_by_id('outerbutton').click()

wd.quit()

嵌套frame的操作

有时候我们会遇到嵌套的frame,如下:

<html>
    <iframe id="frame1">
        <iframe id="frame2" / >
    </iframe>
</html>
  1. 从主文档切到frame2,一层层切进去

driver.switch_to.frame("frame1")
driver.switch_to.frame("frame2")
  1. 从frame2再切回frame1,这里selenium给我们提供了一个方法能够从子frame切回到父frame,而不用我们切回主文档再切进来。

driver.switch_to.parent_frame() # 如果当前已是主文档,则无效果

有了parent_frame()这个相当于后退的方法,我们可以随意切换不同的frame,随意的跳来跳去了。

所以只要善用以下三个方法,遇到frame分分钟搞定:

driver.switch_to.frame(reference)
driver.switch_to.parent_frame()
driver.switch_to.default_content()

弹出对话框

有的时候,我们经常会在操作界面的时候,出现一些弹出的对话框。

首先,将下面html代码保存到一个文件中

后续的代码小案例都是访问此html的

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>警告框处理</title>
    <script type="text/javascript">
        function duihua() {
            alert("这个窗口是对话框!");
            print('你点击了确认');
        }

        function queren() {
            var se = confirm("确认框!");
            if (se == true) {
                print('你点击了确认1');
            } else {
                print('你点击了取消1');
            }
        }

        function tishi() {
            var se = prompt("请输入你想学习的Python课程", "Python基础")
            if (se == null) {
                print('取消操作');
            } else {
                print('你想学习'+ se);
            }
        }

        function print(text) {
            var dom = document.createElement('div')
            dom.innerText = text
            document.getElementsByTagName('body')[0].appendChild(dom)
        }
    </script>
</head>
<body>
<input id="bu1" type="button" onclick="duihua()" value="alert"/>
<br>
<br>
<input id="bu2" type="button" onclick="queren()" value="confirm"/>
<br>
<br>
<input id="bu3" type="button" onclick="tishi()" value="prompt"/>
</body>
</html>

分别点击界面的3个按钮,你可以发现:

弹出的对话框有三种类型,分别是 Alert(警告信息)、confirm(确认信息)和prompt(提示输入)

Alert

Alert 弹出框,目的就是显示通知信息,只需用户看完信息后,点击 OK(确定) 就可以了。

那么,自动化的时候,代码怎么模拟用户点击 OK 按钮呢?

selenium提供如下方法进行操作

driver.switch_to.alert.accept()

注意:如果我们不去点击它,页面的其它元素是不能操作的。

如果程序要获取弹出对话框中的信息内容, 可以通过 如下代码

driver.switch_to.alert.text

示例代码如下

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2022/1/6 21:57
# @Author  : shisuiyi
# @File    : 警告框.py
# @Software: win10 Tensorflow1.13.1 python3.9
from selenium import webdriver
from selenium.webdriver.common.by import By

driver = webdriver.Chrome()
driver.implicitly_wait(5)
driver.get('D:\Lemon\py45\webatuo\警告框.html')

# --- alert ---
driver.find_element(By.ID, 'bu1').click()

# 打印 弹出框 提示信息
print(driver.switch_to.alert.text)

# 点击 OK 按钮
driver.switch_to.alert.accept()

Confirm

Confirm弹出框,主要是让用户确认是否要进行某个操作。

比如:当管理员在网站上选择删除某个账号时,就可能会弹出 Confirm弹出框, 要求确认是否确定要删除。

Confirm弹出框 有两个选择供用户选择,分别是 OK 和 Cancel, 分别代表 确定 和 取消 操作。

那么,自动化的时候,代码怎么模拟用户点击 OK 或者 Cancel 按钮呢?

selenium提供如下方法进行操作

如果我们想点击 OK 按钮, 还是用刚才的 accept方法,如下

driver.switch_to.alert.accept()

如果我们想点击 Cancel 按钮, 可以用 dismiss方法,如下

driver.switch_to.alert.dismiss()

示例代码如下

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2022/1/6 21:57
# @Author  : shisuiyi
# @File    : 警告框.py
# @Software: win10 Tensorflow1.13.1 python3.9
from selenium import webdriver
from selenium.webdriver.common.by import By

driver = webdriver.Chrome()
driver.implicitly_wait(5)

driver.get('D:\Lemon\py45\webatuo\警告框.html')

# --- confirm ---
driver.find_element(By.ID, 'bu2').click()

# 打印 弹出框 提示信息
print(driver.switch_to.alert.text)

# 点击 OK 按钮
driver.switch_to.alert.accept()

driver.find_element(By.ID, 'bu2').click()

# 点击 取消 按钮
driver.switch_to.alert.dismiss()

Prompt

出现 Prompt 弹出框 是需要用户输入一些信息,提交上去。

可以调用如下方法

driver.switch_to.alert.send_keys()

示例代码如下

from selenium import webdriver
from selenium.webdriver.common.by import By

driver = webdriver.Chrome()
driver.implicitly_wait(5)
driver.get('D:\Lemon\py45\webatuo\警告框.html')


# --- prompt ---
driver.find_element(By.ID, 'bu3').click()

# 获取 alert 对象
alert = driver.switch_to.alert

# 打印 弹出框 提示信息
print(alert.text)

# 输入信息,并且点击 OK 按钮 提交
alert.send_keys('web自动化 - selenium')
alert.accept()

# 点击 Cancel 按钮 取消
driver.find_element(By.ID, 'bu3').click()
alert = driver.switch_to.alert
alert.dismiss()

注意 : 有些弹窗并非浏览器的alert 窗口,而是html元素,这种对话框,只需要通过之前介绍的选择器选中并进行相应的操作就可以了。

切换到shadow-root

在做web自动化的时候,一些元素在shadow-root的节点下,使得selenium中无法通过xpath来定位

这里的 ​​#shadow-root​​​ 所包含的内容其实就是所谓的 ​​shadow-dom​​ 。

​​shadow-dom​​ 其实是浏览器的一种能力,它允许在浏览器渲染文档(document)的时候向其中的 Dom 结构中插入一棵 DOM 元素子树,但是特殊的是,这棵子树(shadow-dom)并不在主 DOM 树中。

操作shadow-root内的元素

您必须使用shadowRoot.querySelector(),并且可以使用以下定位器策略:

实现步骤:

  1. 先定位到shadow-root的宿主节点

  2. 在切换到shadow-root中

  3. 在选择shadow-root下的标签:

案例如下

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2023/3/11 8:31
# @Author  : shisuiyi
# @File    : 13_#shadow-root.py
# @Software: win10 Tensorflow1.13.1 python3.9


from selenium import webdriver  # 引入webdriver
from webdriver_manager.chrome import ChromeDriverManager
import time  # time模块

browser = webdriver.Chrome(ChromeDriverManager().install())
url = "https://www.immowelt.de/immobilienpreise"
print('url is ' + url)
browser.maximize_window()
browser.get(url)  # 发送url请求
time.sleep(5)  # 等待时间
element = browser.execute_script("""return document.querySelector('#usercentrics-root').shadowRoot.querySelector(
"button[data-testid='uc-accept-all-button']")""")
element.click()
"""
要单击OK,您必须使用shadowRoot.querySelector(),并且可以使用以下定位器策略:
实现步骤:

1.先定位到shadow-root的宿主节点:document.querySelector('#usercentrics-root')
2.在切换到shadow-root中:shadowRoot.querySelector(button[data-testid='uc-accept-all-button'])
3.在选择shadow-root下的标签:
"""

time.sleep(4)
browser.quit()  # 关闭所有与当前driver相关的窗口
# browser.close() #关闭当前窗口

shadow-root.png