Python+Selenium Web自动化

Python+Selenium Web自动化

selenium

  • selenium是一个Web应用的自动化框架,通过它,我们可以写出自动化程序,模拟用户在浏览器操作Web界面
  • selenium提供了多种语言编写的selenium客户端库,包括java、python、js、ruby等,方便不同编写语言的开发者使用

浏览器驱动

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

selenium的安装

  • python语言安装selenium :
    • pip install selenium
  • 安装浏览器驱动
    • chrome驱动对应浏览器版本下载 :Chrome浏览器驱动下载
    • Windows平台的直接把下载的zip文件解压得到chromedriver.exe

selenium简单示例

from selenium import webdriver

# 创建Webdriver对象,指明使用chrome浏览器驱动

my_driver = webdriver.Chrome(executable_path="d:/chromedriver.exe")

# 调用WebDriver 对象的get方法,可以让浏览器打开指定网址
my_driver.get("https://www.baidu.com")

选择元素的基本方法

根据元素id属性选择元素

  • 根据元素的id属性选择元素 : driver.find_element_by_id("标签id")

  • send_keys()输入框输入文本方法

  • click() : 点击按钮方法

    示例 :

    from selenium import webdriver
    
    # 创建Webdriver对象,指明使用chrome浏览器驱动
    
    my_driver = webdriver.Chrome(executable_path="d:/chromedriver.exe")
    
    # 调用WebDriver 对象的get方法,可以让浏览器打开指定网址
    my_driver.get("https://www.baidu.com")
    
    # 根据id选择定位元素,返回的就是该元素对应的WebElement对象
    element = my_driver.find_element_by_id("kw")  # 定位到百度搜索框
    
    # 通过该WebElement对象,就可以对该页面元素进行操作了
    # 输入字符串到输入框中
    element.send_keys("Python学习")
    
    # 定位百度搜索按钮并点击
    my_driver.find_element_by_id("su").click()
    

根据class属性、tag名选择元素

  • 根据元素的class属性选择元素 : driver.find_element_by_class_name("标签class_name")

  • driver.find_element_by_class_name 返回的是一个对象

  • driver.find_elements_by_class_name 返回的是一个对象列表

  • driver.find_element_by_class_name.text 返回标签text属性的值

  • 网页源代码

        <body>
            
            <div class="plant"><span>土豆</span></div>
            <div class="plant"><span>洋葱</span></div>
            <div class="plant"><span>白菜</span></div>
    
            <div class="animal"><span>狮子</span></div>
            <div class="animal"><span>老虎</span></div>
            <div class="animal"><span>山羊</span></div>
    
        </body>
    

    示例代码:

    from selenium import webdriver
    
    my_driver = webdriver.Chrome(executable_path="d:/chromedriver.exe")
    my_driver.get("http://cdn1.python3.vip/files/selenium/sample1.html")
    element = my_driver.find_element_by_class_name("plant")
    # find_element_by_class_name 定位的class有相同时,只返回第一个元素
    print(element.text) # 土豆
    
    # driver.find_elements_by_class_name 返回的是一个对象列表
    elements = my_driver.find_elements_by_class_name("plant")
    for i in elements:
        print(i.text)
    '''
    土豆
    洋葱
    白菜
    '''
    
  • 根据 tag 名 选择元素

    • find_element_by_tag_name("标签名"): 只返回第一个标签元素对象

    • find_elements_by_tag_name("标签名") : 作为列表返回所有定位的标签对象

      from selenium import webdriver
      
      my_driver = webdriver.Chrome(executable_path="d:/chromedriver.exe")
      my_driver.get("http://cdn1.python3.vip/files/selenium/sample1.html")
      element = my_driver.find_element_by_tag_name("div")
      # find_element_by_class_name 定位的class有相同时,只返回第一个元素
      print(element.text) # 土豆
      
      # river.find_elements_by_tag_name 返回的是一个对象列表
      elements = my_driver.find_elements_by_tag_name("div")
      for i in elements:
          print(i.text)
      '''
      土豆
      洋葱
      ...
      备案号 苏ICP备88885574号
      '''
      

**find_element 和 find_elements 的区别 : **

使用find_element选择符合条件的第一个元素,如果没有符合条件的元素,抛出NoSuchElementException 异常
使用find_elements选择符合条件的所有元素,如果没有符合条件的元素,返回空列表

Selenium设置等待超时时间

  • driver.implicity_wait(5)

    • 当发现元素没有找到的时候, 并不 立即返回 找不到元素的错误,而是周期性(每隔半秒钟)重新寻找该元素,直到该元素找到
    • 如果超出指定的等待时长,这是就会抛出异常
    • 并且后续的find_element和find_elements之类的方法调用都会采用driver.implicity_wait(5)
    from selenium import webdriver
    
    my_driver = webdriver.Chrome(executable_path="d:/chromedriver.exe")
    
    # 设置最大等待时长为 10秒
    my_driver.implicitly_wait(10)
    
    my_driver.get('https://www.baidu.com')
    
    element = my_driver.find_element_by_id('kw')
    
    element.send_keys('python学习')
    
    element = my_driver.find_element_by_id('1')
    
    print (element.text)
    

操控元素的基本方法

  • 点击元素
    • driver.find_element_by_id("xx").click()
  • 文本框输入字符串
    • 清除输入框内容 : driver.find_element_by_id("xx").clear()
    • 输入内容 : driver.find_element_by_id("xx").send_keys("文本内容")
  • 获取元素信息
    • driver.find_element_by_id("xx").text
  • 获取源文本信息2
    • 通过element对象的text属性,可以获取元素展示在界面上的文本内容
    • 但是有时元素元素文本内容没有展示在界面上,或者没有完全展示在界面上时,用element对象的text属性就会出现问题
    • 这时可以尝试使用element.get_attribute("innerText") 或者element.get_attribute("textContent")
  • 获取元素属性
    • driver.find_element_by_id("xx").get_attribute("class") : 获取class元素的类名
  • 退出浏览器
    • driver.quit()
  • 获取整个元素对应的HTML : element.get_attribute('outerHTML')
  • 获取某个元素内部的HTML文本内容 : element.get_attribute('innerHTML')
  • 获取输入框内的文字 : element_getattribute("value")

css表达式 - 上

  • Css Selector选择器

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

    • 根据tag名选择元素
      • elements = driver.find_elements_by_css_selector("div")
      • 等同于 : elements = driver.find_elements_by_tag_name("div")
    • 根据id属性选择元素 : 语法是在id前加#号 #id
      • element = driver.find_element_by_css_selector("#kw")
    • 根据class属性选择所以符合的元素 : 语法是在class值前加点 .class值
      • elements = driver.find_elements_by_css_selector(".button")
      • 等同于 : elements = driver.find_elements_by_class_name("button")
  • 选择子元素和后代元素

    <div id='container'>
        
        <div id='layer1'>
            <div id='inner11'>
                <span>内层11</span>
            </div>
            <div id='inner12'>
                <span>内层12</span>
            </div>
        </div>
    
        <div id='layer2'>
            <div id='inner21'>
                <span>内层21</span>
            </div>
        </div>
        
    </div>
    
    • 上面HTML代码中,id 为 container 的div元素 包含了 id 为 layer1 和 layer2 的两个div元素。

    • 这种包含是直接包含, 中间没有其他的层次的元素了。 所以 id 为 layer1layer2 的两个div元素 是 id 为 container 的div元素 的 直接子元素

    • id 为 layer2 的div元素 又包含了 id 为 inner21 这个div元素。 这种包含关系也是 直接子元素 关系

    • 而对于 id 为 container 的div元素来说, id 为 inner11inner12inner22 的元素 和 两个 span类型的元素 都不是它的直接子元素,但是他们是在container的内部,可以称之为后代元素

    • 后代元素也包括了直接子元素,比如id为layer1layer2的两个div元素,可以是id为container直接子元素,同时也是后代子元素

    • 如果元素2元素1直接子元素,css selector选择子元素的语法是这样的 :

      • 元素1> 元素2 : 最终选择的是元素2,且要是元素2是元素1的直接子元素
    • 如果元素2元素 1的后代元素,css selector 选后代元素的语法是这样的 :

      • 元素1 元素2 : 最终选择的是元素2,且要求元素2是元素1的后代元素
  • 根据属性选择

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

    • 比如 <a href="http://www.miitbeian.gov.cn">苏ICP备88885574号</a>里面根据 href选择,可以用css 选择器

      • from selenium import webdriver
        
        wd = webdriver.Chrome(r'e:\chromedriver.exe')
        
        wd.get('http://cdn1.python3.vip/files/selenium/sample1.html')
        
        # 根据属性选择元素
        element = wd.find_element_by_css_selector('[href="http://www.miitbeian.gov.cn"]')
        
        # 打印出元素对应的html
        print(element.get_attribute('outerHTML'))
        
      • 可以加上标签名的限制,比如 div[class='button'] :表示选择所以标签为div,且class属性值是button的

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

      • css可以选择属性值包含某个字符串的元素

        • 要选择a节点,里面的href属性包含了miui字符串,就可以这样写
          • a[href*="miui"]
      • css可以选择属性值以某个字符串开头的元素

        • 以http开头 : a[href^="http"]
      • css可以选择属性值以某个字符串结尾的元素

        • .com结尾 : a[href$=".com"]
      • 如果一个元素具有多个属性

        • <div class="tianmao" ctype="shop">天猫购物街</div>
        • 指定选择的要素同时具有多个属性的限制 : div[class=tianmao][ctype=shop]

css表达式-下

选择语法联合使用

<div id='bottom'>
    <div class='footer1'>
        <span class='copyright'>版权</span>
        <span class='date'>发布日期:2018-03-03</span>
    </div>
    <div class='footer2'>
        <span>备案号
            <a href="http://www.miitbeian.gov.cn">苏ICP备88885574号</a>
        </span>
    </div>        
</div>      
  • 我们要选择 网页 html 中的元素 <span class='copyright'>版权</span>
    • CSS selector 表达式 可以这样写: div.footer1 > span.copyright
      • 就是选择一个class 属性值为 copyright 的 span 节点, 并且要求其 必须是 class 属性值为 footer1 的 div节点 的子节点
    • 可以更简单一点 : .footer1 > .copyright
      • 就是选择一个class 属性值为copyright 的节点(不限类型), 并且要求其 必须是 class 属性值为 footer1 的节点的 子节点

组选择

如果我们要 同时选择所有class 为 plant class 为 animal 的元素。怎么办?

这种情况,css选择器可以 使用 逗号 ,称之为 组选择 ,像这样

.plant , .animal

再比如,我们要同时选择所有tag名为div的元素 id为BYHY的元素,就可以像这样写

div,#BYHY

对应的selenium代码如下

elements = wd.find_elements_by_css_selector('div,#BYHY')
for element in elements:
    print(element.text)

我们再看一个例子

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>白月黑羽测试网页1a</title>
    </head>
    <body>          
       <div id='t1'>
           <h3> 唐诗 </h3>
           <span>李白</span>
           <p>静夜思</p>
           <span>杜甫</span>
           <p>春夜喜雨</p>              
       </div>         
       <div id='t2'>
           <h3> 宋词 </h3>
           <span>辛弃疾</span>
           <p>北固亭怀古</p>   
       </div>             
    </body>
</html>

我们要选择所有 唐诗里面的作者和诗名, 也就是选择所有 id 为 t1 里面的 span 和 p 元素

我们是不是应该这样写呢?

#t1 > span,p

不行哦,这样写的意思是 选择所有 id 为 t1 里面的 span所有的 p 元素

只能这样写

#t1 > span , #t1 > p

按次序选择子节点

对应的html如下,关键信息如下

    <body>  
       <div id='t1'>
           <h3> 唐诗 </h3>
           <span>李白</span>
           <p>静夜思</p>
           <span>杜甫</span>
           <p>春夜喜雨</p>              
       </div>      
        
       <div id='t2'>
           <h3> 宋词 </h3>
           <span>苏轼</span>
           <p>赤壁怀古</p>
           <p>明月几时有</p>
           <p>江城子·乙卯正月二十日夜记梦</p>
           <p>蝶恋花·春景</p>
           <span>辛弃疾</span>
           <p>京口北固亭怀古</p>
           <p>青玉案·元夕</p>
           <p>西江月·夜行黄沙道中</p>
       </div>             

    </body>
父元素的第n个子节点

我们可以指定选择的元素 是父元素的第几个子节点

使用 nth-child

比如,我们要选择 唐诗 和宋词 的第一个 作者,

也就是说 选择的是 第2个子元素,并且是span类型

所以这样可以这样写 span:nth-child(2)

如果你不加节点类型限制,直接这样写 :nth-child(2)

就是选择所有位置为第2个的所有元素,不管是什么类型

父元素的倒数第n个子节点

也可以反过来, 选择的是父元素的 倒数第几个子节点 ,使用 nth-last-child

比如:

p:nth-last-child(1)

就是选择第倒数第1个子元素,并且是p元素

父元素的第几个某类型的子节点

我们可以指定选择的元素 是父元素的第几个 某类型的 子节点

使用 nth-of-type

比如,

我们要选择 唐诗 和宋词 的第一个 作者,

可以像上面那样思考:选择的是 第2个子元素,并且是span类型

所以这样可以这样写 span:nth-child(2)

还可以这样思考,选择的是 第1个span类型 的子元素

所以也可以这样写 span:nth-of-type(1)

父元素的倒数第几个某类型的子节点

当然也可以反过来, 选择父元素的 倒数第几个某类型 的子节点

使用 nth-last-of-type

像这样

p:nth-last-of-type(2)
奇数节点和偶数节点

如果要选择的是父元素的 偶数节点,使用 nth-child(even)

比如

p:nth-child(even)

如果要选择的是父元素的 奇数节点,使用 nth-child(odd)

p:nth-child(odd)

如果要选择的是父元素的 某类型偶数节点,使用 nth-of-type(even)

如果要选择的是父元素的 某类型奇数节点,使用 nth-of-type(odd)

相邻兄弟节点选择

上面的例子里面,我们要选择 唐诗 和宋词 的第一个 作者

还有一种思考方法,就是选择 h3 后面紧跟着的兄弟节点 span。

这就是一种 相邻兄弟 关系,可以这样写 h3 + span

表示元素 紧跟关系的 是 加号

后续所有兄弟节点选择

如果要选择是 选择 h3 后面所有的兄弟节点 span,可以这样写 h3 ~ span

frame切换/窗口切换

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

当然,要先查看到它们的html元素特征
在这里插入图片描述
如果按照之前的编写方式结果会一片空白,说明没有选择到class属性值为plant的元素,因为这些元素是在一个叫iframe的元素中的。这个 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

wd = webdriver.Chrome(r'd:\webdrivers\chromedriver.exe')

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

wd = webdriver.Chrome(r'd:\webdrivers\chromedriver.exe')

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()

切换到新的窗口

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

如果我们用Selenium写自动化程序 在新窗口里面 打开一个新网址, 并且去自动化操作新窗口里面的元素,会有什么问题呢?问题就在于,即使新窗口打开了, 这时候,我们的 WebDriver对象对应的 还是老窗口,自动化操作也还是在老窗口进行,

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

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

wd.switch_to.window(handle)

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

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

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

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) 方法,切入到每个窗口,然后检查里面该窗口对象的属性(可以是标题栏,地址栏),判断是不是我们要操作的那个窗口,如果是,就跳出循环。
  • 怎样切换到原来的窗口

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

    • 更省事的方法。

      # mainWindow变量保存当前窗口的句柄
      mainWindow = wd.current_window_handle
      #通过前面保存的老窗口的句柄,自己切换到老窗口
      wd.switch_to.window(mainWindow)
      

选择框

常见的选择框包括: radio单选框、checkbox多选框、select下拉框

radio单选框

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

比如, 我们要在下面的html中:

  • 先打印当前选中的老师名字
  • 再选择 小雷老师
<div id="s_radio">
  <input type="radio" name="teacher" value="小江老师">小江老师<br>
  <input type="radio" name="teacher" value="小雷老师">小雷老师<br>
  <input type="radio" name="teacher" value="小凯老师" checked="checked">小凯老师
</div>

对应的代码如下

# 获取当前选中的元素
element = wd.find_element_by_css_selector(
  '#s_radio input[checked=checked]')
print('当前选中的是: ' + element.get_attribute('value'))

# 点选 小雷老师
wd.find_element_by_css_selector(
  '#s_radio input[value="小雷老师"]').click()

checkbox多选框

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

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

比如, 我们要在下面的html中:选中 小雷老师

<div id="s_checkbox">
  <input type="checkbox" name="teacher" value="小江老师">小江老师<br>
  <input type="checkbox" name="teacher" value="小雷老师">小雷老师<br>
  <input type="checkbox" name="teacher" value="小凯老师" checked="checked">小凯老师
</div>

我们的思路可以是这样:

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

示例代码

# 先把 已经选中的选项全部点击一下
elements = wd.find_elements_by_css_selector(
  '#s_checkbox input[checked="checked"]')

for element in elements:
    element.click()

# 再点击 小雷老师
wd.find_element_by_css_selector(
  "#s_checkbox input[value='小雷老师']").click()

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

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

比如,下面的HTML,

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

就可以根据 Bar 这个内容,选择该选项

s.select_by_visible_text('Bar')
  • deselect_by_value

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

  • deselect_by_index

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

  • deselect_by_visible_text

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

  • deselect_all

去除 选中所有元素

Select单选框

对于 select单选框,操作比较简单:

不管原来选的是什么,直接用Select方法选择即可。

例如,选择示例里面的小雷老师,示例代码如下

# 导入Select类
from selenium.webdriver.support.ui import Select

# 创建Select对象
select = Select(wd.find_element_by_id("ss_single"))

# 通过 Select 对象选中小雷老师
select.select_by_visible_text("小雷老师")

Select多选框

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

例如,我们选择示例多选框中的 小雷老师 和 小凯老师

可以用select类 的deselect_all方法,清除所有 已经选中 的选项。

然后再通过 select_by_visible_text方法 选择 小雷老师 和 小凯老师。

示例代码如下:

# 导入Select类
from selenium.webdriver.support.ui import Select

# 创建Select对象
select = Select(wd.find_element_by_id("ss_multi"))

# 清除所有 已经选中 的选项
select.deselect_all()

# 选择小雷老师 和 小凯老师
select.select_by_visible_text("小雷老师")
select.select_by_visible_text("小凯老师")

实用技巧

更多动作

ActionChains
  • 通过 Selenium 提供的 ActionChains 类可以实现鼠标的右击、双击、移动鼠标到某个元素、鼠标的拖拽等

  • ActionChains 类 里面提供了 一些特殊的动作的模拟,我们可以通过 ActionChains 类的代码查看到

在这里插入图片描述

  • 我们以移动鼠标到某个元素为例。

    • 百度首页的右上角,有个 更多 选项,把鼠标放在上边,就会弹出 下面的 糯米、音乐、图片 等图标。

    • 使用 ActionChains 来 模拟鼠标移动 操作的代码如下:

      from selenium import webdriver
      
      driver = webdriver.Chrome(executable_path="D:chromedriver.exe")
      driver.implicitly_wait(5)
      
      driver.get('https://www.baidu.com/')
      
      from selenium.webdriver.common.action_chains import ActionChains
      
      ac = ActionChains(driver)
      
      # 鼠标移动到 元素上
      ac.move_to_element(
          driver.find_element_by_css_selector('[name="tj_briicon"]')
      ).perform()
      
直接执行javascript
# 直接执行 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
冻结界面

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

setTimeout(function(){debugger}, 5000)

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

弹出对话框

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

Alter

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

selenium提供如下方法进行操作点击ok按钮

driver.switch_to.alert.accept()

获取弹出对话框中的信息内容

driver.switch_to.alert.text

示例代码如下

from selenium import webdriver
driver = webdriver.Chrome()
driver.implicitly_wait(5)
driver.get('http://cdn1.python3.vip/files/selenium/test4.html')


# --- alert ---
driver.find_element_by_id('b1').click()

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

# 点击 OK 按钮
driver.switch_to.alert.accept()
Confirm
  • Confirm弹出框,主要是让用户确认是否要进行某个操作

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

  • selenium提供如下方法进行操作

    • 点击ok按钮 : driver.switch_to.alert.accept()

    • 点击Cancel按钮 : driver.switch_to.alert.dismiss()

    • 示例代码如下

      from selenium import webdriver
      driver = webdriver.Chrome()
      driver.implicitly_wait(5)
      driver.get('http://cdn1.python3.vip/files/selenium/test4.html')
      
      # --- confirm ---
      driver.find_element_by_id('b2').click()
      
      # 打印 弹出框 提示信息
      print(driver.switch_to.alert.text)
      
      # 点击 OK 按钮 
      driver.switch_to.alert.accept()
      
      driver.find_element_by_id('b2').click()
      
      # 点击 取消 按钮
      driver.switch_to.alert.dismiss()
      
Prompt
  • 出现 Prompt 弹出框 是需要用户输入一些信息,提交上去。

  • 可以调用如下方法

    driver.switch_to.alert.send_keys()
    
  • 示例代码如下

    from selenium import webdriver
    driver = webdriver.Chrome()
    driver.implicitly_wait(5)
    driver.get('http://cdn1.python3.vip/files/selenium/test4.html')
    
    
    # --- prompt ---
    driver.find_element_by_id('b3').click()
    
    # 获取 alert 对象
    alert = driver.switch_to.alert
    
    # 打印 弹出框 提示信息
    print(alert.text)
    
    # 输入信息,并且点击 OK 按钮 提交
    alert.send_keys('web自动化 - selenium')
    alert.accept()
    
    # 点击 Cancel 按钮 取消
    driver.find_element_by_id('b3').click()
    alert = driver.switch_to.alert
    alert.dismiss()
    

Selenium自动化技巧

窗口大小
  • 获取窗口大小
driver.get_window_size()
  • 改变窗口大小
driver.set_window_size(x, y)
获取当前窗口标题
driver.title
获取当前窗口URL地址
driver.current_url
截屏
from selenium import  webdriver

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

# 打开网站
driver.get('https://www.baidu.com/')

# 截屏保存为图片文件
driver.get_screenshot_as_file('1.png')
手机模式打开Chrome

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

参考代码,如下

from selenium import webdriver

mobile_emulation = { "deviceName": "Nexus 5" }

chrome_options = webdriver.ChromeOptions()

chrome_options.add_experimental_option("mobileEmulation", mobile_emulation)

driver = webdriver.Chrome( desired_capabilities = chrome_options.to_capabilities())

driver.get('http://www.baidu.com')

input()
driver.quit()
上传文件

有时候,网站操作需要上传文件。比如,著名的在线图片压缩网站: https://tinypng.com/

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

如下所示:

<input type="file" multiple="multiple">

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

# 先定位到上传文件的 input 元素
ele = wd.find_element_by_css_selector('input[type=file]')

# 再调用 WebElement 对象的 send_keys 方法
ele.send_keys(r'h:\g02.png')

如果需要上传多个文件,可以多次调用send_keys,如下

ele = wd.find_element_by_css_selector('input[type=file]')
ele.send_keys(r'h:\g01.png')
ele.send_keys(r'h:\g02.png')
自动化Edge浏览器

自动化基于Chromium内核的 微软最新Edge浏览器,首先需要查看Edge的版本。

点击菜单 帮助和反馈 > 关于Microsoft Edge ,在弹出界面中,查看到版本,比如

版本 79.0.309.71 (官方内部版本) (64)

然后 点击这里,打开Edge浏览器驱动下载网页 ,并选择下载对应版本的驱动。

在自动化代码中,指定使用Edge Webdriver类,并且指定 Edge 驱动路径,如下所示

from selenium import webdriver

driver = webdriver.Edge(r'd:\tools\webdrivers\msedgedriver.exe')

driver.get('http://www.51job.com')

Xpath选择器

XPath (XML Path Language) 是由国际标准化组织W3C指定的,用来在 XML 和 HTML 文档中选择节点的语言。

目前主流浏览器 (chrome、firefox,edge,safari) 都支持XPath语法,xpath有 1 和 2 两个版本,目前浏览器支持的是 xpath 1的语法。

既然已经有了CSS,为什么还要学习 Xpath呢? 因为

  • 有些场景 用 css 选择web 元素 很麻烦,而xpath 却比较方便。
  • 另外 Xpath 还有其他领域会使用到,比如 爬虫框架 Scrapy, 手机App框架 Appium。

绝对路径选择

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

elements = driver.find_elements_by_xpath("/html/body/div")

相对路径选择

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

对应的自动化程序如下

elements = driver.find_elements_by_xpath("//div//p")

通配符

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

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

代码如下:

elements = driver.find_elements_by_xpath("//div/*")
for element in elements:
    print(element.get_attribute('outerHTML'))

根据属性选择

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

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

注意:

  • 属性名注意前面有个@
  • 属性值一定要用引号, 可以是单引号,也可以是双引号
根据id属性选择

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

根据class属性选择

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

如果一个元素class 有多个,比如

<p id="beijing" class='capital huge-city'>
    北京    
</p>

如果要选 它, 对应的 xpath 就应该是 //p[@class="capital huge-city"]

不能只写一个属性,像这样 //p[@class="capital"] 则不行

根据其他属性

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

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

属性值包含字符串

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

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

要选择 style属性值 以 某个 字符串 结尾 的 页面元素 ,大家可以推测是 //*[ends-with(@style,'color')], 但是,很遗憾,这是xpath 2.0 的语法 ,目前浏览器都不支持

按次序选择

某类型第几个子元素

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

//p[2]

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

再比如,要选取父元素为div 中的 p类型 第2个 子元素

//div/p[2]
第几个子元素

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

比如 选择父元素为div的第2个子元素,不管是什么类型

//div/*[2]
某类型倒数第几个子元素

当然也可以选取倒数第几个子元素

比如:

  • 选取p类型倒数第1个子元素
//p[last()]
  • 选取p类型倒数第2个子元素
//p[last()-1]
  • 选择父元素为div中p类型倒数第3个子元素
//div/p[last()-2]
范围选择

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

比如,

  • 选取option类型第1到2个子元素
//option[position()<=2]

或者

//option[position()<3]
  • 选择class属性为multi_choice的前3个子元素
//*[@class='multi_choice']/*[position()<=3]
  • 选择class属性为multi_choice的后3个子元素
//*[@class='multi_choice']/*[position()>=last()-2]

为什么不是 last()-3 呢? 因为

last() 本身代表最后一个元素

last()-1 本身代表倒数第2个元素

last()-2 本身代表倒数第3个元素

组选择、父节点、兄弟节点

组选择

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

css 组选择,表达式之间用 逗号 隔开

xpath也有组选择, 是用 竖线 隔开多个表达式

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

//option | //h4

等同于CSS选择器

option , h4

再比如,要选所有的 class 为 single_choice 和 class 为 multi_choice 的元素,可以使用

//*[@class='single_choice'] | //*[@class='multi_choice']

等同于CSS选择器

.single_choice , .multi_choice
选择父节点

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

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

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

当某个元素没有特征可以直接选择,但是它有子节点有特征, 就可以采用这种方法,先选择子节点,再指定父节点。

还可以继续找上层父节点,比如 //*[@id='china']/../../..

兄弟节点选择

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

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

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

等同于CSS选择器 .single_choice ~ *

如果,要选择后续节点中的div节点, 就应该这样写 //*[@class='single_choice']/following-sibling::div

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

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

而CSS选择器目前还没有方法选择前面的 兄弟节点

selenium 注意点

我们的代码:

  • 先选择示例网页中,id是china的元素
  • 然后通过这个元素的WebElement对象,使用find_elements_by_xpath,选择里面的p元素,
# 先寻找id是china的元素
china = wd.find_element_by_id('china')

# 再选择该元素内部的p元素
elements = china.find_elements_by_xpath('//p')

# 打印结果
for element in elements:
    print('----------------')
    print(element.get_attribute('outerHTML'))

运行发现,打印的 不仅仅是 china内部的p元素, 而是所有的p元素。

要在某个元素内部使用xpath选择元素, 需要 在xpath表达式最前面加个点

像这样

elements = china.find_elements_by_xpath('.//p')
choice 的元素,可以使用

//[@class=‘single_choice’] | //[@class=‘multi_choice’]


等同于CSS选择器

.single_choice , .multi_choice


#### 选择父节点

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

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

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

当某个元素没有特征可以直接选择,但是它有子节点有特征, 就可以采用这种方法,先选择子节点,再指定父节点。

还可以继续找上层父节点,比如 `//*[@id='china']/../../..`

#### 兄弟节点选择

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

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

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

等同于CSS选择器 `.single_choice ~ *`

如果,要选择后续节点中的div节点, 就应该这样写 `//*[@class='single_choice']/following-sibling::div`

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

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

而CSS选择器目前还没有方法选择前面的 兄弟节点

## selenium 注意点

我们的代码:

- 先选择示例网页中,id是china的元素
- 然后通过这个元素的WebElement对象,使用find_elements_by_xpath,选择里面的p元素,

```py
# 先寻找id是china的元素
china = wd.find_element_by_id('china')

# 再选择该元素内部的p元素
elements = china.find_elements_by_xpath('//p')

# 打印结果
for element in elements:
    print('----------------')
    print(element.get_attribute('outerHTML'))

运行发现,打印的 不仅仅是 china内部的p元素, 而是所有的p元素。

要在某个元素内部使用xpath选择元素, 需要 在xpath表达式最前面加个点

像这样

elements = china.find_elements_by_xpath('.//p')
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值