【爬虫】DrissionPage-8.1
官网文档:
https://www.drissionpage.cn/browser_control/get_elements/intro
DrissionPage 提供了一套简洁、高效且功能强大的元素定位与操作方法,旨在帮助开发者更轻松地处理各种复杂的网页交互场景。
一、概述
1. 为什么避免使用绝对路径
尽管可以通过浏览器的开发者工具直接复制元素的绝对路径,但这种方法存在诸多缺点:
- 代码冗长,可读性低:绝对路径通常包含多个层级,导致代码冗长且难以阅读。
- 动态页面易失效:动态加载的页面元素可能会因为页面结构变化而失效。
- 无法使用相对定位:绝对路径无法灵活地根据页面结构变化进行调整。
- 容错性低:网页结构稍有改动或出现临时元素,定位就会失效。
- 无法跨
<iframe>
查找元素:绝对路径无法跨越<iframe>
查找元素,增加了操作的复杂性。
因此,作者极不建议使用右键复制的元素路径。
2. DrissionPage 的优势
DrissionPage 提供了一套简洁易用的语法,用于快速定位元素,并具备以下优势:
- 内置等待功能:自动等待元素加载,避免因元素未加载而导致的错误。
- 支持链式查找:可以连续调用多个方法,简化代码结构。
- 兼容多种定位方式:支持 CSS Selector、XPath、Selenium 原生的定位符元组以及自定义的查找语法。
- 简化代码,提高可读性:通过简洁的语法和链式操作,使代码更易于理解和维护。
二、定位语法详解
1. 基本用法
所有页面对象和元素对象(包括 <iframe>
和 shadow-root
)都可以在自己的范围内查找元素。元素对象还可以以自身为基准,相对定位其他元素。
定位元素的方法:
- 在页面或元素内查找子元素:使用
ele()
和eles()
方法。 - 根据 DOM 结构相对定位:使用
parent()
,child()
,next()
,prev()
等方法。 - 根据视觉位置相对定位:使用
east()
,west()
,south()
,north()
等方法。
示例:
from DrissionPage import SessionPagepage = SessionPage()
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)
2. 基本概念
几乎所有查找方法都是基于元素属性进行。元素属性包括以下三种:
写法 | 说明 | 示例 |
---|---|---|
@tag() | 标签名 | 即 <div id="one"> 中的 div |
@**** | 标签体中的属性 | 如 <div id="one"> 中的 id ,写作 '@id' |
@text() | 元素文本 | 即 <p class="p_cls">第三行</p> 中的 第三行 |
查找语法 就是按需要对这三种属性进行组合,达到查找指定元素的目的。
3. 常用定位符
3.1 单属性匹配符 @
单个 @
用于以一个属性作为匹配条件,以 @
开头,后面跟属性名称。
示例:
# 获取第一个 id 为 one 的元素
ele = tab.ele('@id=one')# 获取第一个有 id 属性的元素
ele = tab.ele('@id')# 获取第一个文本为“第一行”的元素
ele = tab.ele('@text()=第一行')
3.2 多属性与匹配符 @@
当需要多个条件同时确定一个元素时,每个属性用 @@
开头。
示例:
# 查找 class 为 p_cls 且文本为“第三行”的元素
ele = tab.ele('@@class=p_cls@@text()=第三行')
3.3 多属性或匹配符 @|
当需要以或关系条件查找元素时,每个属性用 @|
开头。
示例:
# 查找所有 id 为 row1 或 id 为 row2 的元素
eles = tab.eles('@|id=row1@|id=row2')
3.4 否定匹配符 @!
用于否定某个条件。
示例:
# 获取第一个 id 不等于“one”的元素
ele = tab.ele('@!id=one')# 匹配没有 class 属性的元素
ele = tab.ele('@!class')
3.5 混合使用
@@
和 @|
不能同时出现在查找语句中,即一个查找语句只能是与关系或者或关系。
@!
则可与两者混合使用。混用时,与还是或关系视 @@
还是 @|
而定。
示例:
# 匹配 class 等于 p_cls 且 id 不等于 row1 的元素
tab.ele('@@class=p_cls@!id=row1')# 匹配 class 等于 p_cls 或 id 不等于 row1 的元素
tab.ele('@|class=p_cls@!id=row1')
4. 匹配模式
匹配模式指某个查询中匹配条件的方式,有以下四种:
- 精确匹配 (
=
):匹配完全符合的文本或属性。 - 模糊匹配 (
:
):匹配含有指定字符串的文本或属性。 - 匹配开头 (
^
):匹配开头为指定字符串的文本或属性。 - 匹配结尾 (
$
):匹配结尾为指定字符串的文本或属性。
注意:tag()
属性无论用哪种匹配模式,都会视作 =
。
示例:
# 精确匹配 id 为 'row1' 的元素
ele = tab.ele('@id=row1')# 模糊匹配 id 属性包含 'ow' 的元素
ele = tab.ele('@id:ow')# 匹配 id 属性以 'row' 开头的元素
ele = tab.ele('@id^row')# 匹配 id 属性以 'w1' 结尾的元素
ele = tab.ele('@id$w1')
5. 常用语法
基于上述基本逻辑,DrissionPage 提供了一些更易于使用和阅读的语法。
5.1 id
匹配符 #
用于匹配 id
属性,只在语句最前面且单独使用时生效,相当于单属性查找 @id=****
。
示例:
# 查找 id 为 one 的元素
ele = tab.ele('#one')# 查找 id 属性包含 'ne' 的元素
ele = tab.ele('#:ne')# 查找 id 属性以 'on' 开头的元素
ele = tab.ele('#^on')# 查找 id 属性以 'ne' 结尾的元素
ele = tab.ele('#$ne')
5.2 class
匹配符 .
用于匹配 class
属性,只在语句最前面且单独使用时生效,相当于单属性查找 @class=****
。
示例:
# 查找 class 属性为 p_cls 的元素
ele = tab.ele('.p_cls')# 查找 class 属性包含 '_cls' 的元素
ele = tab.ele('.:_cls')# 查找 class 属性以 'p_' 开头的元素
ele = tab.ele('.^p_')# 查找 class 属性以 '_cls' 结尾的元素
ele = tab.ele('.$_cls')
5.3 text
匹配符 text
用于匹配元素文本。只在语句最前面且单独使用时生效,相当于单属性查找 @text()=****
。
示例:
# 查找文本为“第二行”的元素
ele = tab.ele('text=第二行')# 查找文本包含“第二”的元素
ele = tab.ele('text:第二')# 与上一行一致
ele = tab.ele('第二')
注意:如果元素内有多个连续的文本节点,精确查找时可匹配所有文本节点拼成的字符串,模糊查找时可匹配每个文本节点。
TIPS:若要查找的文本包含 text:
,可如下面这样写:
ele2 = tab.ele('text:text:')
5.4 tag
匹配符 tag
用于匹配某类型元素。只在语句最前面且单独使用时生效,相当于单属性查找 @tag()=****
。
示例:
# 查找第一个 div 元素
ele = tab.ele('tag:div')# 与单属性查找配合使用
ele = tab.ele('tag:p@class=p_cls')# 与多属性查找配合使用
ele = tab.ele('tag:p@@class=p_cls@@text()=第二行')
注意:在查找语句中,tag:
与 tag=
效果一致,没有 tag^
和 tag$
语法。
5.5 css
匹配符 css
表示用 CSS Selector 方式查找元素。只在语句最前面且单独使用时生效。
示例:
# 查找 div 元素
ele = tab.ele('css:.div')# 查找 div 子元素,这个写法是本库特有,原生不支持
ele = tab.ele('css:>div')
5.6 xpath
匹配符 xpath
表示用 XPath 方式查找元素。只在语句最前面且单独使用时生效。
示例:
# 查找后代中第一个 div 元素
ele2 = ele1.ele('xpath:.//div')# 和上面一行一样,查找元素的后代时,// 前面的 . 可以省略
ele2 = ele1.ele('xpath://div')# 使用 xpath 获取 div 元素的 class 属性(页面元素无此功能)
ele_class_str = ele1.ele('xpath://div/@class')
TIPS:元素对象的 ele()
支持完整的 XPath 语法,如能使用 XPath 直接获取元素属性(字符串类型)。
5.7 Selenium 的 loc
元组
查找方法能直接接收 Selenium 原生定位元组进行查找,便于项目迁移。
示例:
from DrissionPage.common import By# 查找 id 为 one 的元素
loc1 = (By.ID, 'one')
ele = tab.ele(loc1)# 按 xpath 查找
loc2 = (By.XPATH, '//p[@class="p_cls"]')
ele = tab.ele(loc2)
三、相对定位
相对定位的意思是以一个已获取的元素为基准,按需要使用不同方法获取指定的其它元素。
1. 基于 DOM 相对定位
以下方法可以以某元素为基准,在 DOM 中按照条件获取其直接子节点、同级节点、祖先元素、文档前后节点。
注意:如果元素在 <iframe>
中,相对定位不能超越 <iframe>
文档。
1.1 获取父级元素
parent()
此方法获取当前元素某一级父元素,可指定筛选条件或层数。
参数:
参数名称 | 类型 | 默认值 | 说明 |
---|---|---|---|
level_or_loc | int / str / Tuple[str, str] | 1 | 第几级父元素,从 1 开始,或用定位符在祖先元素中进行筛选。 |
index | int | 1 | 当 level_or_loc 传入定位符,使用此参数选择第几个结果,从当前元素往上级数;当 level_or_loc 传入数字时,此参数无效。 |
timeout | float | 0 | 查找超时时间(秒),为 None 时使用页面对象设置,s 模式下无效。 |
返回类型:
类型 | 说明 |
---|---|
ChromiumElement | d 模式下的元素对象。 |
SessionElement | s 模式下的元素对象。 |
NoneElement | 未获取到结果时。 } |
示例:
# 获取 ele1 的第二层父元素
ele2 = ele1.parent(2)# 获取 ele1 父元素中 id 为 id1 的元素
ele2 = ele1.parent('#id1')
1.2 获取直接子节点
child()
此方法返回当前元素的一个直接子节点,可指定筛选条件和第几个。
参数:
参数名称 | 类型 | 默认值 | 说明 |
---|---|---|---|
locator | str / Tuple[str, str] / int | '' | 用于筛选节点的查询语法,为 int 类型时 index 参数无效。 |
index | int | 1 | 查询结果中的第几个,从 1 开始,可输入负数表示倒数。 |
timeout | float | None | 查找超时时间(秒),为 None 时使用页面对象设置,s 模式下无效。 |
ele_only | bool | True | 是否只查找元素,为 False 时把文本、注释节点也纳入查找范围。 |
返回类型:
类型 | 说明 |
---|---|
str | 获取非元素节点时返回字符串。 |
ChromiumElement | d 模式下的元素对象。 |
SessionElement | s 模式下的元素对象。 |
NoneElement | 未获取到结果时。 } |
children()
此方法返回当前元素全部符合条件的直接子节点组成的列表,可用查询语法筛选。
参数:
参数名称 | 类型 | 默认值 | 说明 |
---|---|---|---|
locator | str / Tuple[str, string] | '' | 用于筛选节点的查询语法。 |
timeout | float | None | 查找超时时间(秒),为 None 时使用页面对象设置,s 模式下无效。 |
ele_only | bool | True | 是否只查找元素,为 False 时把文本、注释节点也纳入查找范围。 |
返回类型:
类型 | 说明 |
---|---|
ChromiumElementsList | d 模式结果列表。 |
SessionElementsList | s 模式结果列表。 } |
1.3 获取后面的同级节点
next()
此方法返回当前元素后面的某一个同级节点,可指定筛选条件和第几个。
参数:
参数名称 | 类型 | 默认值 | 说明 |
---|---|---|---|
locator | str / Tuple[str, string] / int | '' | 用于筛选节点的查询语法,为 int 类型时 index 参数无效。 |
index | int | 1 | 查询结果中的第几个,从 1 开始,可输入负数表示倒数。 |
timeout | float | None | 查找超时时间(秒),为 None 时使用页面对象设置,s 模式下无效。 |
ele_only | bool | True | 是否只查找元素,为 False 时把文本、注释节点也纳入查找范围。 } |
返回类型:
类型 | 说明 |
---|---|
str | 获取非元素节点时返回字符串。 |
ChromiumElement | d 模式下的元素对象。 |
SessionElement | s 模式下的元素对象。 |
NoneElement | 未获取到结果时。 } |
nexts()
此方法返回当前元素后面全部符合条件的同级节点组成的列表,可用查询语法筛选。
参数:
参数名称 | 类型 | 默认值 | 说明 |
---|---|---|---|
locator | str / Tuple[str, string] | '' | 用于筛选节点的查询语法。 |
timeout | float | None | 查找超时时间(秒),为 None 时使用页面对象设置,s 模式下无效。 |
ele_only | bool | True | 是否只查找元素,为 False 时把文本、注释节点也纳入查找范围。 } |
返回类型:
类型 | 说明 |
---|---|
ChromiumElementsList | d 模式结果列表。 |
SessionElementsList | s 模式结果列表。 } |
1.4 获取前面的同级节点
prev()
此方法返回当前元素前面的某一个同级节点,可指定筛选条件和第几个。
参数:
参数名称 | 类型 | 默认值 | 说明 |
---|---|---|---|
locator | str / Tuple[str, string] / int | '' | 用于筛选节点的查询语法,为 int 类型时 index 参数无效。 |
index | int | 1 | 查询结果中的第几个,从 1 开始,可输入负数表示倒数。 |
timeout | float | None | 查找超时时间(秒),为 None 时使用页面对象设置,s 模式下无效。 |
ele_only | bool | True | 是否只查找元素,为 False 时把文本、注释节点也纳入查找范围。 } |
返回类型:
类型 | 说明 |
---|---|
str | 获取非元素节点时返回字符串。 |
ChromiumElement | d 模式下的元素对象。 |
SessionElement | s 模式下的元素对象。 |
NoneElement | 未获取到结果时。 } |
prevs()
此方法返回当前元素前面全部符合条件的同级节点组成的列表,可用查询语法筛选。
参数:
参数名称 | 类型 | 默认值 | 说明 |
---|---|---|---|
locator | str / Tuple[str, string] | '' | 用于筛选节点的查询语法。 |
timeout | float | None | 查找超时时间(秒),为 None 时使用页面对象设置,s 模式无效。 |
ele_only | bool | True | 是否只查找元素,为 False 时把文本、注释节点也纳入查找范围。 } |
返回类型:
类型 | 说明 |
---|---|
ChromiumElementsList | d 模式结果列表。 |
SessionElementsList | s 模式结果列表。 } |
1.5 在后面文档中查找节点
after()
此方法返回当前元素后面的某一个节点,可指定筛选条件和第几个。查找范围不限同级节点,而是整个 DOM 文档。
参数:
参数名称 | 类型 | 默认值 | 说明 |
---|---|---|---|
locator | str / Tuple[str, string] / int | '' | 用于筛选节点的查询语法,为 int 类型时 index 参数无效。 |
index | int | 1 | 查询结果中的第几个,从 1 开始,可输入负数表示倒数。 |
timeout | float | None | 查找超时时间(秒),为 None 时使用页面对象设置,s 模式下无效。 |
ele_only | bool | True | 是否只查找元素,为 False 时把文本、注释节点也纳入查找范围。 } |
返回类型:
类型 | 说明 |
---|---|
str | 获取非元素节点时返回字符串。 |
ChromiumElement | d 模式下的元素对象。 |
SessionElement | s 模式下的元素对象。 |
NoneElement | 未获取到结果时。 } |
afters()
此方法返回当前元素后面符合条件的全部节点组成的列表,可用查询语法筛选。查找范围不限同级节点,而是整个 DOM 文档。
参数:
参数名称 | 类型 | 默认值 | 说明 |
---|---|---|---|
locator | str / Tuple[str, string] | '' | 用于筛选节点的查询语法。 |
timeout | float | None | 查找超时时间(秒),为 None 时使用页面对象设置,s 模式下无效。 |
ele_only | bool | True | 是否只查找元素,为 False 时把文本、注释节点也纳入查找范围。 } |
返回类型:
类型 | 说明 |
---|---|
ChromiumElementsList | d 模式结果列表。 |
SessionElementsList | s 模式结果列表。 } |
1.6 在前面文档中查找节点
before()
此方法返回当前元素前面的某一个符合条件的节点,可指定筛选条件和第几个。查找范围不限同级节点,而是整个 DOM 文档。
参数:
参数名称 | 类型 | 默认值 | 说明 |
---|---|---|---|
locator | str / Tuple[str, string] / int | '' | 用于筛选节点的查询语法,为 int 类型时 index 参数无效。 |
index | int | 1 | 查询结果中的第几个,从 1 开始,可输入负数表示倒数。 |
timeout | float | None | 查找超时时间(秒),为 None 时使用页面对象设置,s 模式下无效。 |
ele_only | bool | True | 是否只查找元素,为 False 时把文本、注释节点也纳入查找范围。 } |
返回类型:
类型 | 说明 |
---|---|
str | 获取非元素节点时返回字符串。 |
ChromiumElement | d 模式下的元素对象。 |
SessionElement | s 模式下的元素对象。 |
NoneElement | 未获取到结果时。 } |
befores()
此方法返回当前元素前面全部符合条件的节点组成的列表,可用查询语法筛选。查找范围不限同级节点,而是整个 DOM 文档。
参数:
参数名称 | 类型 | 默认值 | 说明 |
---|---|---|---|
locator | str / Tuple[str, string] | '' | 用于筛选节点的查询语法。 |
timeout | float | None | 查找超时时间(秒),为 None 时使用页面对象设置,s 模式下无效。 |
ele_only | bool | True | 是否只查找元素,为 False 时把文本、注释节点也纳入查找范围。 } |
返回类型:
类型 | 说明 |
---|---|
ChromiumElementsList | d 模式结果列表。 |
SessionElementsList | s 模式结果列表。 } |
四、基于视觉相对定位
以下方法可以以某元素为基准,向不同方向或指定偏移量获取元素。
注意:只有浏览器模式支持这类定位方式。
只能获取可见的元素(不论是否在视口内),不能获取被遮挡的。
1. east()
此方法用于获取一个在当前元素右边的元素。
参数:
参数名称 | 类型 | 默认值 | 说明 |
---|---|---|---|
loc_or_pixel | str / int | None | 定位符或距离(像素)。 |
index | int | 1 | 第几个,从 1 开始,loc_or_pixel 为 int 格式时无效。 |
timeout | `float | None | 等待元素出现的超时时间(秒),为 None 使用页面 timeout 设置值。 |
返回类型:
类型 | 说明 |
---|---|
ChromiumElement | 找到的元素对象。 |
NoneElement | 未获取到结果时。 } |
2. west()
此方法用于获取一个在当前元素左边的元素。
参数:
参数名称 | 类型 | 默认值 | 说明 |
---|---|---|---|
loc_or_pixel | str / int | None | 定位符或距离(像素)。 |
index | int | 1 | 第几个,从 1 开始,loc_or_pixel 为 int 格式时无效。 |
timeout | `float | None | 等待元素出现的超时时间(秒),为 None 使用页面 timeout 设置值。 |
返回类型:
类型 | 说明 |
---|---|
ChromiumElement | 找到的元素对象。 |
NoneElement | 未获取到结果时。 } |
3. south()
此方法用于获取一个在当前元素下边的元素。
参数:
参数名称 | 类型 | 默认值 | 说明 |
---|---|---|---|
loc_or_pixel | str / int | None | 定位符或距离(像素)。 |
index | int | 1 | 第几个,从 1 开始,loc_or_pixel 为 int 格式时无效。 |
timeout | `float | None | 等待元素出现的超时时间(秒),为 None 使用页面 timeout 设置值。 |
返回类型:
类型 | 说明 |
---|---|
ChromiumElement | 找到的元素对象。 |
NoneElement | 未获取到结果时。 } |
4. north()
此方法用于获取一个在当前元素上边的元素。
参数:
参数名称 | 类型 | 默认值 | 说明 |
---|---|---|---|
loc_or_pixel | str / int | None | 定位符或距离(像素)。 |
index | int | 1 | 第几个,从 1 开始,loc_or_pixel 为 int 格式时无效。 |
timeout | `float | None | 等待元素出现的超时时间(秒),为 None 使用页面 timeout 设置值。 |
返回类型:
类型 | 说明 |
---|---|
ChromiumElement | 找到的元素对象。 |
NoneElement | 未获取到结果时。 } |
5. offset()
此方法用于获取相对于元素左上角指定偏移量的一个元素。
参数:
参数名称 | 类型 | 默认值 | 说明 |
---|---|---|---|
offset_x | int | 必填 | 横坐标偏移量(像素),向右为正。 |
offset_y | int | 必填 | 纵坐标偏移量(像素),向下为正。 |
返回类型:
类型 | 说明 |
---|---|
ChromiumElement | 找到的元素对象。 |
NoneElement | 未获取到结果时。 } |
6. over()
此方法用于获取覆盖在本元素上最上层的元素。
参数:
参数名称 | 类型 | 默认值 | 说明 |
---|---|---|---|
timeout | `float | None | 等待元素出现的超时时间(秒),为 None 使用页面 timeout 设置值。 |
返回类型:
类型 | 说明 |
---|---|
ChromiumElement | 找到的元素对象。 |
NoneElement | 未获取到结果时。 } |
示例:
ele = tab.ele('****')# 判断是否找到元素
if ele:print('找到了。')if not ele:print('没有找到。')
示例,用 try
捕获:
try:ele.click()
except ElementNotFoundError:print('没有找到。')
7. 立即抛出异常
如果想在找不到元素时立刻抛出异常,可以用以下方法设置。
设置全局变量:
from DrissionPage.common import SettingsSettings.set_raise_when_ele_not_found(True)
示例:
from DrissionPage import Chromium
from DrissionPage.common import SettingsSettings.set_raise_when_ele_not_found(True)tab = Chromium().latest_tab
tab.get('https://www.baidu.com')
ele = tab('#abcd') # ('#abcd') 这个元素不存在# 输出:
# DrissionPage.errors.ElementNotFoundError:
# 没有找到元素。
# method: ele()
# args: {'locator': '#abcd'}
8. 设置默认返回值
如果查找元素后要获取一个属性,但这个元素不一定存在,或者链式查找其中一个节点找不到,可以设置查找失败时返回的值,而不是抛出异常,可以简化一些采集逻辑。
使用浏览器页面对象的 set.NoneElement_value()
方法设置该值。
参数:
参数名称 | 类型 | 默认值 | 说明 |
---|---|---|---|
value | Any | None | 将返回的设定值。 |
on_off | `bool | True | bool 表示是否启用。 |
返回:None
示例:
from DrissionPage import Chromiumtab = Chromium().latest_tab
tab.set.NoneElement_value('没找到')
for li in tab.eles('t:li'):name = li('.name').textage = li('.age').textphone = li('.phone').text# 这样,假如某个子元素不存在,不会抛出异常,而是返回'没找到'这个字符串。