现在介绍xpath里,一些常用的函数,叫方法也行,一个意思。前面的网页案例,再改下源码
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<ul style="list-style:none">
<li><span>用户名:</span><input placeholder="8-12位字母数字" /></li>
<li><span>密码:</span><input placeholder="6-16位字母数字" /></li>
<li><span>验证码:</span><input placeholder="4位数字" /></li>
<li><button>按钮</button></li>
</ul>
<script>
document.getElementsByTagName('button')[0].onclick = function(){
alert("haha");
}
</script>
</body>
</html>
现在,最后一个按钮,没有使用input标签了,而是改用button标签了。在页面上,看起来,效果是一样的。现在,要找到这个按钮,再点击,通过属性找,就没法找了,因为"按钮",不是标签的属性,而是标签的文本了。这个时候,要通过"按钮"两个字来找到这个按钮,那么就需要使用text()函数,text()函数就是获取标签的文本的。写法:"//*[text()='按钮']",意思就是在所有层级找文本是"按钮"的元素。需要注意,当元素还有子元素时,它的文本是不包含子元素的,但是包含所有后代元素的文本。比如:把按钮那一行的代码改成"<li><button>小小的<span>按钮</span>吖</button></li>",这个时候,button标签的文本就是"小小的按钮吖"。
有些时候,我们在页面上看到的文本,在代码里,它的前后可能有空格或是换行符存在。那么这个时候,在获取元素的文本时,希望可以去掉首尾的空格或是换行符。这个时候就可以使用normalize-space()函数,比如我们现在要找去掉首尾空格后文本是"按钮"的元素,就可以这样写"//*[normalize-space(text())='按钮']",如果要找value属性去掉首尾空格后是"按钮"的元素,可以这样写"//*[normalize-space(@value)='按钮']"。
有些时候,我们知道元素的一部分文本,通过一部分文本去找元素,这个时候,就可以使用函数contains(),用法:"//*[contains(text(),'按钮')]",意思就是找到所有的文本包含"按钮"的元素。"//*[starts-with(@value, '按钮')]",这个就是找出所有value属性是以"按钮"开头的元素,注意,没有ends-with()函数。
还有一些函数,在selenium里,一般很少会用到,比如:last(),获取子元素的个数,要找某个标签下的最后一个元素,可以这样写"*[lase()]"。name()函数就是获取标签名,"//*[name()='input']",等价于"//input",一般要找标签名以什么开头或是包含什么的时候,可以用这个函数。count()函数,计数用的,"//*[count(input)=2]",意思是所有只含有两个input子元素的元素,当然,除了input子元素,可以有其它子元素,但input子元素有且只有两个。string-length(),顾名思议,获取字符串的长度。"//*[string-length(text())=3]",所有的文本长度为3的元素。position(),获取自身在父元素中的位置,"//input[position()=2]",就是找出所有的在父元素下排第二的input元素。还有,就不一一介绍了,反正在selenium里,很少会用到。
现在,说说相对查找元素的方法。就是先找到一个元素,再相对这个元素,去找到我们需要的元素。以上面写的网页为案例,我们画一个树形图,来描述网页文档的结构
现在假如,我们根据路径,找到了第三个li标签,根据找到的这个li,是不是可以把整个文档分成五部分。首先,第一部分就是它自身。表示自身,用关键字self,写法就是"/html/body/ul/li[3]/self::*",关键字后面加两个冒号,不过,肯定没人会这么写。第二部分,就是li的所有祖先元素,即黄线上的所有元素。使用关键字ancestor,"//li[3]/ancestor::*",即表示黄线上的所有元素,不包括li自身。第三部分就是li的所有子元素,使用关键字descendant,"//li[3]/descendandt::*",这个就代表li的所有子元素,如果想找li的某一类子元素,可以这样写"//li[3]/descendant::input",意思就是找页面上第三个li标签下的所有input标签。还剩下两部分,都应该知道了吧,一个是黄线之上的部分,一个是黄线之下的部分。上面的用关键字preceding,下面的使用关键字following,"//li[3]/preceding::*",就包括head标签及其所有子元素,以及ul标签下的前两个li,包括它们的子元素。"//li[3]/following::*",包括ul下的最后一个li及其所有子元素。
在这些关键字里面,我们一般使用preceding和following比较多。找自身没的说,根本就不会用,找祖先元素,可以使用".."、"../.."这样的,当然,如果要在祖先元素里找一个特定的元素,那就使用ancestor关键字比较方便了。比如找名称为p的祖先元素,那就可以这样写,"./ancestor::p",不过这样的应用场景,在找元素里,很少会遇到。找所有子元素的话,使用"//*"就可以了。
看我们的网页案例,如果输入框内没有placeholder这些文字,要找这个输入框就不好找了,但如果用following,可以通过输入框前面的文本来找到相应的输入框。
@Test
public void test() throws Exception {
// 打开测试页面
driver.get("file:///C:/Users/Administrator/Desktop/test.html");
//通过id找到第一个输入框
WebElement un = driver.findElement(By.xpath("//*[text()='用户名:']/following::input"));
//第一个输入框输入aaa
un.sendKeys("aaa");
//通过className找到第二个输入框
WebElement pw = driver.findElement(By.xpath("//*[text()='密码:']/following::input"));
//第二个输入框输入bbb
pw.sendKeys("bbb");
//通过name找到第三个输入框
WebElement co = driver.findElement(By.xpath("//*[text()='验证码:']/following::input"));
//第三个输入框输入ccc
co.sendKeys("ccc");
Thread.sleep(2000);
//找到页面所有的input标签
WebElement btn = driver.findElement(By.xpath("//*[text()='按钮']"));
//点击按钮
btn.click();
Thread.sleep(3000);
}
运行一下,和之前的效果是一样的。需要注意,xpath找元素,都是基于文档的,不是基于页面展示效果的。在页面上看到一个元素在某一个元素的后面,但在文档里,却不一定是这样。通过绝对定位或是浮动等,都可以改变元素显示的顺序。
对于单选按钮,复选按钮等,则可以使用preceding,具体的演示就不写了,很简单。
preceding和following后面还可以接sibling,则只在当前元素的同一级找。"//li[3]/preceding-sibling::*",意思是页面上第三个li标签之前的所有兄弟元素,不会越过父元素去找。
xpath语法,会用到的差不多就是这些了,当然,xpath还有一些语法,只不过,通过selenium找元素的时候,基本上不会用到,或是使用我们已经掌握的这些语法,可以更好的实现,比如,找父元素有关键字parent,我使用"..",不是更简单么。
欢迎加入测试群:91425817,一起讨论测试的那此事。