说明:
WebDriver属于Selenium体系中用来操作浏览器的一套API,同时,WebDriver也是Python的一个用于实现Web自动化的第三方库。
首先来看一下百度首页的Web页面,如下:
在这张web页面上,我们看到有 输入框、按钮和文字链接,还有图片、页面底部的文字等。自动化要做的就是模拟鼠标和键盘来操作这些元素,或单击,或输入,或鼠标悬停等操作。
操作这些页面元素的前提就是要找到它们,自动化工具不可能像人一样可以通过肉眼就可以分辨这些元素,并且知道它们是干什么用的,那么自动化工具如何找到它们呢?
接下来我们通过前端工具来看一下这些元素的真实面目。
对于谷歌浏览器,我们只需要按 F12 就可以启用开发者工具查看页面元素,如下:
我们可以看到页面上的元素都是由一行行的代码组成的,它们之间是层级关系组织起来的,每个元素有不同的标签名和属性值。WebDriver就是通过这些信息来找到不同的元素的。
WebDriver提供了8种元素定位方法,在Python中,其对应的方法如下:
元素 | 方法 |
---|---|
id | name |
class name | tag name |
link text | partial link text |
xpath | css selector |
find_element_by_id() | find_element_by_name() |
find_element_by_class_name() | find_element_by_tag_name() |
find_element_by_link_text() | find_element_by_partial_tag_name() |
find_element_by_xpath() | find_element_by_css_selector() |
下面是百度首页的前端代码(Chrome开发者工具查看所得)
<!--STATUS OK-->
<html>
<head>…</head>
<body link=”#0000cc”>
<div data-for="result" id="swfEveryCookieWrap" style="width: 0px; height: 0px; overflow: hidden;">...</div>
<script>…</script>
<div id="wrapper" style="display: block;">
<script>
if(window.bds&&bds.util&&bds.util.setContainerWidth){bds.util.setContainerWidth();}
</script>
<div id=”head”>
<div class=”head_wrapper”>
<div class=”s_form”>
<div class="s_form_wrapper soutu-env-nomac soutu-env-index">
<div id="lg">
<img hidefocus="true" src="//www.baidu.com/img/bd_logo1.png" width="270" height="129">
</div>
<a href="/" id="result_logo" onmousedown="return c({'fm':'tab','tab':'logo'})">
<img src="//www.baidu.com/img/baidu_jgylogo3.gif" alt="到百度首页" title="到百度首页">
</a>
<form id="form" name="f" action="/s" class="fm">
<input type="hidden" name="ie" value="utf-8">
<input type="hidden" name="f" value="8">
<input type="hidden" name="rsv_bp" value="0">
<input type="hidden" name="rsv_idx" value="1">
<input type="hidden" name="ch" value="">
<input type="hidden" name="tn" value="baidu">
<input type="hidden" name="bar" value="">
<span class="bg s_ipt_wr quickdelete-wrap">
<span class="soutu-btn"></span>
<input id="kw" name="wd" class="s_ipt" value="" maxlength="255" autocomplete="off">
<a href="javascript:;" id="quickdelete" title="清空" class="quickdelete" style="top: 0px; right: 0px; display: none;"></a>
</span>
<span class="bg s_btn_wr">
<input type="submit" id="su" value="百度一下" class="bg s_btn">
</span>
……
</body>
<html>
从上面的代码我们可以看到
- 它们都是由标签组成:
<html></html>
<body></body>
<div></div>
<form></form>
……
- 标签有各种属性
<div id=”head” class=”s_down”>
<from class=”well”>
<input id="kw" name="wd" class="s_ipt" value="" maxlength="255" autocomplete="off">
就像人也会有各种属性一样,如身份证(id)、姓名(name)、职业(class)等
- 标签对之间可以有文本数据:
<a>新闻</a>
<a>地图</a>
<a>视频</a>
<a>贴吧</a>
- 标签有层级关系
<html>
<body>
</body>
</html>
<div>
<form>
<input />
</form>
</div>
2.1 id定位
HTML规定id属性在HTML文档中必须是唯一的,这类似于公民的身份证号,具有很强的唯一性。
Webdriver提供的id
定位方法就是通过元素的id
属性来查找元素。
例如:通过id定位百度输入框与百度搜索按钮,
用法如下:
find_element_by_id(“kw”)
find_element_by_id(“su”)
说明: find_element_by_id()
方法通过id
属性来定位元素。
2.2 name定位
HTML规定name
来指定元素的名称,因此它的作用就像是人的姓名。
name
的属性值在当前页中可以不唯一。
例如:通过name定位百度输入框,
用法如下:
find_element_by_name(“wd”)
说明: find_element_by_name()
方法通过name属性来定位元素。
注意:由于百度搜索按钮并没有提供name
属性,因此就不能通过name
属性来定位它。
2.3 class定位
HTML规定class
用来指定元素的类名。
例如:通过class
属性来定位百度输入框和搜索按钮,
用法如下:
find_element_by_class_name(“s_ipt”)
find_element_by_class_name(“bg s_btn”)
说明: find_element_by_class_name()
方法通过class
属性来定位元素。
2.4 tag定位
HTML的本质就是通过tag
来定义实现不同的功能,每个元素本质上就是一个tag
。
因为一个tag
往往用来定义一类功能,所以通过tag
识别某个元素的概率很低。
例如,我们打开任意一个页面,查看前端都会发现有大量的<div>、<input>、<a>
等tag
,所以很难通过tag name
去区分不同的元素。
例如:通过tag name
去定位百度的输入框和搜索按钮,你会发现它们完全一样
用法如下:
find_element_by_tag_name(“input”)
说明: find_element_by_tag_name()
方法通过元素的tag name来定位元素。
2.5 link定位
link
专门用来定位文本链接。
例如:百度首页上的几个文本链接如下
其文本链接的代码如下:
<a href="http://news.baidu.com" name="tj_trnews" class="mnav">新闻</a>
<a href="http://www.hao123.com" name="tj_trhao123" class="mnav">hao123</a>
<a href="http://map.baidu.com" name="tj_trmap" class="mnav">地图</a>
<a href="http://v.baidu.com" name="tj_trvideo" class="mnav">视频</a>
<a href="http://tieba.baidu.com" name="tj_trtieba" class="mnav">贴吧</a>
<a href="http://xueshu.baidu.com" name="tj_trxueshu" class="mnav">学术</a>
根据上面的代码,我们发现,其实通过name
属性来定位是一个更好的选择。不过这里为了演示link
定位的使用,用法如下:
find_element_by_link_text(“新闻”)
find_element_by_link_text(“hao123”)
find_element_by_link_text(“地图”)
find_element_by_link_text(“视频”)
find_element_by_link_text(“贴吧”)
find_element_by_link_text(“学术”)
说明: find_element_by_link_text()
方法通过元素标签对之间的文本信息来定位元素。
2.6 partial link定位
partial link
定位是对link
定位的一种补充,有些文本链接较长,这个时候可以取文本链接的一部分进行定位,只要这部分信息可以唯一的标识这个链接即可。
例如:
<a class="mnav" name=”tj_long” href=”#”>一个很长的文本链接</a>
通过partial link定位如下:
find_element_by_partial_link_text(“一个很长的”)
find_element_by_partial_link_text(“文本链接”)
说明: find_element_by_partial_link_text()
方法通过元素标签对之间的部分文本信息来定位元素。
2.7 XPath定位
XPath
是一种在XML文档中定位元素的语言。因为HTML可以看做XML的一种实现,所以Selenium用户可以使用XPath
在web应用中来定位元素。
- 方法1:绝对路径定位
参考前面的前端展示的代码,我们可以通过下面的方法来定位到百度输入框和百度搜索按钮
find_element_by_xpath(“/html/body/div/div/div/div/div/form/span/input”)
find_element_by_xpath(“/html/body/div/div/div/div/div/form/span[2]/input”)
说明: find_element_by_xpath()
方法使用XPath
语言来定位元素。
XPath
主要用标签名的层级关系来定位元素的绝对路径,最外层的是html
语言。然后在body
文本内,一级一级往下查找,如果一个层级下有多个相同的标签名,那么就按照上下顺序确定是第几个,例如span[2]
表示当前层级下的第2个span
标签。
- 方法2:利用元素属性定位
同样以百度输入框和百度搜索按钮为例,用法如下:
find_element_by_xpath(“//input[@id=’kw’]”)
find_element_by_xpath(“//input[@id=’su’]”)
说明:
//
表示当前页面下某个目录,input
表示定位元素的标签名,[@id=’kw’]
表示这个元素的id
属性值等于kw
下面通过id
、name
和class
属性值来定位,方法如下:
find_element_by_xpath(“//input[@id=’kw’]”)
find_element_by_xpath(“//input[@name=’wd’]”)
find_element_by_xpath(“//input[@class=’s_ipt’]”)
find_element_by_xpath(“//input[@class=’bg s_btn’]”)
说明: 如果不指定标签名,则也可以使用星号(*
)来代替。
使用XPath
不局限于id
,name
,class
这三个属性值,元素的任意属性值都可以使用,只要它能唯一的标识这个元素。
例如:
find_element_by_xpath(“//input[@maxlength=255]”)
find_element_by_xpath(“//input[@autocomplete=’off’]”)
find_element_by_xpath(“//input[@type=’submit’]”)
- 方法3:层级与属性结合
如果一个元素本身没有可以唯一标识这个元素的属性值,那么我们可以找其上一级元素,如果它的上一级元素有可以唯一标识的属性值,也可以拿来使用。
还是以定位百度输入框和百度搜索按钮为例,用法如下:
find_element_by_xpath(“//span [@class=’bg s_ipt_wr’]/input”)
find_element_by_xpath(“//span [@class=’bg s_btn_wr’]/input”)
说明: span [@class=’bg s_ipt_wr’]
通过class
元素定位到父元素,后面的/input
就是父元素下面的子元素。如果父元素没有可利用的属性值,那么可以继续向上找“爷爷”元素,如:
find_element_by_xpath(“//form [@id=’form’]/span/input”)
find_element_by_xpath(“//form [@id=’form’]/span[2]/input”)
- 方法4:使用逻辑运算符
如果一个属性不能唯一区分一个元素,我们可以使用逻辑运算符连接多个属性来定位元素。
…….
<input id=”kw” class=”su” name=”ie”>
<input id=”kw” class=”aa” name=”ie”>
<input id=”bb” class=”su” name=”ie”>
…….
如上面的三行元素,假设我们现在要定位第一行的元素,如果使用id
将会与第二行元素重名,如果使用class
将会与第三行元素重名,而如果同时使用id
和class
就会唯一的标识第一行元素,这个时候就可以通过逻辑运算符“and”
来连接这两个条件,用法如下:
find_element_by_xpath(“//input[@id=’kw’ and class=’su’]”)
2.8 CSS定位
CSS(Cascading Style Sheets)
是一种语言,它用来描述HTML和XML文档的表现。CSS使用选择器来为页面元素绑定属性。
CSS选择器的常见语法如下:
选择器 | 例子 | 描述 |
---|---|---|
.class | .intro | class选择器,选择class=“intro”的所有元素 |
#id | #firstname | id选择器,选择id=“firstname”的所有元素 |
* | * | 选择所有元素 |
element | P | 元素所有 <p> 元素 |
element > element | div > input | 选择父元素为<div> 的所有<input> 元素 |
element+ element | div + input | 选择同一级中紧接在<div> 元素之后的所有<input> 元素 |
[attribute=value] | [target=_blank] | 选择target=“_blank”的所有元素 |
下面同样以百度输入框和百度搜索按钮为例介绍CSS定位的用法
……
<span class="bg s_ipt_wr quickdelete-wrap">
<span class="soutu-btn"></span>
<input id="kw" name="wd" class="s_ipt" value="" maxlength="255" autocomplete="off">
<a href="javascript:;" id="quickdelete" title="清空" class="quickdelete" style="top: 0px; right: 0px; display: none;"></a>
</span>
<span class="bg s_btn_wr">
<input type="submit" id="su" value="百度一下" class="bg s_btn">
</span>
……
1)通过class属性定位
find_element_by_css_selector(“.s_ipt”)
//选择class=“s_ipt”
的元素,即百度输入框
find_element_by_css_selector(“.bg s_btn”)
//选择class=“bg s_btn”
的元素,即百度搜索按钮
2)通过id属性定位
find_element_by_css_selector(“#kw”)
//选择id=“kw”
的元素,即百度输入框
find_element_by_css_selector(“#su”)
//选择id=“su”
的元素,即百度搜索按钮
3)通过标签名定位
find_element_by_css_selector(“input”)
说明:
在CSS中,用标签名定位元素不需要任何标识符号,直接使用标签名即可。但我们在前面已经了解到,标签名重复的概率非常大,所以直接通过标签名定位的方式很难准确找到我们想要的元素。
4)通过父子关系定位
find_element_by_css_selector(“span>input”)
说明:
上面的写法表示有父元素,其父元素的标签名为span
,查找它的所有标签名为input
的子元素。
5)通过属性定位
find_element_by_css_selector(“[autocomplete=off]”)
find_element_by_css_selector(“[name=’kw’]”)
find_element_by_css_selector(‘[type=”submit”]’)
说明:
在CSS中,对于属性值来说,可加引号,也可以不加引号,但注意和整个字符串的引号进行区分。
6)组合定位
组合定位就是将前面的定位方式组合起来使用
find_element_by_css_selector(“form.fm>span>input.s_ipt”)
find_element_by_css_selector(“form#form>span>input#kw”)
说明:
对于第一行,我们要找的这个元素的标签名为input
,这个元素的class
属性值为s_ipt
;并且它有一个父元素,其标签名为span
,它的父元素还有父元素,其标签名为form
,而且form
元素的class
属性值为fm
。
对于第二行,我们要找的这个元素的标签名为input
,这个元素的id
属性值为kw
;并且它有一个父元素,其标签名为span
,它的父元素还有父元素,其标签名为form
,而且form
元素的id
属性值为form
。
从上面的两行描述,我们看到我们要找的这个元素就是百度输入框。
2.9 By定位
针对前面提到的8中元素定位方法,WebDriver还提供了另外一套写法,即统一调用find_element()
方法,通过By
来生命定位的方法,并且传入对应定位方法的定位参数。具体用法如下:
find_element(By.ID,”kw”)
find_element(By.NAME,”wd”)
find_element(By.CLASS_NAME,”s_ipt”)
find_element(By.TAG_NAME,”input”)
find_element(By.LINK_TEXT,”新闻”)
find_element(By.PARTIAL_LINK_TEXT,”新”)
find_element(By.XPATH,”//*[@class=’bg s_btn’]”)
find_element(By.SELECTOR,”span.bg s_btn_wr>input#su”)
说明:
find_element()
方法只用于定位元素。它需要两个参数,第一个参数是定位的类型,由By
提供;第二个参数是定位的具体方式。
在使用By之前需要导入By
类,如下:
from selenium.webdriver.common.by import By
最后我们以定位百度输入框为例,写一段代码如下:
#coding=utf-8
from selenium import webdriver
import time
driver = webdriver.Chrome()
driver.get("http://www.baidu.com")
time.sleep(2)
#########百度输入框的定位方式##########
#通过id方式定位
driver.find_element_by_id("kw").send_keys("python ")
#通过name方式定位
driver.find_element_by_name("wd").send_keys("selenium ")
#通过class name 方式定位
driver.find_element_by_class_name("s_ipt").send_keys("java ")
#通过CSS方式定位
driver.find_element_by_css_selector("#kw").send_keys("C++ ")
#通过xpath方式定位
driver.find_element_by_xpath("//input[@id='kw']").send_keys("JavaScript ")
driver.find_element_by_id("su").click()
time.sleep(10)
driver.quit()
运行结果如下: