1 Selenium原理和安装

1.1 原理

Selenium 是一套 Web网站 的程序自动化操作 解决方案。

通过它,我们可以写出自动化程序,像人一样在浏览器里操作web界面。 比如点击界面按钮,在文本框中输入文字 等操作。

我们写的自动化程序需要使用客户端库。我们程序的自动化请求都是通过这个库里面的编程接口发送给浏览器。比如,我们要模拟用户点击界面按钮, 自动化程序里面就应该调用客户端库相应的函数, 就会发送点击元素 的请求给 下方的浏览器驱动。 然后,浏览器驱动再转发这个请求给浏览器。这个自动化程序发送给浏览器驱动的请求是HTTP请求。

浏览器驱动也是一个独立的程序,是由浏览器厂商提供的, 不同的浏览器需要不同的浏览器驱动。 比如 Chrome浏览器和火狐浏览器有各自不同的驱动程序。浏览器驱动接收到我们的自动化程序发送的界面操作请求后,会转发请求给浏览器, 让浏览器去执行对应的自动化操作。浏览器执行完操作后,会将自动化的结果返回给浏览器驱动, 浏览器驱动再通过HTTP响应的消息返回给我们的自动化程序的客户端库。自动化程序的客户端库接收到响应后,将结果转化为数据对象 返回给我们的代码。

1.2 安装

这里我们需要安装客户端库和浏览器驱动

Python客户端安装如下:

    pip install selenium

    Chrome浏览器驱动:Chrome for Testing availability (googlechromelabs.github.io)

    Edge浏览器驱动:Microsoft Edge WebDriver | Microsoft Edge Developer

    测试:

    • 01
    • 02
    • 03
    • 04
    • 05
    • 06
    • 07
    • 08
    • 09
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    from selenium import webdriver from selenium.webdriver.chrome.service import Service ​ ​ def run():    # 创建 WebDriver 对象,指明使用Edge浏览器驱动    wd = webdriver.Edge(service=Service(r'./driver/msedgedriver.exe')) ​    # 调用WebDriver 对象的get方法 可以让浏览器打开指定网址    wd.get('https://www.baidu.com') ​    # 程序运行完会自动关闭浏览器,就是很多人说的闪退    # 这里加入等待用户输入,防止闪退    input('等待回车键结束程序') ​ ​ if __name__ == '__main__':    run()

    2 选择元素

    对于百度搜索页面,如果我们想自动化输入一些内容 ,怎么做呢?这就是在网页中,操控界面元素。web界面自动化,要操控元素,首先需要 选择 界面元素 ,或者说 定位 界面元素

    我们可以使用浏览器自带的工具来选择元素

    2.1 根据ID选择元素

    比如上图的输入框,它有一个属性叫id,为kw,根据规范, 如果元素有id属性 ,这个id 必须是当前html中唯一的,所以如果元素有id, 根据id选择元素是最简单高效的方式。

    • 01
    • 02
    • 03
    • 04
    • 05
    • 06
    • 07
    • 08
    • 09
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    def run():    # 创建 WebDriver 对象,指明使用Edge浏览器驱动    wd = webdriver.Edge(service=Service(r'./driver/msedgedriver.exe')) ​    # 调用WebDriver 对象的get方法 可以让浏览器打开指定网址    wd.get('https://www.baidu.com') ​    # 程序运行完会自动关闭浏览器,就是很多人说的闪退    # 这里加入等待用户输入,防止闪退    keyword = input('输入你要查询的内容:') ​    # 根据id选择元素,返回的就是该元素对应的WebElement对象    element = wd.find_element(By.ID, 'kw') ​    # 通过该 WebElement对象,就可以对页面元素进行操作了    # 比如输入字符串到 这个 输入框里    element.send_keys(keyword) ​    input('输入任意键退出...')

    其中wd 赋值的是 WebDriver 类型的对象,我们可以通过这个对象来操控浏览器,比如 打开网址、选择界面元素等。

      wd = webdriver.Edge()

      WebDriver 对象 的方法 find_element 就会发起一个请求通过 浏览器驱动 转发给浏览器,告诉它,需要选择一个id为kw的元素。浏览器找到id为kw的元素后,将结果通过浏览器驱动返回给 自动化程序, 所以find_element 方法会 返回一个WebElement类型的对象。这个WebElement 对象可以看成是对应 页面元素 的遥控器。我们通过这个WebElement对象,就可以 操控 对应的界面元素。

      比如我们执行的send_keys方法就可以在对应的元素中输入字符串,调用这个对象的click方法就可以点击 该元素。

      如果根据 传入的ID,找不到这样的元素,find_element 方法就会抛出selenium.common.exceptions.NoSuchElementException 异常

      2.2 根据class属性和tag名选择元素

      web自动化的难点和重点之一,就是如何 选择 我们想要操作的web页面元素。除了根据元素的id ,我们还可以根据元素的 class 属性选择元素。还是刚刚的例子,输入框的class为s_ipt,那我们就可以写成:

      • 01
      # 根据class选择元素,返回的就是该元素对应的WebElement对象 element = wd.find_element(By.CLASS_NAME, 's_ipt')

      find_element是取返回符合条件的 第一个 元素,如果有多个符合条件的元素,就得用

        wd.find_elements(By.CLASS_NAME, 's_ipt')

        这个方法返回的是WebElement对象列表。

        class只能选其中一个比如上面的mnavc-font-normal,而不能mnav c-font-normal c-color-t

        类似的,我们可以通过指定 参数为 By.TAG_NAME ,选择所有的tag名为 div的元素,如下所示

          elements = wd.find_elements(By.TAG_NAME, 'div')

          使用 find_elements 选择的是符合条件的 所有 元素, 如果没有符合条件的元素, 返回空列表

          使用 find_element 选择的是符合条件的 第一个 元素, 如果没有符合条件的元素, 抛出 NoSuchElementException 异常

          2.3 通过WebElement对象选择元素

          不仅 WebDriver对象有 选择元素 的方法, WebElement对象 也有选择元素的方法。WebElement对象 也可以调用 find_elementsfind_element 之类的方法

          WebDriver 对象 选择元素的范围是 整个 web页面, 而WebElement 对象 选择元素的范围是 该元素的内部。

          • 01
          • 02
          • 03
          • 04
          element = wd.find_element(By.ID, 's-top-left') elements = element.find_elements(By.CLASS_NAME, 'mnav')for e in elements:    print(e.text)

          2.4 等待界面元素出现

          在我们进行网页操作的时候, 有的元素内容不是可以立即出现的, 可能会等待一段时间。比如我们打开百度,我们点击搜索后, 浏览器需要把这个搜索请求发送给服务器, 服务器进行处理后,再把搜索结果返回给我们。

          假如我们想要拿搜索结果的话,就可能报如下异常

            selenium.common.exceptions.NoSuchElementException: Message: no such element: Unable to locate element: XXX

            NoSuchElementException 的意思就是在当前的网页上找不到该元素。

            假如点击搜索后, 用sleep 来 等待几秒钟,等服务器返回结果后,再去选择元素,基本是可以的,不会再报错了,但是这样的方法 有个很大的问题,就是:设置等待多长时间合适呢?

            Selenium提供了一个更合理的解决方案,是这样的:

            • 当发现元素没有找到的时候, 并不立即返回 找不到元素的错误。

            • 周期性(每隔半秒钟)重新寻找该元素,直到该元素找到

            • 或者超出指定最大等待时长,这时才抛出异常(如果是 find_elements 之类的方法, 则是返回空列表)。

            Selenium 的 Webdriver对象有个方法叫 implicitly_wait ,可以称之为 隐式等待 ,或者 全局等待 。该方法接受一个参数, 用来指定最大等待时长。

              wd.implicitly_wait(10)

              上面的意思是后续所有的 find_element 或者 find_elements 之类的方法调用 都会采用如果找不到元素, 每隔半秒钟再去界面上查看一次, 直到找到该元素, 或者过了10秒最大时长才抛出异常(如果是 find_elements 之类的方法, 则是返回空列表)

              那么是不是有了 implicitwait , 可以彻底不用sleep了呢?不是的,有的时候我们等待元素出现,仍然需要sleep。因为浏览器的变化可能还需要一段时间来响应,就有可能找不到元素或元素不对

              2.5 CSS Selector

              HTML中经常要 为 某些元素 指定 显示效果,比如 前景文字颜色是红色, 背景颜色是黑色, 字体是微软雅黑等。那么CSS必须告诉浏览器:要 选择哪些元素 , 来使用这样的显示风格。

              既然 CSS Selector 语法天生就是浏览器用来选择元素的,selenium自然就可以使用它用在自动化中,去选择要操作的元素了。只要 CSS Selector 的语法是正确的, Selenium 就可以选择到该元素。

              通过 CSS Selector 选择单个元素的方法是

                find_element(By.CSS_SELECTOR, CSS Selector参数)

                选择所有元素的方法是

                  find_elements(By.CSS_SELECTOR, CSS Selector参数)

                  2.5.1 根据tag名选择元素

                  CSS Selector 同样可以根据tag名、id 属性和 class属性 来 选择元素,

                  根据 tag名 选择元素的 CSS Selector 语法非常简单,直接写上tag名即可,

                  比如 要选择 所有的tag名为div的元素,就可以是这样

                    elements = wd.find_elements(By.CSS_SELECTOR, 'div')

                    等价于

                      elements = wd.find_elements(By.TAG_NAME, 'div')

                      2.5.2 根据id选择元素

                      根据id属性 选择元素的语法是在id号前面加上一个井号: #id值

                        element = wd.find_element(By.CSS_SELECTOR, '#s-top-left')

                        2.5.3 根据class选择元素

                        根据class属性 选择元素的语法是在 class 值 前面加上一个点: .class值

                          elements = wd.find_elements(By.CLASS_NAME, 's_ipt')

                          2.5.4 选择子元素和后代元素

                          HTML中,元素内部可以包含其他元素

                          如果 元素2元素1 的 直接子元素, CSS Selector 选择子元素的语法是这样的,最终选择的元素是元素2,中间用一个大于号,并且要求这个元素2元素1的直接子元素

                            元素1 > 元素2

                            如果 元素2元素1 的 后代元素, CSS Selector 选择后代元素的语法是中间是一个或者多个空格隔开,最终选择的元素是元素2 , 并且要求这个元素2元素1的后代元素。

                              元素1   元素2

                              2.5.5 根据属性选择

                              css 选择器支持通过任何属性来选择元素,语法是用一个方括号 []

                              比如要选择a元素,就可以使用 [href="http://news.baidu.com"]

                              当然,前面可以加上标签名的限制,比如 div[class='s_ipt'] 表示 选择所有 标签名为div,且class属性值为s_ipt的元素。

                              根据属性选择,还可以不指定属性值,比如 [href] , 表示选择 所有 具有 属性名 为href 的元素,不管它们的值是什么。

                              CSS 还可以选择 属性值 包含 某个字符串 的元素,比如, 要选择a节点,里面的href属性包含了 baidu字符串,就可以这样写

                                a[href*="baidu"]

                                还可以 选择 属性值 以某个字符串 开头 的元素,比如, 要选择a节点,里面的href属性以 http 开头 ,就可以这样写

                                  a[href^="http"]

                                  还可以 选择 属性值 以某个字符串 结尾 的元素,比如, 要选择a节点,里面的href属性以 gov.cn 结尾 ,就可以这样写

                                    a[href$="gov.cn"]

                                    如果一个元素具有多个属性

                                      <div class="misc" ctype="gun">沙漠之鹰</div>

                                      CSS 选择器 可以指定 选择的元素要 同时具有多个属性的限制,像这样 div[class=misc][ctype=gun]

                                      更多的可以参考CSS语法,这里就不做过多介绍了

                                      2.6 XPath

                                      前面我们学习了CSS 选择元素,大家可以发现非常灵活、强大。还有一种 灵活、强大 的选择元素的方式,就是使用 Xpath 表达式。

                                      xpath 语法中,整个HTML文档根节点用'/'表示,如果我们想选择的是根节点下面的html节点,则可以在搜索框输入

                                        /html

                                        如果输入下面的表达式

                                          /html/body/div

                                          这个表达式表示选择html下面的body下面的div元素。

                                          2.6.1 绝对路径选择

                                          从根节点开始的,到某个节点,每层都依次写下来,每层之间用 / 分隔的表达式,就是某元素的 绝对路径

                                          上面的xpath表达式 /html/body/div ,就是一个绝对路径的xpath表达式, 等价于 css表达式 html>body>div

                                          自动化程序要使用Xpath来选择web元素,应该调用 WebDriver对象的方法 find_element_by_xpath 或者 find_elements_by_xpath,像这样:

                                            elements = driver.find_elements(By.XPATH, "/html/body/div")

                                            2.6.2 相对路径选择

                                            有的时候,我们需要选择网页中某个元素, 不管它在什么位置

                                            比如,选择示例页面的所有标签名为 div 的元素,如果使用css表达式,直接写一个 div 就行了。

                                            那xpath怎么实现同样的功能呢? xpath需要前面加 // , 表示从当前节点往下寻找所有的后代元素,不管它在什么位置。

                                            所以xpath表达式,应该这样写: //div

                                            '//' 符号也可以继续加在后面,比如,要选择 所有的 div 元素里面的 所有的 p 元素 ,不管div 在什么位置,也不管p元素在div下面的什么位置,则可以这样写 //div//p

                                            2.6.3 通配符

                                            如果要选择所有div节点的所有直接子节点,可以使用表达式 //div/*

                                            * 是一个通配符,对应任意节点名的元素,等价于CSS选择器 div > *

                                            2.6.4 根据属性选择

                                            Xpath 可以根据属性来选择元素。

                                            根据属性来选择元素 是通过 这种格式来的 [@属性名='属性值']

                                            2.6.4.1 根据id属性选择

                                            选择 id 为 west 的元素,可以这样 //*[@id='west']

                                            2.6.4.2 根据class属性选择

                                            选择所有 select 元素中 class为 single_choice 的元素,可以这样 //select[@class='single_choice']

                                            这里的class要全写,注意和By.CLASS_NAME的区别

                                            2.6.4.3 根据其他属性

                                            同样的道理,我们也可以利用其它的属性选择

                                            比如选择 具有multiple属性的所有页面元素 ,可以这样 //*[@multiple]

                                            2.6.4.4 属性值包含字符串

                                            要选择 style属性值 包含 color 字符串的 页面元素 ,可以这样 //*[contains(@style,'color')]

                                            要选择 style属性值 以 color 字符串 开头 的 页面元素 ,可以这样 //*[starts-with(@style,'color')]

                                            目前浏览器都不支持某个字符串结尾的页面元素

                                            2.6.5 按次序选择

                                            2.6.5.1 某类型第几个子元素

                                            要选择 p类型第2个的子元素,就是

                                              //p[2]

                                              注意,选择的是 p类型第2个的子元素 , 不是 第2个子元素,并且是p类型

                                              2.6.5.2 第几个子元素

                                              也可以选择第2个子元素,不管是什么类型,采用通配符

                                                //div/*[2]

                                                2.6.5.3 某类型倒数第几个子元素

                                                • 选取p类型倒数第1个子元素

                                                  //p[last()]
                                                  • 选取p类型倒数第2个子元素

                                                    //p[last()-1]

                                                    2.6.5.4 范围选择

                                                    xpath还可以选择子元素的次序范围。

                                                    • 选取option类型第1到2个子元素

                                                      //option[position()<=2]

                                                      或者

                                                        //option[position()<3]
                                                        • 选择class属性为multi_choice的后3个子元素

                                                          //*[@class='multi_choice']/*[position()>=last()-2]

                                                          2.6.6 组选择

                                                          css有组选择,可以同时使用多个表达式,多个表达式选择的结果都是要选择的元素,xpath也有组选择, 是用竖线隔开多个表达式

                                                          比如,要选所有的option元素 和所有的 h4 元素,可以使用

                                                            //option | //h4

                                                            2.6.7 选择父节点

                                                            xpath可以选择父节点, 这是css做不到的。

                                                            某个元素的父节点用 /.. 表示

                                                            比如,要选择 id 为 china 的节点的父节点,可以这样写 //*[@id='china']/..

                                                            2.6.8 兄弟节点选择

                                                            前面学过 css选择器,要选择某个节点的后续兄弟节点,用 波浪线

                                                            • xpath也可以选择 后续 兄弟节点,用这样的语法 following-sibling::

                                                            比如,要选择 class 为 single_choice 的元素的所有后续兄弟节点 //*[@class='single_choice']/following-sibling::*

                                                            • xpath还可以选择 前面的 兄弟节点,用这样的语法 preceding-sibling::

                                                            比如,要选择 class 为 single_choice 的元素的 所有 前面的兄弟节点,这样写

                                                              //*[@class='single_choice']/preceding-sibling::*

                                                              2.6.9 使用浏览器工具

                                                              浏览器可以直接复制元素为XPath

                                                              3 操控元素

                                                              3.1 点击元素

                                                              点击元素 非常简单,就是调用元素WebElement对象的 click方法。前面我们已经学过。

                                                              • 01
                                                              • 02
                                                              • 03
                                                              • 04
                                                              • 05
                                                              • 06
                                                              # 根据id选择元素,返回的就是该元素对应的WebElement对象 element = wd.find_element(By.ID, 'kw') element.send_keys('selenium') ​ # 点击提交按钮 submit = wd.find_element(By.ID, 'su') submit.click()

                                                              这里需要注意:当我们调用 WebElement 对象的 click 方法去点击 元素的时候, 浏览器接收到自动化命令,点击的是该元素的 中心点 位置 。

                                                              3.2 输入框

                                                              3.2.1 文本输入框

                                                              输入字符串 也非常简单,就是调用元素WebElement对象的send_keys方法。前面我们也已经学过。

                                                              如果我们要 把输入框中已经有的内容清除掉,可以使用WebElement对象的clear方法

                                                              3.2.2 radio框

                                                              radio框选择选项,直接用WebElement的click方法,模拟用户点击就可以了。

                                                              比如

                                                              • 01
                                                              • 02
                                                              • 03
                                                              • 04
                                                              <div id="s_radio">  <input type="radio" name="name" value="z3">张三<br>  <input type="radio" name="name" value="l4">李四<br>  <input type="radio" name="name" value="w5" checked="checked">王五 </div>

                                                              对应的代码如下

                                                              • 01
                                                              • 02
                                                              • 03
                                                              • 04
                                                              • 05
                                                              # 获取当前选中的元素 element = wd.find_element(By.CSS_SELECTOR, '#s_radio input[name="name"]:checked') print('当前选中的是: ' + element.get_attribute('value')) ​ # 点选 张三 wd.find_element(By.CSS_SELECTOR, '#s_radio input[value="z3"]').click()

                                                              3.2.3 checkbox框

                                                              对checkbox进行选择,也是直接用 WebElement 的 click 方法,模拟用户点击选择。

                                                              需要注意的是,要选中checkbox的一个选项,必须 先获取当前该复选框的状态 ,如果该选项已经勾选了,就不能再点击。否则反而会取消选择。

                                                              比如上面,我们的思路可以是这样:

                                                              • 先把 已经选中的选项全部点击一下,确保都是未选状态

                                                              • 再点击张三

                                                              • 01
                                                              • 02
                                                              • 03
                                                              • 04
                                                              • 05
                                                              • 06
                                                              • 07
                                                              # 先把 已经选中的选项全部点击一下 elements = wd.find_elements(By.CSS_SELECTOR, '#s_checkbox input[name="name"]:checked') ​ for element in elements:    element.click() ​ # 再点击张三 wd.find_element(By.CSS_SELECTOR, "#s_checkbox input[value='z3']").click()

                                                              3.2.4 select框

                                                              radio框及checkbox框都是input元素,只是里面的type不同而已。select框 则是一个新的select标签,大家可以对照浏览器网页内容查看一下对于Select 选择框, Selenium 专门提供了一个 Select类 进行操作。

                                                              Select类 提供了如下的方法

                                                              • select_by_value:根据选项的 value属性值 ,选择元素。

                                                              比如,下面的HTML,

                                                                <option value="foo">Bar</option>

                                                                就可以根据 foo 这个值选择该选项,

                                                                  s.select_by_value('foo')
                                                                  • select_by_index:根据选项的 次序 (从0开始),选择元素

                                                                  • select_by_visible_text:根据选项的 可见文本 ,选择元素。

                                                                  • deselect_by_value:根据选项的value属性值, 去除 选中元素

                                                                  • deselect_by_index:根据选项的次序,去除 选中元素

                                                                  • deselect_by_visible_text:根据选项的可见文本,去除 选中元素

                                                                  • deselect_all:去除 选中所有元素

                                                                  3.2.5 Select多选框

                                                                  对于select多选框,要选中某几个选项,要注意去掉原来已经选中的选项。

                                                                  可以用select类的deselect_all方法,清除所有已经选中的选项,然后再根据其他方法选择要选的选项

                                                                  3.3 获取元素信息

                                                                  3.3.1 获取元素的文本内容

                                                                  通过WebElement对象的 text 属性,可以获取元素 展示在界面上的 文本内容。

                                                                  比如百度上面的几个链接,长这样的

                                                                  • 01
                                                                  • 02
                                                                  <a href="http://news.baidu.com" target="_blank" class="mnav c-font-normal c-color-t">新闻</a> <a href="https://www.hao123.com?src=from_pc" target="_blank" class="mnav c-font-normal c-color-t">hao123</a> ...

                                                                  我们就可以使用

                                                                  • 01
                                                                  • 02
                                                                  • 03
                                                                  • 04
                                                                  • 05
                                                                  • 06
                                                                  • 07
                                                                  # 根据class选择元素,返回的就是该元素对应的WebElement对象 elements = wd.find_elements(By.CLASS_NAME, 'mnav') ​ # 通过该 WebElement对象,就可以对页面元素进行操作了 # 比如输入字符串到 这个 输入框里 for element in elements:    # 输出内容    print(element.text)

                                                                  但是,有时候,元素的文本内容没有展示在界面上,或者没有完全完全展示在界面上。 这时,用WebElement对象的text属性,获取文本内容,就会有问题。

                                                                  出现这种情况,可以尝试使用 element.get_attribute('innerText') ,或者 element.get_attribute('textContent')。使用 innerText 和 textContent 的区别是,前者只显示元素可见文本内容,后者显示所有内容(包括display属性为none的部分)

                                                                  3.3.2 获取元素属性

                                                                  通过WebElement对象的 get_attribute 方法来获取元素的属性值

                                                                  比如要获取元素属性class的值,就可以使用 element.get_attribute('class')

                                                                  3.3.3 获取整个元素对应的HTML

                                                                  要获取整个元素对应的HTML文本内容,可以使用 element.get_attribute('outerHTML')

                                                                  如果,只是想获取某个元素 内部 的HTML文本内容,可以使用 element.get_attribute('innerHTML')

                                                                  3.3.4 获取输入框里面的文字

                                                                  对于input输入框的元素,要获取里面的输入文本,用text属性是不行的,这时可以使用 element.get_attribute('value')

                                                                  4 frame切换/窗口切换

                                                                  4.1 frame切换

                                                                  在html语法中,frame 元素 或者iframe元素的内部 会包含一个被嵌入的另一份html文档。在我们使用selenium打开一个网页是, 我们的操作范围缺省是当前的 html , 并不包含被嵌入的html文档里面的内容。如果我们要操作被嵌入的 html 文档 中的元素, 就必须 切换操作范围 到 被嵌入的文档中。

                                                                  使用 WebDriver 对象的 switch_to 属性,像这样就可以切换到被嵌入的文档

                                                                    wd.switch_to.frame(frame_reference)

                                                                    其中, frame_reference 可以是 frame 元素的属性 name 或者 ID 。也可以填写frame 所对应的 WebElement 对象。

                                                                    如果我们已经切换到某个iframe里面进行操作了,那么后续选择和操作界面元素 就都是在这个frame里面进行的。这时候,怎么切换回原来的主html呢?很简单,写如下代码即可

                                                                      wd.switch_to.default_content()

                                                                      4.2 窗口切换

                                                                      在网页上操作的时候,我们经常遇到,点击一个链接或者按钮,就会打开一个新窗口 。如果我们用Selenium写自动化程序在新窗口里面 打开一个新网址, 并且去自动化操作新窗口里面的元素,即使新窗口打开了, 这时候,我们的 WebDriver对象对应的 还是老窗口,自动化操作也还是在老窗口进行,

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

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

                                                                        wd.switch_to.window(handle)

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

                                                                        • 01
                                                                        • 02
                                                                        • 03
                                                                        • 04
                                                                        • 05
                                                                        • 06
                                                                        for handle in wd.window_handles:    # 先切换到该窗口    wd.switch_to.window(handle)    # 得到该窗口的标题栏字符串,判断是不是我们要操作的那个窗口    if 'Bing' in wd.title:        # 如果是,那么这时候WebDriver对象就是对应的该该窗口,正好,跳出循环,        break

                                                                        我们依次获取 wd.window_handles 里面的所有 句柄 对象, 并且调用 wd.switch_to.window(handle) 方法,切入到每个窗口,然后检查里面该窗口对象的属性(可以是标题栏,地址栏),判断是不是我们要操作的那个窗口,如果是,就跳出循环。

                                                                        如果我们在新窗口 操作结束后, 还要回到原来的窗口,该怎么办?我们可以仍然使用上面的方法,依次切入窗口,然后根据 标题栏 之类的属性值判断。但还有更省事的方法。

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

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

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

                                                                        • 01
                                                                        #通过前面保存的老窗口的句柄,自己切换到老窗口 wd.switch_to.window(mainWindow)

                                                                        5 其他操作

                                                                        5.1 ActionChains

                                                                        之前我们对web元素做的操作主要是:选择元素,然后点击元素或者输入字符串。但还有鼠标右键点击、双击、移动鼠标到某个元素、鼠标拖拽等,这些操作,可以通过 Selenium 提供的 ActionChains 类来实现。

                                                                        就比如百度右上角的天气,我们可以滑动到这个地方

                                                                        • 01
                                                                        • 02
                                                                        setting = wd.find_element(By.CLASS_NAME, 'weather-icon') # 鼠标移动到 元素上 ac.move_to_element(setting).perform()

                                                                        5.2 执行javascript

                                                                        我们可以直接让浏览器运行一段javascript代码,并且得到返回值,如下

                                                                        • 01
                                                                        • 02
                                                                        • 03
                                                                        • 04
                                                                        • 05
                                                                        • 06
                                                                        • 07
                                                                        • 08
                                                                        • 09
                                                                        • 10
                                                                        • 11
                                                                        # 直接执行 javascript,里面可以直接用return返回我们需要的数据 nextPageButtonDisabled = driver.execute_script(    '''   ele = document.querySelector('.soupager > button:last-of-type');   return ele.getAttribute('disabled')   ''') ​ # 返回的数据转化为Python中的数据对象进行后续处理 if nextPageButtonDisabled == 'disabled': # 是最后一页    return True else: # 不是最后一页    return False

                                                                        有时,自动化的网页内容很长,或者很宽,超过一屏显示,如果我们要点击的元素不在窗口可见区内,新版本的selenium协议, 浏览器发现要操作(比如点击操作)的元素,不在可见区内,往往会操作失败,

                                                                        这时,可以调用 execute_script 直接执行js代码,让该元素出现在窗口可见区正中

                                                                          driver.execute_script("arguments[0].scrollIntoView({block:'center',inline:'center'})", job)

                                                                          其中 arguments[0] 就指代了后面的第一个参数 job 对应的js对象,js对象的 scrollIntoView 方法,就是让元素滚动到可见部分

                                                                          有些网站上面的元素, 我们鼠标放在上面,会动态弹出一些内容。如果我们要用selenium自动化,就需要F12查看这个元素的特征。但是当我们的鼠标从这个栏目移开, 这个栏目就整个消失了, 就没法查看其对应的 HTML。

                                                                          在 开发者工具栏 console 里面执行如下js代码

                                                                            setTimeout(function(){debugger}, 5000)

                                                                            执行该命令会 浏览器会进入debug状态。 debug状态有个特性, 界面被冻住, 不管我们怎么点击界面都不会触发事件。

                                                                            5.3 弹出对话框

                                                                            有的时候,我们经常会在操作界面的时候,出现一些弹出的对话框。弹出的对话框有三种类型,分别是 Alert(警告信息)、confirm(确认信息)和prompt(提示输入)

                                                                            5.3.1 Alert

                                                                            Alert 弹出框,目的就是显示通知信息,只需用户看完信息后,点击OK(确定) 就可以了。那么,自动化的时候,代码怎么模拟用户点击OK按钮呢?

                                                                            selenium提供如下方法进行操作

                                                                              driver.switch_to.alert.accept()

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

                                                                                driver.switch_to.alert.text

                                                                                5.3.2 Confirm

                                                                                Confirm弹出框,主要是让用户确认是否要进行某个操作。比如:当管理员在网站上选择删除某个账号时,就可能会弹出 Confirm弹出框, 要求确认是否确定要删除。Confirm弹出框有两个选择供用户选择,分别是OK和Cancel, 分别代表确定和取消操作。

                                                                                selenium提供如下方法进行操作

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

                                                                                  driver.switch_to.alert.accept()

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

                                                                                    driver.switch_to.alert.dismiss()

                                                                                    5.3.3 Prompt

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

                                                                                    可以调用如下方法

                                                                                      driver.switch_to.alert.send_keys()

                                                                                      5.4 窗口大小

                                                                                      有时间我们需要获取窗口的属性和相应的信息,并对窗口进行控制

                                                                                      • 获取窗口大小

                                                                                        driver.get_window_size()
                                                                                        • 改变窗口大小

                                                                                          driver.set_window_size(x, y)

                                                                                          5.5 获取当前窗口标题和URL

                                                                                          浏览网页的时候,我们的窗口标题是不断变化的,可以使用WebDriver的title属性来获取当前窗口的标题栏字符串。

                                                                                            driver.title

                                                                                            获取URL可以用

                                                                                              driver.current_url

                                                                                              5.6 截屏

                                                                                              有的时候,我们需要把浏览器屏幕内容保存为图片文件。比如,做自动化测试时,一个测试用例检查点发现错误,我们可以截屏为文件,以便测试结束时进行人工核查。可以使用 WebDriver 的 get_screenshot_as_file方法来截屏并保存为图片。

                                                                                              • 01
                                                                                              • 02
                                                                                              • 03
                                                                                              • 04
                                                                                              • 05
                                                                                              • 06
                                                                                              • 07
                                                                                              • 08
                                                                                              • 09
                                                                                              from selenium import  webdriver ​ driver = webdriver.Chrome() driver.implicitly_wait(5) ​ # 打开网站 driver.get('https://www.baidu.com/') ​ # 截屏保存为图片文件 driver.get_screenshot_as_file('1.png')

                                                                                              5.7 手机模式

                                                                                              我们可以通过 options 参数,指定以手机模式打开chrome浏览器

                                                                                              • 01
                                                                                              • 02
                                                                                              • 03
                                                                                              • 04
                                                                                              ops = webdriver.EdgeOptions() mobile_emulation = {"deviceName": "Nexus 5"} ops.add_experimental_option('mobileEmulation', mobile_emulation) wd = webdriver.Edge(service=Service(r'./driver/msedgedriver.exe'), options=ops) wd.get('https://www.damai.cn/')

                                                                                              5.8 上传文件

                                                                                              有时候,网站操作需要上传文件。通常,网站页面上传文件的功能,是通过 type 属性 为 file 的 HTML input 元素实现的。

                                                                                              使用selenium自动化上传文件,我们只需要定位到该input元素,然后通过 send_keys 方法传入要上传的文件路径即可。如下所示:

                                                                                              • 01
                                                                                              • 02
                                                                                              • 03
                                                                                              • 04
                                                                                              # 先定位到上传文件的 input 元素 ele = wd.find_element(By.CSS_SELECTOR, 'input[type=file]') ​ # 再调用 WebElement 对象的 send_keys 方法 ele.send_keys(r'D:\g02.png')

                                                                                              但是,有的网页上传,是没有file类型的input元素的。如果是Windows上的自动化,可以采用 Windows 平台专用的方法:

                                                                                              安装pypiwin32

                                                                                                pip install pypiwin32

                                                                                                然后:

                                                                                                • 01
                                                                                                • 02
                                                                                                • 03
                                                                                                • 04
                                                                                                • 05
                                                                                                • 06
                                                                                                • 07
                                                                                                • 08
                                                                                                • 09
                                                                                                • 10
                                                                                                • 11
                                                                                                • 12
                                                                                                # 找到点击上传的元素,点击 driver.find_element(By.CSS_SELECTOR, '.dropzone').click() ​ sleep(2) # 等待上传选择文件对话框打开# 直接发送键盘消息给 当前应用程序, # 前提是浏览器必须是当前应用 import win32com.client shell = win32com.client.Dispatch("WScript.Shell") ​ # 输入文件路径,最后的'\n',表示回车确定,也可能时 '\r' 或者 '\r\n' shell.Sendkeys(r"D:\g02.png" + '\n') sleep(1)