Selenium教程系列(五):xpath进阶

现在介绍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,一起讨论测试的那此事。

发布了47 篇原创文章 · 获赞 9 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/kingzhsh/article/details/100795497