selenium定位元素的八种策略

目录

元素定位介绍

在Webdriver中,定位元素由WebDriver实例对象完成,通过find_element(s)_by_locator方法根据不同的定位器locator进行定位,返回另一个基本对象类型WebElement,WebElement对象也可以选择元素。

  • WebDriver 代表浏览器
  • WebElement 表示特定的 DOM 节点(控件,例如链接或输入栏等)

find_element两种表示(以id定位为例):

  • driver.find_element_by_id('kw')
  • driver.find_element(By.ID, 'kw')

element与elements:

  • find_element 只返回第一个符合条件的元素,没有符合元素则抛出异常
  • find_elements 返回所有符合条件的元素,没有符合元素则返回空列表

元素选择策略

在 WebDriver 中有 8 种不同的内置元素定位策略:

定位器 Locator 描述
class name 定位class属性与搜索值匹配的元素(不允许使用复合类名)
css selector 定位 CSS 选择器匹配的元素
id 定位 id 属性与搜索值匹配的元素
name 定位 name 属性与搜索值匹配的元素
link text 定位link text可视文本与搜索值完全匹配的锚元素
partial link text 定位link text可视文本部分与搜索值部分匹配的锚点元素。如果匹配多个元素,则只选择第一个元素。
tag name 定位标签名称与搜索值匹配的元素
xpath 定位与 XPath 表达式匹配的元素

1. 定位器class_name、id、name和tag_name

根据class属性、id属性、name属性、标签名称定位元素。

以百度为例,定位搜索框,HTML页面代码(搜索框为input标签):

<span id="s_kw_wrap" class="bg s_ipt_wr new-pmd quickdelete-wrap">
    <span class="soutu-btn"></span>
    <input type="text" class="s_ipt" name="wd" id="kw" maxlength="100" autocomplete="off">
    <span class="soutu-hover-tip" style="display: none;">按图片搜索</span></span>

定位方式:

driver.find_element_by_class_name('s_ipt')
driver.find_element_by_id('kw')
driver.find_element_by_name('wd')
driver.find_element_by_tag_name('input')

注意实际百度页面input标签不唯一且第一个input标签不是输入框,以上tag name方式无法定位。

根据链接文本和部分链接文本定位元素。

依然以百度为例:

<a href="http://xueshu.baidu.com" target="_blank" class="mnav c-font-normal c-color-t">学术</a>

定位相应超链接文本并点击:

driver.find_element_by_link_text('学术').click()
driver.find_element_by_partial_link_text('术').click()

3. CSS选择器

方法:find_element(s)_by_css_selector(CSS Selector参数)

根据tag name、id、class选择元素:

# 1. tag_name
elements = driver.find_elements_by_css_selector('div')
# 等价于
elements = driver.find_elements_by_tag_name('div')

# 2. id(需要在id前加上'#') #id值
elements = driver.find_elements_by_css_selector('#kw')
# 等价于
elements = driver.find_elements_by_id('kw')

# 3. class(需要在class值前加上'.') .class值
elements = driver.find_elements_by_css_selector('.s_ipt')
# 等价于
elements = driver.find_elements_by_class_name('s_ipt')

根据属性选择元素:

element = driver.find_element_by_css_selector('[href="http://news.baidu.com"]')

element = driver.find_element_by_css_selector('[href]')

# 可以组合
element = driver.find_element_by_css_selector('div[target="_blank"]')

选择子元素和后代元素(可组合使用):

  1. 限定元素1的子元素 元素1 > 元素2 如:#s_kw_wrap > .s_ipt

    elements = driver.find_elements_by_css_selector('#s_kw_wrap > .s_ipt')
    

    支持多层级 元素1 > 元素2 > 元素3 > 元素4

  2. 限定元素1的后代元素 元素1 元素2 如:# form .s_ipt

    支持多层级 元素1 元素2 元素3 元素4

组选择:

用逗号分隔,如:.s_ipt,#kw表示class属性为s_ipt或id为kw

根据次序选择子节点:

p:nth-child(2):选择属于其父元素的第二个子元素的每个p元素。p元素为其父节点的第二个元素。

p:nth-last-child(2):选择属于其父元素的倒数第2个子元素的p元素

p:nth-of-type(2):选择属于其父元素第二个p元素的每个p元素。

p:nth-last-of-type(2):选择属于其父元素倒数第二个p元素的每个p元素。

p:nth-child(even):偶数节点

p:nth-child(odd):奇数节点

选择兄弟节点:

div > p:选择父元素是div元素的所有p元素

div + p:选择紧跟div元素的首个p元素

div ~ p:选择前面有div元素的每个p元素

CSS选择器完整列表,取自w3school

选择器 例子 例子描述
.class .intro 选择 class="intro" 的所有元素。
.class1.class2 .name1.name2 选择 class 属性中同时有 name1 和 name2 的所有元素。
.class1 .class2 .name1 .name2 选择作为类名 name1 元素后代的所有类名 name2 元素。
#id #firstname 选择 id="firstname" 的元素。
* * 选择所有元素。
element p 选择所有 p 元素。
element.class p.intro 选择 class="intro" 的所有 p 元素。
element,element div, p 选择所有 div 元素和所有 p 元素。
element element div p 选择 div 元素内的所有 p 元素。
element>element div > p 选择父元素是 div 的所有 p 元素。
element+element div + p 选择紧跟 div 元素的首个 p 元素。
element1~element2 p ~ ul 选择前面有 p 元素的每个 ul 元素。
[attribute] [target] 选择带有 target 属性的所有元素。
[attribute=value] [target=_blank] 选择带有 target="_blank" 属性的所有元素。
[attribute~=value] [title~=flower] 选择 title 属性包含单词 "flower" 的所有元素。
[attribute|=value] [lang|=en] 选择 lang 属性值以 "en" 开头的所有元素。
[attribute^=value] a[href^="https"] 选择其 src 属性值以 "https" 开头的每个 a 元素。
[attribute$=value] a[href$=".pdf"] 选择其 src 属性以 ".pdf" 结尾的所有 a 元素。
[attribute**=value*] a[href*="w3schools"] 选择其 href 属性值中包含 "abc" 子串的每个 a 元素。
:active a:active 选择活动链接。
::after p::after 在每个 p 的内容之后插入内容。
::before p::before 在每个 p 的内容之前插入内容。
:checked input:checked 选择每个被选中的 input 元素。
:default input:default 选择默认的 input 元素。
:disabled input:disabled 选择每个被禁用的 input 元素。
:empty p:empty 选择没有子元素的每个 p 元素(包括文本节点)。
:enabled input:enabled 选择每个启用的 input 元素。
:first-child p:first-child 选择属于父元素的第一个子元素的每个 p 元素。
::first-letter p::first-letter 选择每个 p 元素的首字母。
::first-line p::first-line 选择每个 p 元素的首行。
:first-of-type p:first-of-type 选择属于其父元素的首个 p 元素的每个 p 元素。
:focus input:focus 选择获得焦点的 input 元素。
:fullscreen :fullscreen 选择处于全屏模式的元素。
:hover a:hover 选择鼠标指针位于其上的链接。
:in-range input:in-range 选择其值在指定范围内的 input 元素。
:indeterminate input:indeterminate 选择处于不确定状态的 input 元素。
:invalid input:invalid 选择具有无效值的所有 input 元素。
:lang(language) p:lang(it) 选择 lang 属性等于 "it"(意大利)的每个 p 元素。
:last-child p:last-child 选择属于其父元素最后一个子元素每个 p 元素。
:last-of-type p:last-of-type 选择属于其父元素的最后 p 元素的每个 p 元素。
:link a:link 选择所有未访问过的链接。
:not(selector) :not(p) 选择非 p 元素的每个元素。
:nth-child(n) p:nth-child(2) 选择属于其父元素的第二个子元素的每个 p 元素。
:nth-last-child(n) p:nth-last-child(2) 同上,从最后一个子元素开始计数。
:nth-of-type(n) p:nth-of-type(2) 选择属于其父元素第二个 p 元素的每个 p 元素。
:nth-last-of-type(n) p:nth-last-of-type(2) 同上,但是从最后一个子元素开始计数。
:only-of-type p:only-of-type 选择属于其父元素唯一的 p 元素的每个 p 元素。
:only-child p:only-child 选择属于其父元素的唯一子元素的每个 p 元素。
:optional input:optional 选择不带 "required" 属性的 input 元素。
:out-of-range input:out-of-range 选择值超出指定范围的 input 元素。
::placeholder input::placeholder 选择已规定 "placeholder" 属性的 input 元素。
:read-only input:read-only 选择已规定 "readonly" 属性的 input 元素。
:read-write input:read-write 选择未规定 "readonly" 属性的 input 元素。
:required input:required 选择已规定 "required" 属性的 input 元素。
:root :root 选择文档的根元素。
::selection ::selection 选择用户已选取的元素部分。
:target #news:target 选择当前活动的 #news 元素。
:valid input:valid 选择带有有效值的所有 input 元素。
:visited a:visited 选择所有已访问的链接。

4. XPath表达式

以百度搜索输入栏为例

  • 绝对路径:/表示

    /html/body/div[1]/div[2]/div[5]/div[1]/div/form/span[1]/input

  • 相对路径://表示

    //input ,当前存在20个input标签,不唯一,需要其他信息

    • 相对路径 + 索引定位

      找到上级中唯一的标签,//form/span[1]/input

    • 相对路径 + 属性定位

      找到唯一属性值,//input[@autocomplete="off"]

    • 相对路径 + 通配符定位(Chrome复制xpath方式)

      任意标签下的唯一属性值,//*[@autocomplete="off"]//*[@*="off"]

      复制xpath经常会出错,不是万能的

    • 相对路径 + 部分属性值定位

      以of开头://input[starts-with(@autocomplete,"of")]

      以ff结尾://*[substring(@autocomplete,2)='ff']从第2个字符开始的值为ff

      包含: //*[contains(@autocomplete,'of')]

    • 相对路径 + 文本定位

      非超链接文本://span[text()='按图片搜索']

XPath语法部分,选自w3school

选取节点:

表达式 描述
nodename 选取此节点的所有子节点。
/ 从根节点选取,绝对路径。
// 从匹配选择的当前节点选择文档中的节点,相对路径。
. 选取当前节点。
.. 选取当前节点的父节点。
@ 选取属性。

以XML文档为例:

<?xml version="1.0" encoding="ISO-8859-1"?>

<bookstore>

<book>
  <title lang="eng">Harry Potter</title>
  <price>29.99</price>
</book>

<book>
  <title lang="eng">Learning XML</title>
  <price>39.95</price>
</book>

</bookstore>
路径表达式 结果
bookstore 选取 bookstore 元素的所有子节点。
/bookstore 选取根元素 bookstore。注释:假如路径起始于正斜杠( / ),则此路径始终代表到某元素的绝对路径!
bookstore/book 选取属于 bookstore 的子元素的所有 book 元素。
//book 选取所有 book 子元素,而不管它们在文档中的位置。
bookstore//book 选择属于 bookstore 元素的后代的所有 book 元素,而不管它们位于 bookstore 之下的什么位置。
//@lang 选取名为 lang 的所有属性

选取指定节点

路径表达式 结果
/bookstore/book[1] 选取属于 bookstore 子元素的第一个 book 元素。
/bookstore/book[last()] 选取属于 bookstore 子元素的最后一个 book 元素。
/bookstore/book[last()-1] 选取属于 bookstore 子元素的倒数第二个 book 元素。
/bookstore/book[position()<=2] 选取最前面的两个属于 bookstore 元素的子元素的 book 元素。
//title[@lang] 选取所有拥有名为 lang 的属性的 title 元素。
//title[@lang='eng'] 选取所有 title 元素,且这些元素拥有值为 eng 的 lang 属性。
/bookstore/book[price>35.00] 选取 bookstore 元素的所有 book 元素,且其中的 price 元素的值须大于 35.00。
/bookstore/book[price>35.00]/title 选取 bookstore 元素中的 book 元素的所有 title 元素,且其中的 price 元素的值须大于 35.00。

选取未知节点

通配符 描述
* 匹配任何元素节点。
@* 匹配任何属性节点。
node() 匹配任何类型的节点。
路径表达式 结果
/bookstore/* 选取 bookstore 元素的所有子元素。
//* 选取文档中的所有元素。
//title[@*] 选取所有带有属性的 title 元素。

组选择,用"|"运算符表示:

路径表达式 结果
//book/title | //book/price 选取 book 元素的所有 title 和 price 元素。
//title | //price 选取文档中的所有 title 和 price 元素。
/bookstore/book/title | //price 选取属于 bookstore 元素的 book 元素的所有 title 元素,以及文档中所有的 price 元素。

XPath轴

轴名称 结果
ancestor 选取当前节点的所有先辈(父、祖父等)。
ancestor-or-self 选取当前节点的所有先辈(父、祖父等)以及当前节点本身。
attribute 选取当前节点的所有属性。
child 选取当前节点的所有子元素。
descendant 选取当前节点的所有后代元素(子、孙等)。
descendant-or-self 选取当前节点的所有后代元素(子、孙等)以及当前节点本身。
following 选取文档中当前节点的结束标签之后的所有节点。
namespace 选取当前节点的所有命名空间节点。
parent 选取当前节点的父节点。
preceding 选取文档中当前节点的开始标签之前的所有节点。
preceding-sibling 选取当前节点之前的所有同级节点。
self 选取当前节点。

步的实例:

轴名称::节点测试[谓语]

例子 结果
child::book 选取所有属于当前节点的子元素的 book 节点。
attribute::lang 选取当前节点的 lang 属性。
child::* 选取当前节点的所有子元素。
attribute::* 选取当前节点的所有属性。
child::text() 选取当前节点的所有文本子节点。
child::node() 选取当前节点的所有子节点。
descendant::book 选取当前节点的所有 book 后代。
ancestor::book 选择当前节点的所有 book 先辈。
ancestor-or-self::book 选取当前节点的所有 book 先辈以及当前节点(如果此节点是 book 节点)
child::*/child::price 选取当前节点的所有 price 孙节点。

(以上:*在Markdown格式显示成了emoji,所以用代码表示了)

选择定位器

如果HTML的 id 唯一、可用、可预测,优先选择id定位。

如果没有唯一的id,优先使用CSS选择器查找元素。

XPath表达式类似CSS选择器,功能强大、非常灵活,但语法复杂、很难调试,通常未经过浏览器厂商的性能测试,并且运行速度很慢。在定位元素时,建议优先使用CSS选择器,CSS不好解决再考虑XPath。

链接文本和部分链接文本的选择策略只能作用于链接元素,且它们在WebDriver内部调用XPath选择器。

由于页面上经常出现同一标签的多个元素,标签名定位元素比较危险,但是在调用find_elements方法返回元素集合的时候非常有用。

建议尽可能保持定位器的紧凑性和可读性。使用 WebDriver 遍历 DOM 结构是一项性能花销很大的操作,搜索范围越小越好。

上一篇:2021-10-18 数学建模小结


下一篇:XPath 与 CSS 定位方式