2023-01-30 17:51:18 +08:00

27 KiB
Raw Blame History

本节介绍如何获取元素对象。可以从页面中查找元素,也可以在元素中查找子元素。

无论是数据采集还是页面自动化,定位元素都是重中之重的的技能,浏览器开发者工具虽然可以直接复制绝对 xpath 或 css 路径,但这样做一来代码繁琐,可读性低,二来难以应付动态变化的页面。
本库提供一套简洁易用的语法,用于快速定位元素,并且内置等待功能、支持链式查找,减少了代码的复杂性。

定位元素大致分为三种方法:

  • 在页面或元素内查找子元素
  • 根据 DOM 结构相对定位
  • 根据页面布局位置相对定位

浏览器的元素还有专门用于处理 shadow dom 的shadow_root属性。获取到的元素可继续用这些方法获取后代元素,使用方法和普通元素一致。

✔️ 示例

先看一些示例,后面在详细讲解用法。

📍 简单示例

假设有这样一个页面:

<html>
<body>
<div id="one">
    <p class="p_cls" name="row1">第一行</p>
    <p class="p_cls" name="row2">第二行</p>
    <p class="p_cls">第三行</p>
</div>
<div id="two">
    第二个div
</div>
</body>
</html>

我们用页面对象去获取其中的元素:

# 获取 id 为 one 的元素
div1 = page.ele('#one')

# 获取 div1 元素内所有 p 元素组成的列表
p_eles = div1.eles('tag:p')

# 获取 name 属性为 row1 的元素
p1 = page.ele('@name=row1')

# 获取 name 属性为 row2 且包含“第二”文本的 p 元素
p_ele = page.ele('tag:p@@text():第二@@name=row2')

# 获取包含“第二个div”文本的元素
div2 = page.ele('第二个div')

# 用 xpath 查找
div2 = page.ele('xpath://div[@id="tow"]')

# 从第一行元素用相对定位获取第三行元素
p3 = p1.next(2)

# 获取 p1 元素的父元素
parent = p1.parent()

# 获取 p1 后面的第一个 div 元素
div2 = p1.after(1, 'tag:div')

📍 实际示例

复制此代码可直接运行查看结果。

from DrissionPage import WebPage

page = WebPage('s')
page.get('https://gitee.com/explore')

# 获取包含“全部推荐项目”文本的 ul 元素
ul_ele = page.ele('tag:ul@@text():全部推荐项目')  

# 获取该 ul 元素下所有 a 元素
titles = ul_ele.eles('tag:a')  

# 遍历列表,打印每个 a 元素的文本
for i in titles:  
    print(i.text)

"""
输出:
全部推荐项目
前沿技术
智能硬件
IOT/物联网/边缘计算
车载应用
……
"""

✔️ 查找元素方法

📍 查找单个元素

查找单个元素使用ele()方法。

页面对象和元素对象都拥有此方法,用于查找第一个匹配条件的元素。
d 模式下返回ChromiumElement对象s 模式下返回SessionElement对象,没有找到返回None
在元素下查找子元素时,还可以用 xpath 获取元素属性,直接返回属性文本。
页面对象和元素对象的ele()方法参数名称稍有不同,但用法一样。

参数:

  • loc_or_str(元素对象):元素的定位信息,可以是 loc 元组,或查询字符串
  • loc_or_ele页面对象元素的定位信息可以是元素对象loc 元组,或查询字符串
  • timeout查找元素超时时间默认与元素所在页面等待时间一致s 模式下无效

返回: s 模式下返回SessionElementd 模式下返回ChromiumElement;或用 xpath 获取到的属性值

from DrissionPage import WebPage

page = WebPage()

# 在页面内查找元素
ele1 = page.ele('search text')

# 在元素内查找后代元素
ele2 = ele1.ele('search text')

# 使用 xpath 获取后代中第一个 div 元素的 class 属性(元素内查找可用)
ele_class = ele1.ele('xpath://div/@class')

📍 查找多个元素

查找多个元素使用eles()方法。

此方法与ele()相似,但返回的是匹配到的所有元素组成的列表,用 xpath 获取元素属性时,返回属性文本组成的列表。

参数:

  • loc_or_str:元素的定位信息,可以是 loc 元组,或查询字符串
  • timeout查找元素超时时间默认与元素所在页面等待时间一致s 模式下无效

返回: s 模式下返回SessionElement组成的列表d 模式下返回ChromiumElement组成的列表;或用 xpath 获取到的属性值组成的列表

# 获取 ele 元素内的所有 p 元素
p_eles = ele.eles('tag:p')
# 打印第一个 p 元素
print(p_eles[0])  

📍 查找单个静态元素

静态元素即 s 模式的SessionElement元素对象,是纯文本构造的,因此用它处理速度非常快速。对于复杂的页面,要在成百上千个元素中采集数据时,转换为静态元素可把速度提升几个数量级。作者曾在采集的时候,用同一套逻辑,仅仅把元素转换为静态,就把一个要 30 秒才采集 完成的页面,加速到零点几秒完成。
我们甚至可以把整个页面转换为静态元素,再在其中提取信息。 当然,这种元素不能进行点击等交互。
s 模式下所有元素本身就是静态元素d 模式下,用s_ele()可在把查找到的动态元素转换为静态元素输出,或者获取元素或页面本身的静态元素副本。

s_ele()方法

页面对象和元素对象都拥有此方法,用于查找第一个匹配条件的元素,获取其静态版本。

页面对象和元素对象的s_ele()方法参数名称稍有不同,但用法一样。

此方法用于在一个元素下查找后代元素,以SessionElement形式返回结果xpath 获取属性值时依然是返回str),也可以直接将一个元素或页面转换为SessionElement版本。

这是为了 d 模式处理速度的提升,当爬取复杂页面信息而且不须要和元素进行交互时,生成整个页面或者主要容器元素的SessionElement,再在其中获取信息,可以将速度提升几个数量级。

s 模式下这个方法和ele()是一样的。

参数:

  • loc_or_str(元素对象):元素的定位信息,可以是 loc 元组,或查询字符串。为None时直接返回当前元素的SessionElemnet版本
  • loc_or_ele(页面对象):元素的定位信息,可以是 loc 元组,或查询字符串。为None时直接返回当前页面的 SessionElemnet版本

返回:SessionElement,或用 xpath 获取到的属性值

!>注意:
页面对象和元素对象的s_ele()方法不能搜索到在 frame 里的元素,页面对象的静态版本也不能搜索 frame 里的元素。要使用 frame 里元素的静态版本,可先获取该元素,再转换。而使用ChromiumFrame对象,则可以直接用s_ele()查找元素,这在后面章节再讲述。

from DrissionPage import WebPage

p = WebPage()

# 在页面中查找元素,获取其静态版本
ele1 = page.s_ele('search text')

# 在动态元素中查找元素,获取其静态版本
ele = page.ele('search text')
ele2 = ele.s_ele()

# 获取页面元素的静态副本(不传入参数)
s_page = page.s_ele()

# 获取动态元素的静态副本
s_ele = ele.s_ele()

# 在静态副本中查询下级元素因为已经是静态元素用ele()查找结果也是静态)
ele3 = s_page.ele('search text')
ele4 = s_ele.ele('search text')

📍 查找多个静态元素

查找多个静态元素使用s_eles()方法。

此方法与s_ele()相似,但返回的是匹配到的所有元素组成的列表,或属性值组成的列表。

参数:

  • loc_or_str:元素的定位信息,可以是 loc 元组,或查询字符串(必填)

返回:SessionElement组成的列表,或用 xpath 获取到的属性值组成的列表

from DrissionPage import WebPage

p = WebPage()
for ele in p.s_eles('search text'):
    print(ele.text)

📍 获取当前焦点元素

使用方法active_ele()获取页面上焦点所在元素,为 d 模式独有。

ele = page.active_ele

📍 获取 shadow_root

d 模式元素如果包含 shadow_root可使用shadow_root属性获取。
该属性返回的是一个ChromiumShadowRootElement对象,用法与ChromiumElement相似。也能使用各种元素查找方式,返回内部元素或相对位置元素,返回 ChromiumElement元素。返回的ChromiumElement和普通的没有区别。

# 获取一个元素下是 shadow root
shadow_ele = ele.shadow_root
# 获取该 shadow root 下的一个元素
ele1 = shadow_ele.ele('search text')
# 点击获取到的元素
ele1.click()  

✔️ 查找语法

我们使用一套简洁高效的语法去定位元素,大大简化了定位元素的代码量,增强了功能,也兼容 css selector、xpath、selenium 原生的 loc 元组s 模式也能用。d 模式和 s 模式定位元素的语法是完全一样的,便于模式切换时平滑过渡。

匹配模式 指字符串是否完全匹配,有以下两种:

📍 精确匹配符 =

表示精确匹配,匹配完全符合的文本或属性。

📍 模糊匹配符 :

表示模糊匹配,匹配含有某个字符串的文本或属性。

关键字 是出现在定位语句最左边,用于指明该语句以哪种方式去查找元素,有以下这些:

📍 id 匹配符 #

表示id属性,只在语句最前面且单独使用时生效,可配合=:

# 在页面中查找 id 属性为 ele_id 的元素
ele1 = page.ele('#ele_id')

# 在 ele1 元素内查找 id 属性包含 ele_id 文本的元素
ele2 = ele1.ele('#:ele_id')  

📍 class 匹配符 .

表示class属性,只在语句最前面且单独使用时生效,可配合=:

# 查找 class 属性为 ele_class 的元素
ele2 = ele1.ele('.ele_class')

# 查找 class 属性包含 ele_class 文本的元素
ele2 = ele1.ele('.:ele_class')  

📍 单属性匹配符 @

表示某个属性,只匹配一个属性。
@关键字只有一个简单功能,就是匹配@后面的内容,不再对后面的字符串进行解析。因此即使后面的字符串也存在@@@,也作为要匹配的内容对待。

!> 注意:
如果属性中包含特殊字符,如包含@,用这个方式不能正确匹配到,须使用 css selector 方式查找。且特殊字符要用\转义。

# 查找 name 属性为 ele_name 的元素
ele2 = ele1.ele('@name=ele_name')

# 查找 name 属性包含 ele_name 文本的元素
ele2 = ele1.ele('@name:ele_name')

# 查找有 name 属性的元素
ele2 = ele1.ele('@name')

# 查找没有任何属性的元素
ele2 = ele1.ele('@')

# 查找 emaile 属性为 abc@def.com 的元素,有多个 @ 也不会重复处理
ele2 = ele1.ele('@email=abc@def.com')  

# 属性中有特殊字符的情形匹配abc@def属性等于v的元素
ele2 = ele1.ele('css:div[abc\@def="v"]')

📍 多属性匹配符 @@

表示某个属性,多属性匹配时使用,个数不限。还能匹配要忽略的元素,匹配文本时也和@不一样。
@@后跟 - 时,表示 not。如

  • @@-name表示匹配没有name属性的元素

  • @@-name=ele_name表示匹配name属性不为ele_name的元素

如有以下情况,不能使用此方式,须改用 xpath 的方式:

  • 匹配文本或属性中出现@@

  • 属性名本身以-开头

!> 注意:
如果属性中包含特殊字符,如包含@,用这个方式不能正确匹配到,须使用 css selector 方式查找。且特殊字符要用\转义。

# 查找 name 属性为 name 且 class 属性包含 cls 文本的元素
ele2 = ele1.ele('@@name=name@@class:cls')

# 查找没有 class 属性的元素
ele2 = ele1.ele('@@-class')

# 查找 name 属性不包含 ele_name 的元素
ele2 = ele1.ele('@@-name:ele_name')

📍 文本匹配符 text

要匹配的文本,查询字符串如开头没有任何关键字,也表示根据传入的文本作模糊查找。
如果元素内有多个直接的文本节点,精确查找时可匹配所有文本节点拼成的字符串,模糊查找时可匹配每个文本节点。

# 查找文本为 some text 的元素
ele2 = ele1.ele('text=some text')

# 查找文本包含 some text 的元素
ele2 = ele1.ele('text:some text')

# 与上一行一致
ele2 = ele1.ele('some text')  

?> Tips
若要查找的文本包含text: ,可下面这样写,即第一个text: 为关键字,第二个是要查找的内容:

ele2 = page.ele('text:text:')

📍 文本匹配符 text()

作为查找属性时使用的文本关键字,必须与@@@配合使用。

# 查找文本为 some text 的元素
ele2 = ele1.ele('@text()=some text')

# 查找文本包含 some text 的元素
ele2 = ele1.ele('@text():some text')

# 查找文本为 some text 且 class 属性为 cls 的元素
ele2 = ele1.ele('@@text()=some text@@class=cls')

# 查找文本为 some text 且没有任何属性的元素(因第一个 @@ 后为空)
ele2 = ele1.ele('@@@@text():some text')

# 查找直接子文本包含 some text 字符串的元素,和 text 关键字没有区别
ele = page.ele('@text():some text')
ele = page.ele('text:some text')

# 查找元素内部包含 some text 字符串的元素
ele = page.ele('@@text():some text')

须要注意的是,'text=xxxx''@text()=xxxx'使用上是有细微差别的。

text=表示在元素的直接子文本节点中匹配,@text()=会忽略一些文本标签,在整个元素的内容里匹配。

📍 类型匹配符 tag

表示元素的标签,只在语句最前面且单独使用时生效,可与@@@配合使用。tag:tag=效果一致。

# 定位 div 元素
ele2 = ele1.ele('tag:div')

# 定位 class 属性为 cls 的 div 元素
ele2 = ele1.ele('tag:div@class=cls')

# 定位文本为 text 的 div 元素
ele2 = ele1.ele('tag:div@text()=text')

# 定位 class 属性为 cls 且文本为 text 的 div 元素
ele2 = ele1.ele('tag:div@@class=cls@@text()=text')

# 查找直接文本节点包含 text 字符串的 div 元素
ele2 = ele1.ele('tag:div@text():text')

# 查找内部文本节点包含 text 字符串的 div 元素
ele2 = ele1.ele('tag:div@@text():text')  

?> Tips:
注意, tag:div@text():texttag:div@@text():text 是有区别的,前者只在div的直接文本节点搜索,后者搜索div的整个内部。

📍 css selector 匹配符 css

表示用 css selector 方式查找元素。css:css=效果一致。

# 查找 div 元素
ele2 = ele1.ele('css:.div')

# 查找 div 子元素元素,这个写法是本库特有,原生不支持
ele2 = ele1.ele('css:>div')  

📍 xpath 匹配符 xpath

表示用 xpath 方式查找元素。xpath:xpath=效果一致。
该方法支持完整的 xpath 语法,能使用 xpath 直接获取元素属性selenium 不支持这种用法。

# 查找 div 元素
ele2 = ele1.ele('xpath:.//div')

# 和上面一行一样,查找元素的后代时,// 前面的 . 可以省略
ele2 = ele1.ele('xpath://div')

# 获取 div 元素的 class 属性,返回字符串
txt = ele1.ele('xpath://div/@class')  

?> Tips:
查找元素的后代时selenium 原生代码要求 xpath 前面必须加.,否则会变成在全个页面中查找。作者觉得这个设计是画蛇添足,既然已经通过元素查找了,自然应该只查找这个元素内部的元素。所以,用 xpath 在元素下查找时,最前面///前面的.可以省略。

📍 selenium 的 loc 元组

查找方法能直接接收 selenium 原生定位元组进行查找s 模式下也支持这种写法。

from selenium.webdriver.common.by import By

# 查找 id 为 ele_id 的元素
loc1 = (By.ID, 'ele_id')
ele = page.ele(loc1)

# 按 xpath 查找
loc2 = (By.XPATH, '//div[@class="ele_class"]')
ele = page.ele(loc2)  

✔️ 等待

📍 等待元素加载

由于网络的不稳定性、js 运行时间的不确定性等因素,经常须要等待元素加载到 DOM 中才能使用。

d 模式下所有查找元素操作都自带等待,时间默认跟随元素所在页面timeout属性(默认 10 秒),也可以在每次查找时单独设置,单独设置的等待时间不会改变页面原来设置。

# 页面初始化时设置查找元素超时时间为 15 秒
page = WebPage(timeout=15)
# 设置查找元素超时时间为 5 秒
page.timeout = 5

# 使用页面超时时间来查找元素5 秒)
ele1 = page.ele('search text')
# 为这次查找页面独立设置等待时间1 秒)
ele1 = page.ele('search text', timeout=1)
# 查找后代元素使用页面超时时间5 秒)
ele2 = ele1.ele('search text')
# 查找后代元素使用单独设置的超时时间1 秒)
ele2 = ele1.ele('some text', timeout=1)  

📍 等待元素状态改变

有时候我们须要等待元素到达某种状态,如显示、隐藏、删除。页面对象和元素对象都内置了wait_ele()方法,用于等待元素状态变化。

该方法可接收现成的ChromiumElement对象,或定位符,默认等待时间为页面对象的timeout值,也可以单独设定。

wait_ele()方法

参数:

  • loc_or_ele:要等待的元素,可以是元素或定位符
  • timeout:等待超时时间,默认使用页面超时时间

方法:

方法 参数 功能
display() 等待元素从 DOM 显示
hidden() 等待元素从 DOM 隐藏
delete() 等待元素从 DOM 删除

返回: 这些方法返回布尔值,代表是否等待成功。

# 等待 id 为 div1 的元素显示,超时使用页面设置
page.wait_ele('#div1').display()

# 等待 id 为 div1 的元素被删除(使用 loc 元组设置超时3秒
ele.wait_ele('#div1', timeout=3).delete()

# 等待已获取到的元素被隐藏
ele2 = ele1.ele('#div1')
ele1.wait_ele(ele2).hidden()

✔️ 相对定位

以下方法可以以某元素为基准,在 DOM 中按照条件获取其兄弟元素、祖先元素、文档前后元素。
除获取元素外,还能通过 xpath 获取任意节点内容,如文本节点、注释节点。这在处理元素和文本节点混排的时候非常有用。

📍 获取父级元素

parent()方法获取当前元素某一级父元素,可指定筛选条件或层数。

参数:

  • level_or_loc:第几级父元素,或定位符

返回: 某层父级元素

# 获取 ele1 的第二层父元素
ele2 = ele1.parent(2)

# 获取 ele1 父元素中 id 为 id1 的元素
ele2 = ele1.parent('#id1')

📍 获取后面单个兄弟元素

next()方法返回当前元素后面的某一个同级元素,可指定筛选条件和第几个。

参数:

  • filter_loc:用于筛选元素的查询语法
  • index:查询结果中的第几个
  • timeout:查找元素的超时时间

返回: 本元素后面某个兄弟元素或节点文本

# 获取 ele1 后面第一个兄弟元素
ele2 = ele1.next()

# 获取 ele1 后面第 3 个兄弟元素
ele2 = ele1.next(3)

# 获取 ele1 后面第 3 个 div 兄弟元素
ele2 = ele1.next('tag:div', 3)

# 获取 ele1 后面第一个文本节点的文本
txt = ele1.next('xpath:text()', 1)

📍 获取后面多个兄弟元素

nexts()方法返回当前元素后面全部符合条件的同级元素或节点组成的列表,可用查询语法筛选。

参数:

  • filter_loc:用于筛选元素的查询语法
  • timeout:查找元素的超时时间

返回: 本元素前面符合条件的兄弟元素或节点文本组成的列表

# 获取 ele1 后面所有兄弟元素
eles = ele1.nexts()

# 获取 ele1 后面所有 div 兄弟元素
divs = ele1.nexts('tag:div')

# 获取 ele1 后面的所有文本节点
txts = ele1.nexts('xpath:text()')

📍 获取前面单个兄弟元素

prev()方法返回当前元素前面的某一个同级元素,可指定筛选条件和第几个。

参数:

  • filter_loc:用于筛选元素的查询语法
  • index:查询结果中的第几个
  • timeout:查找元素的超时时间

返回: 本元素前面某个兄弟元素或节点文本

# 获取 ele1 前面第一个兄弟元素
ele2 = ele1.prev()

# 获取 ele1 前面第 3 个兄弟元素
ele2 = ele1.prev(3)

# 获取 ele1 前面第 3 个 div 兄弟元素
ele2 = ele1.prev(3, 'tag:div')

# 获取 ele1 前面第一个文本节点的文本
txt = ele1.prev(1, 'xpath:text()')

📍 获取前面多个兄弟元素

prevs()方法返回当前元素前面全部符合条件的同级元素或节点组成的列表,可用查询语法筛选。

参数:

  • filter_loc:用于筛选元素的查询语法
  • timeout:查找元素的超时时间

返回: 本元素前面符合条件的兄弟元素或节点文本组成的列表

# 获取 ele1 前面所有兄弟元素
eles = ele1.prevs()

# 获取 ele1 前面所有 div 兄弟元素
divs = ele1.prevs('tag:div')

📍 在后面文档中查找单个元素

after()方法返回当前元素后面的某一个元素,可指定筛选条件和第几个。这个方法查找范围不局限在兄弟元素间,而是整个 DOM 文档。

参数:

  • filter_loc:用于筛选元素的查询语法
  • index:查询结果中的第几个
  • timeout:查找元素的超时时间

返回: 本元素后面某个元素或节点

# 获取 ele1 后面第 3 个元素
ele2 = ele1.after(3)

# 获取 ele1 后面第 3 个 div 元素
ele2 = ele1.after('tag:div', 3)

# 获取 ele1 后面第一个文本节点的文本
txt = ele1.after('xpath:text()', 1)

📍 在后面文档中查找多个元素

afters()方法返回当前元素后面符合条件的全部元素或节点组成的列表,可用查询语法筛选。这个方法查找范围不局限在兄弟元素间,而是整个 DOM 文档。

参数:

  • filter_loc:用于筛选元素的查询语法
  • timeout:查找元素的超时时间

返回: 本元素后面符合条件的元素或节点组成的列表

# 获取 ele1 后所有元素
eles = ele1.afters()

# 获取 ele1 前面所有 div 元素
divs = ele1.afters('tag:div')

📍 在前面文档中查找单个元素

before()方法返回当前元素前面的某一个元素,可指定筛选条件和第几个。这个方法查找范围不局限在兄弟元素间,而是整个 DOM 文档。

参数:

  • filter_loc:用于筛选元素的查询语法
  • index:查询结果中的第几个
  • timeout:查找元素的超时时间

返回: 本元素前面某个元素或节点

# 获取 ele1 前面第 3 个元素
ele2 = ele1.before(3)

# 获取 ele1 前面第 3 个 div 元素
ele2 = ele1.before('tag:div', 3)

# 获取 ele1 前面第一个文本节点的文本
txt = ele1.before('xpath:text()', 1)

📍 在前面文档中查找多个元素

befores()方法返回当前元素前面全部符合条件的元素或节点组成的列表,可用查询语法筛选。这个方法查找范围不局限在兄弟元素间,而是整个 DOM 文档。

参数:

  • filter_loc:用于筛选元素的查询语法
  • timeout:查找元素的超时时间

返回: 本元素前面符合条件的元素或节点组成的列表

# 获取 ele1 前面所有元素
eles = ele1.befores()

# 获取 ele1 前面所有 div 元素
divs = ele1.befores('tag:div')

✔️ 查找 frame 里的元素

📍 在页面下跨级查找

与 selenium 不同,本库可以直接查找 frame 里面的元素,而且无视层级,可以直接获取到多层 iframe 里的元素。无需切入切出,大大简化了程序逻辑,使用更便捷。

假设在页面中有个两级 iframe其中有个元素<div id='abc'></div>,可以这样获取:

page = WebPage()
ele = page('#abc')

获取前后无须切入切出,也不影响获取页面上其它元素。如果用 selenium要这样

driver = webdriver.Chrome()
driver.switch_to.frame(0)
driver.switch_to.frame(0)
ele = driver.find_element(By.ID, 'abc')
driver.switch_to.default_content()

显然比较繁琐,而且切入到 iframe 后无法对 iframe 外的元素进行操作。

!>注意:
跨级查找只是页面对象支持,元素对象不能直接查找内部 iframe 里的元素。

📍 在 iframe 元素下查找

本库把 iframe 看作一个特殊元素/页面对象看待,逻辑更清晰,还可以实现同时操作多个 iframe而无须来回切换。查找元素外的更多功能详见介绍 iframe 的章节。

对于跨域名的 iframe我们无法通过页面直接查找里面的元素可以先获取到 iframe 元素,再在其下查找。当然,非跨域 iframe 也可以这样操作。

假设一个 iframe 的 id 为 'iframe1',要在其中查找一个 id 为'abc'的元素:

page = WebPage()
iframe = page('#iframe1')
ele = iframe('#abc')

这个 iframe 元素是一个页面对象,因此可以继续在其下进行跨 iframe 查找(相对这个 iframe 不跨域的)。

✔️ ShadowRootElement相关查找

本库把 shadow-root 也作为元素对象看待,是为ChromiumShadowRootElement对象。该对象可与普通元素一样查找下级元素和 DOM 内相对定位。
ChromiumShadowRootElement对象进行相对定位时,把它看作其父对象内部的第一个对象,其余定位逻辑与普通对象一致。

!> 注意:
如果ChromiumShadowRootElement元素的下级元素中有其它ChromiumShadowRootElement元素,那这些下级ChromiumShadowRootElement 元素内部是无法直接通过定位语句查找到的,只能先定位到其父元素,再用shadow-root属性获取。

# 获取一个 shadow-root 元素
sr_ele = page.ele('#app').shadow_root

# 在该元素下查找下级元素
ele1 = sr_ele.ele('tag:div')

# 用相对定位获取其它元素
ele1 = sr_ele.parent(2)
ele1 = sr_ele.next('tag:div', 1)
ele1 = sr_ele.after('tag:div', 1)
eles = sr_ele.nexts('tag:div')

# 定位下级元素中的 shadow+-root 元素
sr_ele2 = sr_ele.ele('tag:div').shadow_root

✔️ 简化写法

为进一步精简代码,对语法进行了精简

  • 定位语法都有其简化形式。
  • 页面和元素对象都实现了__call__()方法,可直接调用。
  • 所有查找方法都支持链式操作

示例:

# 定位到页面中 id 为 table_id 的元素,然后获取它的所有 tr 元素
eles = page('#table_id').eles('t:tr')

# 定位到 class 为 cls 的元素,然后在它里面查找文本为 text 的元素
ele2 = ele1('.cls1')('tx=text')

# 获取 ele1 的 shadow_root 元素,再在里面查找 class 属性为 cls 的元素
ele2 = ele1.sr('.cls')

# 按xpath 查找元素
ele2 = ele1('x://div[@class="ele_class"]')  

简化写法对应列表

原写法 简化写法
text tx
text() tx()
tag t
xpath x
css c
shadow_root sr

✔️ Tips

  • 从一个ChromiumElement元素获取到的SessionElement版本,依然能够使用相对定位方法定位祖先或兄弟元素。但如果元素在 frame 中,相对定位不能超越 frame 文档。
  • SessionElementSessionPageele()eles()方法也有timeout参数,但它是不生效的,仅用于保持与 d 模式元素书写一致,便于无差别的调用。
  • 定位语句内容与关键字重复时,请使用 xpath 或 css selector 代替。