XPath 选择器 在上一篇名为《入门基于》的文章中, 当咱们讲解那个东西的入门知识的时候, 挖了好几个大坑, 其中一个就是这篇文章所讲的。在当今的Web自动化测试领域, XPath与CSS选择器的使用频率非常高。借助这两种工具, 测试人员能够非常准确地找到需要操作的页面元素。只要掌握了其中任选一种, 编写自动化测试脚本这项任务就可以被完成得相当出色。在这篇内容里, 我会专门从 web 自动化测试这个视角, 来好好讲一讲这两个选择器是怎么回事, 至于和其他方面没啥关系的功能部分, 我大概率就不展开去说, 碰到那些需要解释各自专用术语的地方, 我也可能不会说得那么深入细致, 原因, 毕竟想要把这个东西彻底搞明白确实也不是一件特别轻松容易的事情。由于内容的篇幅确实过长, 因此, 我便将这个两个选择器的说明事宜, 分开进行详细的阐述与解读。尽管已经做了如此分离的处理, 但是, 所涉及的内容依然属于非常复杂的范畴, 所以, 还请大家能够保持一颗耐心, 并且仔细地、不遗漏任何细节地进行阅读。简介XPath 它是一门那个语言, 专门用来在 XML 文档里面查找那些信息的, 它可以被用到在 XML 文档当中, 去对元素以及属性进行遍历。HTML 这个东西, 可以被看作是那种不太严格的 XML 格式, 所以呢, 它也是能够直接用 XPath 工具来查元素所在的。了解元素在这两个选择器的具体情况被我们弄明白之前, 我们有必要先把那些存在于HTML标签之中的各个元素相互之间所存在的那些关系给清楚, 先把它给搞清楚。元素、属性和, 以及对应的值。我们最开始的部分, 是从一个非常简单的html标签开始进行的:text在上面的那一段内容之中, 整个的这个叫作div的这种标签它就是一个独立的元素, 然后这个div, 它就是标签的具体名字。该元素所具备的属性, 具体来说包含两个。那个东西里的文字内容就是 text 这个玩意儿。元素间的关系元素之间的关系类型, 主要可以分为以下几种情况: 一个是自身或者叫self, 第二个是先辈关系里面括号是空的和后代关系里面括号也是空的, 第三个是父亲角色和叫做child的孩子角色之间的父子关系这里括号为空, 第四个是同胞关系这里括号是空的, 第五个是前面的状态和后面的状态前后关系这两个括号里都是空的。这些关系在字面上面看来, 大家是应该比较容易去理解的, 我这边主要来说明了先辈或者的后代与父子关系它们之间有什么样的一个区别, 父子关系仅仅只是包括那些直接从属的关系而言, 而先辈或者是后代这种关系它不仅仅是包括了直接从属的部分内容了, 并且它还包含了那些间接从属的关系在其中。我们要去查看一个相当完整且相对基础的html标签代码内容, 在此之后所展开的大部分讲述环节, 将会主要围绕这份具体的示例来进行展开说明。...11-11-21-31-3-11-3-1-11-3-1-21-3-21-3-2-11-3-2-21-3-31-3-3-11-41-523针对id属性值为test的那个li元素。以该元素为例它的父元素, 是为 an_1 的 ul 那个元素。它的先辈元素, 除了父元素之外, 还有以下几种情况。那个父元素, 还有那些先辈的元素。它的子元素有它的那个后代成分, 除了那个子成分以外, 还有:子元素与后代元素它的那些同胞元素方面。在那东西后面紧跟着的、跟它属于同一类的其他东西, 有这些:同胞元素在基本选取元素的时候, 我们通常采用的是绝对路径与相对路径这种方式。首先咱们得说说 XPath, 这个玩意儿分绝对路和相对路两种情况。你要看路径是不是以一个斜杠开头, 如果是那个样子的话, 那它就是绝对路径了要是没有这么干的话, 那就只能算作是相对路径。绝对路径这个说法, 它是跟整个HTML代码进行对比而言的。相对路径这个东西, 它其实是有使用范围的, 也就是说, 只能在特定的情况下进行使用, 比方说在像某某标签这样的场景之中, 针对某个具体实例来进行引用的时候。a driver.find_element(By.XPATH, //div[idcontent_left])b a.find_elements(By.XPATH, div[not(tplrecommend_list)]//h3)上面的那一份代码里头, 变量它是采用的是绝对的地址路径, 而变量它用的是相对的地址路径。标签如果想要查到某个带有特定标签名的元素, 那么用户只需要把这个标签名输入进去就完事了, 这样的例子有很多,比方说像是这种类型的标签以及这种类型的标签都是可以的。全体表示全体元素所使用的符号是*, 其作用为选取当前所选中元素的下面层次中的所有元素, 不论这些元素的标签名称是什么样的, 均可被选取到。如果使用绝对路径查询全体astrongcode/*/code/strong/a通常情况下, 大家都会把目光锁定在html元素上面。属性在标签的后面加上中括号, 在中括号的内部写上该元素的属性信息, 这样一来就可以表示选出那些符合指定条件的元素对象。下标您可以在属性里填上具体数字, 那个数字代表的是第几个元素。你要搞清楚一点, 在XPath里面进行下标定位的时候, 是从编号1开始计算的, 这个特点和绝大多数的编程语言是完全不一样的。如astrongcodediv[3]/code/strong/a它用来表示选择的是第三个 div 元素, 更确切地说, 是那个位于特定路径里面的div 元素列表中的第三个。参数我们需要在那些参数名称的前面加上一个符号, 然后把它们写到属性里面, 这样做是表示我们要选取具有这些参数的元素, 举个例子来说就是:astrongcodediv[id]/code/strong/a这句话的意思就是, 你要去把那些拥有id属性值的div标签给选出来。“参数名值”这种写法, 是用来表示选取那些参数等于某个特定值的元素, 就例如像这样子的:astrongcodeli[idtest]/code/strong/a指的是去选取那个id属性值是test的li元素。文本值使用单引号进行包裹是完全可行的, 同时, 使用双引号进行包裹也是完全正确的, 其核心要点在于必须保持整体风格上的一致性, 至于最终的选择, 这主要取决于个人的操作习惯, 然而, 倘若你需要将该用法投入到具体的编程环境中去应用, 那么最佳策略是选用与你平时用于包裹其他字符串时所使用的那些引号符号完全不同的另外一种引号类型, 这样做能够有效地免去后续处理中可能出现的字符转义所带来的各类麻烦:astrongcodedriver.find_element(By.XPATH, //div[idcontent_left])/code/strong/adriver.find_element(By.XPATH, //div[idcontent_left])子元素与后代元素如果想要查询到元素a这个父元素下面的子元素b的话, 就可以采取这样的操作: 在元素a也就是父元素这个位置后面接上一个斜杠符号, 然后再接上子元素b这个名字。astrongcodea/b/code/strong/a这种操作模式是能够持续不断地执行下去的。假如用户想要去查询那些属于元素a的后代范畴以内的元素b, 那么就需要将符号//接在后面进行表示。astrongcodea//b/code/strong/a在许多的场景之中, 大家往往会在一段关于XPath的语句的最开始部分使用它, 通过这样的方式能够直接地对具体的元素进行定位操作, 从而完全避免了去书写像包含html标签还有body标签等等在内的、位于该元素前面的一些完全没有意义的层级结构信息, 这种做法能够有效地节约掉项目开发过程中的不必要的时间, 同时也能够让整体的代码可读性得到更加显著的提升, 就比如说在一个示例里面, 当我们需要来对一个id属性值被设置为test的li标签元素执行定位动作的时候, 可以直接采用下面这种简便的方式进行编写。astrongcode//li[idtest]/code/strong/a因此, 不应该被写成如下的形式:astrongcode/html/body/div[1]/ul/li[idtest]/code/strong/a这种情况有一个前提条件, 这个前提条件就是必须要能够实现精确定位。父元素如果你打算去查询一下某个元素的父级元素的话, 那么是可以使用双点的形式来表示的, 具体的情况例如下面这样。astrongcode//li[idtest]/../li[1]/code/strong/a我们在示例里头, 先找到那个id叫做test的li元素, 然后去拿它的那个父元素, 接着再从它的子元素里面挑出第一个来, 这个被选中的子元素的id是si_1。聪明的你肯定已经注意到了, XPath这种写法和文件路径的表示方式有着非常相似的地方, 所以我们有时候就会把这种东西称为“XPath 路径”同时应该也可以想到, 符号 . 是用来表示自己这个意思的。关于运算以及那些常用的函数, 还有基本的那些运算。在XPath语法里面, 它是支持加法、减法、乘法还有除法这些运算的, 另外也包含了像加号或者减号, 还包括用div来进行除以的情况, 同时它也涵盖了能够进行取余数操作的这种叫做mod这样的东西, 此外它还支持用来进行比较的这种相等于或者说不等于等等以及小于以及大于这样的符号, 它还可以实现像是与或者是或者是非这样的一些逻辑判断的运算功能。请大家特别留心, 在 XPath 这个里头, 除法得用 div 来代替呢, 取余数的话是用 mod, 要是表示等于, 那只需要写一个等号就能完事儿了。至于说到逻辑运算那边, “与”和“或”这两个分别使用 and 还有 or 来进行连接操作, 而说到了“非”, 那是得使用 not() 这个函数来表示出来的。如astrongcode//li[not(index div alt 6)]/code/strong/a选择出那些不符合 index 属性除以 alt 属性的结果小于 6 这一条件的 li 元素。要是有一个 li 元素的这些属性没法进行计算, 比如里面是文本形式的字符串, 又或者少了这两个属性里的某一个, 那么该 li 元素也会被选中。假如把这段描述放到以下的 HTML 代码里去执行:12345678910111213所以啊, 那些文字内容是1和7的li元素, 它是不会被选中的, 剩下的别的元素都会被选中。你可以使用圆括号来提高运算的优先级, 不过这种情况用得并不是特别多, 基本上是在包裹整个路径的时候用到了, 另外就是在属性里面使用。选取第几个元素前面我们说了如何选取第几个元素, 现在说点高级的。从序列里面挑出排在末尾的那个东西, 用方括号把last这个函数装起来就行。astrongcode//li[idtest]/../li[last()]/code/strong/a选择的是示例当中那个标签的编号是 test 的 li 元素, 然后找到它的那个父元素, 最后再从这个父元素里面选出最后一个作为子元素的 li 元素, 这一系列操作下来得到的是一个标签的编号为 si_4 的元素。若是想要挑选出倒数第几位置的那一个对象, 照样可以照此方法去处理, 比如:astrongcode//li[idtest]/../li[last()-1]/code/strong/a选取的对象, 是示例里面id标记为test的那个li标签元素的父级元素的倒数第二个子li元素, 具体来说, 就是id标记为si_3的那个元素。在方括号里面, 你仍然可以用括号包围的函数这个词来指代被选中的元素的位置或者编号, 并且这种做法是可以和之前的那些限定性的条件放在一起配合使用的。举个例子吧:astrongcode//li[idtest]/../li[position()3]/code/strong/a我们进行的选择操作, 是针对那个示例里面, 它的 id 属性等于 test 的那个 li 元素来说的, 先去找到这个 li 元素的父级元素, 然后从这个父级元素当中, 再把它的子元素里面的前两个 li 子元素给拿出来, 这拿出来的这两个元素呢, 它们的 id 分别就是 si_1, 还有那个是 si_2 的元素。选取多个路径您可以把多个路径用竖线这个符号给它连接起来, 这样做是为了表示说去选择那些能够满足其中任意一个路径条件的那些元素。比如说:astrongcode//ul[idde_1] | //li[idsi_3]/code/strong/a选取的是示例中 id属性为 de_1的 ul元素, 以及 id属性为 si_3的 li元素。文字text() 这个函数是用来表示元素的文字的, 在 Web 自动化测试的时候, 它常被用来定义属性, 举个例子来说如:astrongcode//li[text()1-3-3-1]/code/strong/a选择的是文本内容为“1-3-3-1”的li元素, 这个li元素的id是de_8。注意一下, 这个函数抓取的文本内容不包含后代元素里的文字部分, 要是里面出现换行符号、空格还有缩进, 那些也都会计数进去。假使换行位或者缩进位中间被其他的元素给隔开了, 那结果也会拆成两个分开的片段来进行计算。以下就是对示例代码执行该操作之后的情况。astrongcode//li[idtest]/text()/code/strong/a实际上我们进行了挑选, 最终选取了四处:astrongcode $x(//li[idtest]/text())/code/strong/a(4) [text, text, text, text] $x(//li[idtest]/text())[0].textContent1-3\n $x(//li[idtest]/text())[1].textContent\n $x(//li[idtest]/text())[2].textContent\n $x(//li[idtest]/text())[3].textContent\n 所以呢, 要是你把下面这一条语句给执行了的话, 你就压根儿也选取不到任何的内容。astrongcode/li[text1-3]/code/strong/a把包含关系给搞清楚, 然后再去进行连接的这一系列操作, 同时也需要从中去选定并提取那些指定的类名下所拥有的具体内容。函数是这么写的, (a, b), 这个表示的意思就是 a 当中包含了 b。在选取带有class属性值的元素的时候, 经常会碰到这么一种情况, 就是某个元素的class属性里面包含了不止一个值, 而是有多个值都存在的情况:texttexttext在当前的操作过程当中, 必须从所有的HTML元素里面, 挑选出那些div标签, 并且这些div标签的class这个属性的值当中, 包含有ding-class这一段文字信息, 如果采用。astrongcode//div[classding-class]/code/strong/a它的实际处理逻辑就是, 只去把那一个最靠前头的元素给挑出来。当运用像下面这样的语句时, 就可以把这三个元素给选出来。astrongcode//div[contains(class, ding-class)]/code/strong/a这里所要表示出来的那个 div 元素, 它在 class 这个属性里面, 是包含了 ding-class 这一部分内容的。然而, 这个状况还不算是完全妥当, 好比说下面所提及的这一点情形:texttext运用上文所述语句, 就会达成将这两个元素一并选中的效果, 而这一结果并非是我们所期望的。我们有一个取巧的方法, 在两个查询文本左右都加上空格。给 ding-class 加上空格非常简单, 只要对字符串两边加上空格就可以了但是要给 class 加上空格的话, 就要用到 () 函数了。(a, b, ...) 函数表示连接括号里面的字符串。对于前面的这些需要, 如果要把功能实现的话, 只需将相应的代码部分修改成为这种形式即可。astrongcode//div[contains(concat( , class, ), ding-class )]/code/strong/a这样的话, 就可以做到精确的选取了。是以某个特定的字符串作为开头的。使用-with(a, b) 这种方式来表达的是, a是以b作为开头的部分。因为这和包含的关系看起来是一样的, 所以这里就不再多说其他的了。关于那些属于同一个父级的兄弟姐妹元素, 以及作为祖先的上层元素这种情况呢, 咱们现在能够去借助一个被叫做“轴”的这一概念来加以处理。关于轴的这些方面, 它的用法是比较完整的, 具体如下。astrongcode 之前的元素/轴::标签[条件或下标]/code/strong/a在之前的那个元素的轴里面, 选出那些符合一定条件或者是指定下标的地方所对应的标签。针对Web自动化测试而言, 与之相关的轴包括如下这些。重点还是在于后面那两个, 毕竟它们的实用程度更高一些。如果说直接告诉你答案, 可能你还是比较难懂, 那么你还是看一下具体给你准备的这个示例吧。依旧沿用先前那个实例, 采取下面这段文字内容:astrongcode//li[idtest]/preceding-sibling::*/code/strong/a这句话的意思是指在选取示例的时候, 需要找到那个 id 标签的值叫做 test 的 li 元素, 然后选定在这个 li 元素的前面, 所有的与它属于同一级的同胞元素, 具体来说嘛, 就是那两个 id 标识分别为 si_1, 和 si_2 的 li 元素。只是有一点是必须要给大家提醒一下的, 如果情况里涉及到下标线这个概念的话, 它是要去根据那个具体的元素的实际位置, 然后由近到远来执行排序操作的, 这绝对不是按照你们写的代码里的先后顺序去进行排序的。比如这样的:astrongcode//li[idtest]/preceding-sibling::*[1]/code/strong/a我们选取了id为si_2的li元素, 原因在于这个元素距离id为test的那个li元素的最近。关于书写选择器文本这件事, 掌握使用工具的诀窍是非常关键的。在通常用的浏览器里, 开发人员那个工具都支持刚才说的那两种选择器。你可以去看看之前介绍过的文章, 按里面的方法在浏览器里面调试选择器文本。我们在浏览器里面去调试那个选择器所对应到的文本内容。需要避免使用那种自动生成出来的东西, 还有层级非常多的结构, 同时也不能在很多地方都用上标或者下标的这种嵌套方式。因为Web框架会改变层级, 也会改变元素, 所以这种查询语句, 经常找不到想要的元素。建议用一些特别的特征, 去确定位置。例如: 。astrongcode//*[idpost-XPath]/div[1]/main/div[2]/div/div[2]/div/p/code/strong/a//h3[idroot-node]/../div[classbody]/p后者显然要优于前者。