第4章 WebDriver API
从本章正式开始学习Selenium ,对于本章标题的命名比较纠结,要想起一个确切的符合主题的名字并不太容易。WebDriver 属于Selenium 体系中设计出来操作浏览器的一套API,它支持多种编程语言,这个我们前面已经介绍过。那么站在编程语言的角度来看,Selenium WebDriver 只是Java 的一个第三方框架,和Spring web 开发框架属于同一性质,只是Spring 只在Java 语言中存在,其它语言也有用于web 开发框架,但不叫Spring。而Selenium WebDriver 框架针对不同语言分别开发了该框架,所以,在不同的编程语言里它都叫Selenium WebDriver。那么本章,我们将详细介绍这个框架所提供的操作web 页面的各种常用方法。
4.1 从定位元素开始
在本章学习开始之前,我们先来看一张Web 页面,如图4.1 所示。
图4.2,通过前端工具,我们看可以看到,页面上的元素都是由一行行的代码组成的,它们之间有层级地组织起来,每个元素有不同的标签名和属性值。WebDriver 就是通过这信息来找到不同的元素的。
WebDriver 提供了八种元素定位方法:
id
name
class name
tag name
link text
partial link text
xpath
css selector
在Java 语言中对应的定位方法如下:
import org.openqa.selenium.By;
…
findElement(By.id())
findElement(By.name())
findElement(By.className())
findElement(By.tagName())
findElement(By.linkText())
findElement(By.partialLinkText())
findElement(By.xpath())
findElement(By.cssSelector())
下面我们就逐一来介绍这些定位方法的使用。在此之前,我们复制百度首页的前端代码,并以此为例来讲解定位页面上元素的定位方法。
baidu.html
(1)它们由标签对组成:
html、div 就是标签的标签名。(2)标签有各种属性:
新闻
hao123
地图
(4)标签有层级关系:
对于上面的结构,如果把input 看作子标签,那么form 就是它的父标签。
理解上面这些特性是学习定位方法的基础。我们以百度输入框和百度搜索按钮为例,来学习不同的定位方法,百度输入框和搜索按钮的代码如下。
……
……
……
如果把页面上的元素看作人的话,在现实世界中如何找到某人呢?
首先,是可以通过人本身的属性,例如他的姓名,手机号,身份证号,性别,这些都是用于区别它人的属性。在Web 页面上的元素也有这一些属性,例如,id、name、class name、tag name 等。
其次,在找查找某人的时候可以通过位置属性,例如,x 国,x 市,x 路,x 号。XPath 和CSS 就提供了这种以标签名为层级关系的定位方式。
最后,可以借助相关人的属性来找到某人,例如,我没有小明的联系方式,但我有他爸爸的手机号,那么通过他爸爸手机号最终也可以找到小明。XPath 和CSS 同样提供了这种通过相关元素来查找最终元素的方式。
理解了这些查找规则之后,下面所要介绍的几种定位方式就很好理解了。
4.1.1 id 定位
HTML 规定id 属性在HTML 文档中必须是唯一的,这就类似于公民的身份证号,具有很强的唯一。WebDriver提供的id 定位方法就是通过元素的id 属性来查找元素。通过id 定位百度输入框与百度搜索按钮,用法如下:
findElement(By.id(“kw”))
findElement(By.id(“su”))
find_element(By.id())方法通过id 属性定位来元素。
4.1.2 name 定位
HTML 规定name 来指定元素的名称,因此它的作用更像是人的姓名。name 的属性值,在当前页面中可以不唯一。通过name 定位百度输入框:
findElement(By.name(“wd”))
find_element(By.name())方法通过name 属性来定位元素。由于百度搜索按钮并没有提供name 属性,因此我们不能通过name 属性来定位它。
4.1.3 class 定位
HTML 规定class 来指定元素的类名。其用法与id、name 类似,下面通过class 属性定位百度输入框和搜索按钮:
findElement(By.className(“s_ipt”))
find_element(By.className())方法通过class 属性来定位元素。
4.1.4 tag 定位
HTML 的本质就是通过tag 来定义实现不同的功能,每一个元素本质上也是一个tag。因为一个tag 往往用来定义一类功能,所以通过tag 识别某个元素的概率很低。例如我们打开任意一个页面,查看前端都会发现大量的
通过标tag name 定位百度的输入框与百度按钮会发现它们完全相同:
findElement(By.tagName(“input”))
find_element_(By.tagName())方法通过元素的tag name 来定位元素。
4.1.5 link 定位
link 定位与前面介绍的几种定位方法有所不同,它专门用来定位文本链接。百度输入框上面的几个文本链接的代码如下:
新闻
hao123
地图
视频
贴吧>
查看上面的代码。我们发现,通过name 属性定位是个不错的选择。不过这里是为了演示link 定位的使用,通过link 定位链接如下:
findElement(By.linkText(“新闻”))
findElement(By.linkText(“hao123”))
findElement(By.linkText(“地图”))
findElement(By.linkText(“视频”))
findElement(By.linkText(“贴吧”))
findElement(By.linkTame())方法通过元素标签对之间的文本信息来定位元素。
4.1.6 partial link 定位
parial link 定位是对link 定位的一种补充,有些文本链接会比较长,这个时候我们可以取文本链接的一部分定位,只要这一部分信息可以唯一地标识这个链接。
一个很长很长的文本链接
通过partial link 定位如下:
findElement(By.partialLinkText(“一个很长的”))
findElement(By.partialLinkText(“文本链接”))
findElement(By.partialLinkText())方法通过元素标签对之间的部分文本信息来定位元素。
前面所介绍的几种定位方法相对来说比较简单,理想状态下,在一个页面当中每一个元素都有一个唯一id和name 属性值,我们可以通过它们的属性值来找到它们。但在实际项目中,并非想象得这般美好。有时候一个元素并没有id、name 属性,或者页面上多个元素的id 和name 属性值相同,又或者每一次刷新页面,id 值都会随机变化。在这种情况下,我们如何来定位元素呢?
下面介绍xpath 与CSS 定位,与前面介绍的几种定位方式相比,它们提供了灵活的定位策略,可以通过不同的方式定位到想要的元素。
4.1.7 XPath 定位
XPath 是一种在XML 文档中定位元素的语言。因为HTML 可以看作XML 的一种实现,所以Selenium 用户可以使用这种强大的语言在Web 应用中定位元素。
绝对路径定位。
XPath 有多种定位策略,最简单直观的就是写出元素的绝对路径。如果仍把一个元素看作一个人的话,假设这个人没有任何属性特征(手机号、姓名、身份证号),但这个人一定存在于某个地理位置,如xx 省xx 市xx 区xx 路xx 号。对于页面上的元素而言也会有这样一个绝对地址。
参考baidu.html 前端工具所展示的代码,我们可以通过下面的方式找到百度输入框和搜索按钮。
findElement(By.xpath("/html/body/div/div[2]/div/div/div/from/span/input"))
findElement(By.xpath("/html/body/div/div[2]/div/div/div/from/span[2]/input"))
find_element_by_xpath()方法使用XPath 语言来定位元素。XPath 主要用标签名的层级关系来定位元素的绝对路径,最外层为html 语言。有body 文本内,一级一级往下查找,如果一个层级下有多个相同的标签名,那么就按上下顺序确定是第几个,例如div[2]表示当前层级下的第二个div 标签。
利用元素属性定位。
除了使用绝对路径外,XPath 也可以使用元素的属性值来定位。同样以百度输入框和搜索按钮为例:
findElement(By.xpath("//input[@id=‘kw’]"))
findElement(By.xpath("//input[@id=‘su’]"))
//表示当前页面某个目录下,input 表示定位元素的标签名,[@id=‘kw’] 表示这个元素的id 属性值等于kw。下面通过name 和class 属性值来定位。
findElement(By.xpath("//input[@name=‘wd’]"))
findElement(By.xpath("//input[@class=‘s_ipt’]"))
findElement(By.xpath("//*[@class=‘bg s_btn’]"))
如果不想指定标签名,则也可以用星号(*)代替。当然,使用XPath 不局限于id、name 和class 这三个属性值,元素的任意属性值都可以使用,只要它能唯一的标识一个元素。
findElement(By.xpath("//input[@maxlength=‘100’]"))
findElement(By.xpath("//input[@autocomplete=‘off’]"))
findElement(By.xpath("//input[@type=‘submit’]"))
层级与属性结合。
如果一个元素本身并没有可以唯一标识这个元素的属性值,那么我们可以找其上一级元素,如果它的上一级元素有可以唯一标识属性的值,也可以拿来使用。参考baidu.html 文本。
……
……假如百度输入框本身没有可利用的属性值,那么我们可以查找它的上一级属性。例如,“小明”刚出生的时候没有名字,没上户口(没身份证号),那么亲朋好友来找“小明”时可以先找到小明的爸爸,因为他爸爸是有很多属性特征的,找到了小明的爸爸后,就可以找到小明了。通过XPath 描述如下:
findElement(By.xpath("//span[@class=‘bg s_ipt_wr’]/input"))
span[@class=‘bg s_ipt_wr’] 通过class 属性定位到父元素,后面/input 就表示父元素下面的子元素。如果父元素没有可利用的属性值,那么可以继续向上查找“爷爷”元素。
findElement(By.xpath("//form[@id=‘form’]/span/input"))
findElement(By.xpath("//form[@id=‘form’]/span[2]/input"))
我们可以通过这种方法一级一级地向上查找,直到找到最外层的标签,那么就是一个绝对路径的写法了。
使用逻辑运算符
如果一个属性不能唯一地区分一个元素,我们还可以使用逻辑运算符连接多个属性来查找元素。
……
……
如上面的三行元素,假设我们现在要定位第一行元素,如果使用id 将会与第二行元素重名,如果使用class将会与第三行元素重名。如果同时使用id 和class 就会唯一的标识这个元素,那么这个时候就可以通过逻辑运算符“and”来连接两个条件。
findElement(By.xpath("//input[@id=‘kw’ and @class=‘su’]/span/input"))
当然,我们也可以用“and”连接更多的属性来唯一地标识一个元素。
我们在本书第1 章中介绍的Firebug 前端调试工具和FirePath 插件可以方便地辅助生成XPath 语法。打开Firefox 浏览器的FireBug 插