mirror of
https://gitee.com/g1879/DrissionPage.git
synced 2024-12-10 04:00:23 +08:00
532 lines
15 KiB
Markdown
532 lines
15 KiB
Markdown
# 获取页面元素
|
||
|
||
本节介绍如何从页面对象或元素对象中获取须要的元素对象。
|
||
|
||
页面对象(MixPage)和元素对象(DriverElement 或 SessionElement)都可通过 ele()、eles()、s_ele()、s_eles()方法定位到其后代元素。
|
||
DriverElement 还有专门用于获取shadow-root 的 shadow_root 属性。获取到的元素可继续用这些方法获取后代元素,支持链式操作。
|
||
|
||
除了以上方法,元素还有 next、prev、parent、nexts()、prevs()、parents() 等相对定位方法。
|
||
|
||
d 模式获取元素的方法内置了等待,默认使用元素所属页面的超时设置,也可用在每次获取元素时单独设置等待时间。
|
||
|
||
|
||
|
||
## 简单示例:
|
||
|
||
```html
|
||
<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>
|
||
```
|
||
|
||
假设 page 为以上页面的 MixPage 对象,我们可以用这些方法获取其中的元素:
|
||
|
||
```python
|
||
# 获取 id 为 one 的元素
|
||
div1 = page.ele('#one')
|
||
|
||
# 获取 div1 元素内所有 p 元素组成的列表
|
||
p_eles = div1.eles('tag:p')
|
||
|
||
# 获取 name 属性为 row1 的元素
|
||
row1 = 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('x://div[@id="tow"]')
|
||
```
|
||
|
||
|
||
|
||
## 实际示例:
|
||
|
||
```python
|
||
from DrissionPage import MixPage
|
||
|
||
page = MixPage('s')
|
||
page.get('https://gitee.com/explore')
|
||
|
||
ul_ele = page.ele('tag:ul@@text():全部推荐项目') # 获取包含“全部推荐项目”文本的 ul 元素
|
||
titles = ul_ele.eles('tag:a') # 获取该 ul 元素下所有 a 元素
|
||
|
||
for i in titles: # 遍历列表,打印每个 a 元素的文本
|
||
print(i.text)
|
||
|
||
"""输出:
|
||
全部推荐项目
|
||
前沿技术
|
||
智能硬件
|
||
IOT/物联网/边缘计算
|
||
车载应用
|
||
以下省略……
|
||
"""
|
||
```
|
||
|
||
# 一览表
|
||
|
||
以下为获取元素方法一览表,然后再详细介绍每个方法。
|
||
|
||
```python
|
||
# 在页面或元素内部查找一个元素
|
||
ele1 = page.ele('...')
|
||
ele2 = ele1.ele('...')
|
||
|
||
# 在页面或元素内部查找多个元素
|
||
ele_list1 = page.eles('...')
|
||
ele_list2 = ele1.eles('...')
|
||
|
||
# 在页面或元素内部查找一个元素并返回其 SessionElement 形式的对象
|
||
ele1 = page.s_ele('...')
|
||
ele2 = ele1.s_ele('...')
|
||
|
||
# 在页面或元素内部查找多个元素并返回它们的 SessionElement 形式的对象
|
||
ele1 = page.s_eles('...')
|
||
ele2 = ele1.s_eles('...')
|
||
|
||
# 获取元素后面一个兄弟元素
|
||
ele2 = ele1.next
|
||
|
||
# 获取元素前面一个兄弟元素
|
||
ele2 = ele1.prev
|
||
|
||
# 获取元素的父元素
|
||
ele2 = ele1.parent
|
||
|
||
# 获取元素后面第 2 个兄弟元素
|
||
ele2 = ele1.nexts(2)
|
||
|
||
# 获取元素前面第 2 个兄弟元素
|
||
ele2 = ele1.prevs(2)
|
||
|
||
# 获取元素的第 2 层父元素
|
||
ele2 = ele1.parents(2)
|
||
|
||
# 获取元素内部的 shadow_root 并以 ShadowRootElement 对象返回
|
||
ele_sr = shadow_root
|
||
```
|
||
|
||
# 定位方法
|
||
|
||
## ele()
|
||
|
||
此方法用于查找并返回第一个匹配的元素,d 模式下返回 DriverElement,s 模式下返回 SessionElement,用 xpath 获取元素属性时,直接返回属性文本。查找不到结果则返回 None。
|
||
|
||
```python
|
||
# 在页面内查找元素
|
||
ele1 = page.ele('search text')
|
||
|
||
# 在元素内查找后代元素
|
||
ele2 = ele1.ele('search text')
|
||
```
|
||
|
||
|
||
|
||
## eles()
|
||
|
||
此方法与 ele() 相似,但返回的是匹配到的所有元素组成的列表,用 xpath 获取元素属性时,返回属性文本组成的列表。
|
||
|
||
```python
|
||
# 获取 ele 元素内的所有 p 元素
|
||
p_eles = ele.eles('tag:p')
|
||
# 打印第一个 p 元素
|
||
print(p_eles[0])
|
||
```
|
||
|
||
|
||
|
||
## s_ele()
|
||
|
||
此方法用于在一个元素下查找后代元素,以 SessionElement 形式返回结果(xpath 获取属性值时依然是返回 str),也可以直接将一个元素转换为 SessionElement 版本。
|
||
|
||
这是为了 d 模式处理速度的提升,当爬取复杂页面信息而且不须要和元素进行交互时,生成整个页面或者主要容器元素的 SessionElement ,再在其中获取信息,可以将速度提升几个数量级。
|
||
|
||
s 模式下这个方法和 ele() 是一样的。
|
||
|
||
```python
|
||
# 获取 ele1 元素的 SessionElement 版本
|
||
ele2 = ele1.s_ele()
|
||
|
||
# 在 ele1 元素下查找元素,并以 SessionElemnet 返回
|
||
ele2 = ele1.s_ele('search text')
|
||
|
||
# 在页面下查找元素,并以 SessionElemnet 返回
|
||
ele = page.s_ele('search text')
|
||
```
|
||
|
||
## s_eles()
|
||
|
||
此方法与 s_ele() 相似,但返回的是匹配到的所有元素组成的列表,或属性值组成的列表。页面对象没有此方法。
|
||
|
||
## next
|
||
|
||
此属性返回当前元素的后一个兄弟元素。
|
||
|
||
```python
|
||
# 获取 ele1 后面一个兄弟元素
|
||
ele2 = ele1.next
|
||
```
|
||
|
||
|
||
|
||
## prev
|
||
|
||
此属性返回当前元素的前一个兄弟元素。
|
||
|
||
```python
|
||
# 获取 ele1 前面一个兄弟元素
|
||
ele2 = ele1.prev
|
||
```
|
||
|
||
|
||
|
||
## parent
|
||
|
||
此属性返回当前元素的父元素。
|
||
|
||
注:ShadowRootElement 的这个属性返回其所属元素。
|
||
|
||
```python
|
||
# 获取 ele1 的父元素
|
||
ele2 = ele1.parent
|
||
```
|
||
|
||
|
||
|
||
## nexts()
|
||
|
||
此方法返回当前元素后面第若干个兄弟元素。
|
||
|
||
```python
|
||
# 获取 ele1 后面第三个兄弟元素
|
||
ele2 = ele1.nexts(3)
|
||
```
|
||
|
||
|
||
|
||
## prevs()
|
||
|
||
此方法返回当前元素前面第若干个兄弟元素。
|
||
|
||
```python
|
||
# 获取 ele1 前面第三个兄弟元素
|
||
ele2 = ele1.prevs(3)
|
||
```
|
||
|
||
|
||
|
||
## parents()
|
||
|
||
此方法返回当前元素第若干层祖先元素。
|
||
|
||
```python
|
||
# 获取 ele1 上面 3 层父元素
|
||
ele2 = ele1.parents(3)
|
||
```
|
||
|
||
|
||
|
||
## shadow_root
|
||
|
||
DriverElement 元素除了以上方法和属性外,还有 shadow_root 属性,用于获取其内部的 shadow_root 元素。
|
||
该属性返回的是一个 ShadowRootElement,类似于 DriverElement,功能比 DriverElement 少。但也有 ele() 和 eles() 方法,可直接搜索其下的元素,返回 DriverElement 元素。
|
||
该 DriverElement 和普通的没有区别。
|
||
|
||
```python
|
||
# 获取一个元素下是 shadow root
|
||
shadow_ele = ele.shadow_root
|
||
# 获取该 shadow root 下的一个元素
|
||
ele1 = shadow_ele.ele('search text')
|
||
# 点击获取到的元素
|
||
ele1.click()
|
||
```
|
||
|
||
## active_ele
|
||
|
||
该属性返回当前页面焦点所在元素。
|
||
|
||
```python
|
||
ele = page.active_ele
|
||
```
|
||
|
||
# 查找语法
|
||
|
||
我们使用一套简洁高效的语法去定位元素,大大简化了定位元素的代码量,增强了功能,也兼容 css selector、xpath、selenium 原生的 loc 元组(s 模式也能用)。d 模式和 s
|
||
模式定位元素的语法是完全一样的,便于模式切换时平滑过渡。
|
||
|
||
**关键字** 是出现在定位语句最左边,用于指明该语句以哪种方式去查找元素,有以下这些:
|
||
|
||
## =
|
||
|
||
表示精确匹配,匹配完全符合的文本或属性。
|
||
|
||
## :
|
||
|
||
表示模糊匹配,匹配含有某个字符串的文本或属性。
|
||
|
||
|
||
|
||
## \#
|
||
|
||
表示 id 属性,只在语句最前面且单独使用时生效,可配合 = 或 :。
|
||
|
||
```python
|
||
# 在页面中查找 id 属性为 ele_id 的元素
|
||
ele1 = page.ele('#ele_id')
|
||
|
||
# 在 ele1 元素内查找 id 属性包含 ele_id 文本的元素
|
||
ele2 = ele1.ele('#:ele_id')
|
||
```
|
||
|
||
## .
|
||
|
||
表示 class 属性,只在语句最前面且单独使用时生效,可配合 = 或 :。
|
||
|
||
```python
|
||
# 查找 class 属性为 ele_class 的元素
|
||
ele2 = ele1.ele('.ele_class')
|
||
|
||
# 查找 class 属性包含 ele_class 文本的元素
|
||
ele2 = ele1.ele('.:ele_class')
|
||
```
|
||
|
||
## @
|
||
|
||
表示某个属性,只匹配一个属性。
|
||
@ 关键字只有一个简单功能,就是匹配 @ 后面的内容,不再对后面的字符串进行解析。因此即使后面的字符串也存在 @ 或 @@,也作为要匹配的内容对待。
|
||
|
||
```python
|
||
# 查找 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')
|
||
```
|
||
|
||
## @@
|
||
|
||
表示某个属性,多属性匹配时使用,个数不限。还能匹配要忽略的元素,匹配文本时也和 @ 不一样。
|
||
@@ 后跟 - 时,表示 not。如:
|
||
|
||
- `@@-name`表示匹配没有 name 属性的元素
|
||
|
||
- `@@-name=ele_name`表示匹配 name 属性不为 ele_name 的元素
|
||
|
||
如有以下情况,不能使用此方式,须改用 xpath的方式:
|
||
|
||
- 匹配文本或属性中出现 @@
|
||
|
||
- 属性名本身以 - 开头
|
||
|
||
```python
|
||
# 查找 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
|
||
|
||
要匹配的文本,查询字符串如开头没有任何关键字,也表示根据 text 作模糊查找。
|
||
如果元素内有多个直接的文本节点,精确查找时可匹配所有文本节点拼成的字符串,模糊查找时可匹配每个文本节点。
|
||
|
||
```python
|
||
# 查找文本为 some text 的元素
|
||
ele2 = ele1.ele('text=some text')
|
||
|
||
# 查找文本包含 some text 的元素
|
||
ele2 = ele1.ele('text:some text')
|
||
|
||
# 与上一行一致
|
||
ele2 = ele1.ele('some text')
|
||
```
|
||
|
||
## text()
|
||
|
||
作为查找属性时使用的文本关键字,必须与 @ 或 @@ 配合使用。
|
||
与 @ 配合和与 @@ 配合使用时,text() 的意义是有差别的。
|
||
|
||
@text() 表示在元素的直接子文本节点中匹配,且多个节点不能合并匹配。 @@text() 表示在元素内部所有文本中匹配,且会把元素内部所有文本拼成一个总字符串再进行匹配,因此可以模糊匹配元素里面任意文本。
|
||
|
||
```python
|
||
# 查找文本为 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')
|
||
```
|
||
|
||
## tag
|
||
|
||
表示元素的标签,只在语句最前面且单独使用时生效,可与 @ 或 @@ 配合使用。tag: 与 tag= 效果一致。
|
||
|
||
```python
|
||
# 定位 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():text'` 和 `'tag:div@@text():text'` 是有区别的,前者只在 div 的直接文本节点搜索,后者搜索 div 的整个内部。
|
||
|
||
## css
|
||
|
||
表示用 css selector 方式查找元素。css: 与 css= 效果一致。
|
||
|
||
```python
|
||
# 查找 div 元素
|
||
ele2 = ele1.ele('css:.div')
|
||
|
||
# 查找 div 子元素元素,这个写法是本库特有,原生不支持
|
||
ele2 = ele1.ele('css:>div')
|
||
```
|
||
|
||
## xpath
|
||
|
||
表示用 xpath 方式查找元素。xpath: 与 xpath= 效果一致。
|
||
该方法支持完整的 xpath 语法,能使用 xpath 直接获取元素属性,selenium 不支持这种用法。
|
||
|
||
```python
|
||
# 查找 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 模式下也支持这种写法。
|
||
|
||
```python
|
||
# 查找 id 为 ele_id 的元素
|
||
loc1 = By.ID, 'ele_id'
|
||
ele = page.ele(loc1)
|
||
|
||
# 按 xpath 查找
|
||
loc2 = By.XPATH, '//div[@class="ele_class"]'
|
||
ele = page.ele(loc2)
|
||
```
|
||
|
||
# 等待
|
||
|
||
d 模式下所有查找元素操作都自带等待,默认为 10 秒,也可以在每次查找时单独设置。元素等待时间默认继承其所在页面时间。单独设置的等待时间不会改变页面原来设置。
|
||
|
||
```python
|
||
# 页面初始化时设置查找元素超时时间为 15 秒
|
||
page = MixPage(timeout=15)
|
||
# 设置查找元素超时时间为 5 秒
|
||
page.timeout = 5
|
||
|
||
# 使用页面超时时间来查找元素(5 秒)
|
||
ele1 = page.ele('some text')
|
||
# 为这次查找页面独立设置等待时间(1 秒)
|
||
ele1 = page.ele('some text', timeout=1)
|
||
# 查找后代元素,使用页面超时时间(5 秒)
|
||
ele2 = ele1.ele('some text')
|
||
# 查找后代元素,使用单独设置的超时时间(1 秒)
|
||
ele2 = ele1.ele('some text', timeout=1)
|
||
```
|
||
|
||
# 简化写法
|
||
|
||
为进一步精简代码,对语法进行了精简
|
||
|
||
- 定位语法都有其简化形式。
|
||
- 页面和元素对象都实现了 \_\_call\_\_() 方法,可直接调用。
|
||
- 所有查找方法都支持链式操作
|
||
|
||
示例:
|
||
|
||
```python
|
||
# 定位到页面中 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
|
||
|
||
- 从一个 DriverElement 元素获取到的 SessionElement 版本,依然能够使用相对定位方法定位祖先或兄弟元素。
|
||
- SessionElement 和 SessionPage 的 ele() 和 eles() 方法也有 timeout 参数,但它是不生效的,仅用于保持与 d 模式元素书写一致,便于无差别的调用。
|
||
- ShadowRootElement 的 ele() 不支持 xpath 关键字。
|
||
- 定位语句内容与关键字重复时,可使用 xpath 或 css selector。
|
||
|