目录
一、概述
DrissionPage 是一个基于 python 的网页自动化工具。
它既能控制浏览器,也能收发数据包,还能把两者合而为一。
可兼顾浏览器自动化的便利性和 requests 的高效率。
它功能强大,内置无数人性化设计和便捷功能。
它的语法简洁而优雅,代码量少,对新手友好。
二、特性
2.1 强大的内核
本库采用全自研的内核,内置了 N 多实用功能,对常用功能作了整合和优化,对比 selenium,有以下优点:
- 无 webdriver 特征
- 无需为不同版本的浏览器下载不同的驱动
- 运行速度更快
- 可以跨 iframe 查找元素,无需切入切出
- 把 iframe 看作普通元素,获取后可直接在其中查找元素,逻辑更清晰
- 可以同时操作浏览器中的多个标签页,即使标签页为非激活状态,无需切换
- 可以直接读取浏览器缓存来保存图片,无需用 GUI 点击另存
- 可以对整个网页截图,包括视口外的部分(90以上版本浏览器支持)
- 可处理非
open
状态的 shadow-root
2.2 亮点功能
- 极简的语法规则。集成大量常用功能,代码更优雅
- 定位元素更加容易,功能更强大稳定
- 无处不在的等待和自动重试功能。使不稳定的网络变得易于控制,程序更稳定,编写更省心
- 提供强大的下载工具。操作浏览器时也能享受快捷可靠的下载功能
- 允许反复使用已经打开的浏览器。无需每次运行从头启动浏览器,调试超方便
- 使用 ini 文件保存常用配置,自动调用,提供便捷的设置,远离繁杂的配置项
- 内置 lxml 作为解析引擎,解析速度成几个数量级提升
- 使用 POM 模式封装,可直接用于测试,便于扩展
- 高度集成的便利功能,从每个细节中体现
三、安装与升级
# 安装
pip install DrissionPage
# 升级
pip install DrissionPage --upgrade
# 升级指定版本
pip install DrissionPage==4.0.0b17
四、导包与简单示例
4.1 导包
# 如果只要控制浏览器,导入ChromiumPage。
from DrissionPage import ChromiumPage
# 如果只要收发数据包,导入SessionPage。
from DrissionPage import SessionPage
# WebPage是功能最全面的页面类,既可控制浏览器,也可收发数据包。
from DrissionPage import WebPage
4.2 简单示例
from DrissionPage import ChromiumPage
# 导入
from DrissionPage import ChromiumPage
# 创建对象
page = ChromiumPage()
# 访问网页
page.get('https://www.baidu.com')
# 输入文本
page('#kw').input('DrissionPage')
# 点击按钮
page('#su').click()
# 等待页面跳转
page.wait.load_start()
# 获取所有结果
links = page.eles('tag:h3')
# 遍历并打印结果
for link in links:
print(link.text)
五、查找元素
5.1 概述
本库提供一套简洁易用的语法,用于快速定位元素,并且内置等待功能、支持链式查找,减少了代码的复杂性。
同时也兼容 css selector、xpath、selenium 原生的 loc 元组。
定位元素大致分为三种方法:
- 在页面或元素内查找子元素
- 根据 DOM 结构相对定位
- 根据页面布局位置相对定位
使用方式
所有页面对象和元素对象,都可以在自己内部查找元素,元素对象还能以自己为基准,相对定位其它元素。
页面对象包括:SessionPage
、ChromiumPage
、ChromiumTab
、ChromiumFrame
、WebPage
、WebPageTab
元素对象包括:SessionElement
、ChromiumElement
、ShadowRoot
5.1.1 在页面中查找
使用页面对象的ele()
和eles()
方法,获取页面内指定元素对象。
from DrissionPage import SessionPage
page = SessionPage()
page.get('https://www.baidu.com')
ele = page.ele('#su')
5.1.2 在元素中查找
使用元素对象的ele()
、eles()
、child()
、children()
方法,获取元素内指定后代元素对象。
ele1 = page.ele('#s_fm')
ele2 = ele1.ele('#su')
son = ele1.child('tag:div') # 获取第一个直接div子元素
sons = ele1.children('tag:div') # 获取所有直接div子元素
5.1.3 链式查找
因为对象本身又可以查找对象,所有支持链式操作,上面两个例子可合并为:
ele = page.ele('#s_fm').ele('#su')
5.1.4 相对查找
元素对象在以自己为基准,执行相对查找。
ele = page.ele('#su')
parent = ele.parent(2) # 获取ele元素的第二层父元素
brother = ele.next('tag:a') # 获取ele元素后面的第一个a元素
after = ele.after('tag:div') # 获取ele后面文档中第一个div元素
5.1.5 shadow root
使用浏览器元素对象的shadow_root
属性获取该元素下的ShadowRoot
对象。
shadow = page.ele('#ele1').shadow_root
在 shadow root 元素中搜索方法与普通元素一致。
shadow = page.ele('#ele1').shadow_root
ele = shadow.ele('#ele2')
5.1.6 简单示例
<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="su" class="btn self-btn bg s_btn">
我们可以用页面对象去获取其中的元素:
# 获取 id 为 one 的元素
div1 = page.ele('#one')
# 获取 name 属性为 row1 的元素
p1 = page.ele('@name=row1')
# 获取包含“第二个div”文本的元素
div2 = page.ele('第二个div')
# 获取所有div元素
div_list = page.eles('tag:div')
也可以获取到一个元素,然后在它里面或周围查找元素:
# 获取到一个元素div1
div1 = page.ele('#one')
# 在div1内查找所有p元素
p_list = div1.eles('tag:p')
# 获取div1后面一个元素
div2 = div1.next()
5.2 基本用法 📌
5.2.1 查找元素的方法
ele()
页面对象和元素对象都拥有此方法,用于查找其内部的一个条件匹配的元素。
页面对象和元素对象的ele()
方法参数名称稍有不同,但用法一样。
SessionPage
和ChromiumPage
获取元素的方法是一致的,但前者返回的元素对象为SessionElement
,后者是ChromiumElement
。
参数名称 | 类型 | 默认值 | 说明 |
---|---|---|---|
locator (元素对象) |
str Tuple[str, str] |
必填 | 元素的定位信息。可以是查询字符串,或 loc 元组 |
locator (页面对象) |
str SessionElement Tuple[str, str] |
必填 | 元素的定位信息。可以是查询字符串、loc 元组或一个SessionElement 对象 |
index |
int |
1 |
获取第几个匹配的元素,从1 开始,可输入负数表示从后面开始数 |
timeout |
float |
None |
等待元素出现的超时时间,为None 使用页面对象设置,SessionPage 中无效 |
返回类型 | 说明 |
---|---|
SessionElement |
SessionPage 或SessionElement 查找到的第一个符合条件的元素对象 |
ChromiumElement |
浏览器页面对象或元素对象查找到的第一个符合条件的元素对象 |
ChromiumFrame |
当结果是框架元素时,会返回ChromiumFrame ,但 IDE 中不会包含该提示 |
NoneElement |
未找到符合条件的元素时返回 |
说明
- loc 元组是指 selenium 定位符,例:(By.ID, 'XXXXX')。下同。
ele('xxxx', index=2)
和eles('xxxx')[1]
结果一样,不过前者会快很多。
示例:
from DrissionPage import SessionPage
page = SessionPage()
# 在页面内查找元素
ele1 = page.ele('#one')
# 在元素内查找后代元素
ele2 = ele1.ele('第二行')
eles()
此方法与ele()
相似,但返回的是匹配到的所有元素组成的列表。
页面对象和元素对象都可调用这个方法。
eles()
返回的是普通列表,链式操作需加下标,如page.eles('...')[0].ele('...')
。
参数名称 | 类型 | 默认值 | 说明 |
---|---|---|---|
locator |
str Tuple[str, str] |
必填 | 元素的定位信息,可以是查询字符串,或 loc 元组 |
timeout |
float |
None |
等待元素出现的超时时间,为None 使用页面对象设置,SessionPage 中无效 |
返回类型 | 说明 |
---|---|
List[SessionElement] |
SessionPage 或SessionElement 找到的所有元素组成的列表 |
List[ChromiumElement, ChromiumFrame] |
浏览器页面对象或元素对象找到的所有元素组成的列表 |
示例:
# 获取页面内的所有p元素
p_eles = page.eles('tag:p')
# 获取ele1元素内的所有p元素
p_eles = ele1.eles('tag:p')
# 打印第一个p元素的文本
print(p_eles[0])
5.2.2 匹配模式
精确匹配 =
表示精确匹配,匹配完全符合的文本或属性。
# 获取name属性为'row1'的元素
ele = page.ele('@name=row1')
模糊匹配 :
表示模糊匹配,匹配含有指定字符串的文本或属性。
# 获取name属性包含'row1'的元素
ele = page.ele('@name:row1')
匹配开头 ^
表示匹配开头,匹配开头为指定字符串的文本或属性。
# 获取name属性以'row1'开头的元素
ele = page.ele('@name^ro')
匹配结尾 $
表示匹配结尾,匹配结尾为指定字符串的文本或属性。
# 获取name属性以'w1'结尾的元素
ele = page.ele('@name$w1')
5.2.3 查找语法
id 匹配符 #
表示id
属性,只在语句最前面且单独使用时生效,可配合匹配模式使用。
# 在页面中查找id属性为one的元素
ele1 = page.ele('#one')
# 在ele1元素内查找id属性包含ne文本的元素
ele2 = ele1.ele('#:ne')
class 匹配符 .
表示class
属性,只在语句最前面且单独使用时生效,可配合匹配模式使用。
# 查找class属性为p_cls的元素
ele2 = ele1.ele('.p_cls')
# 查找class属性'_cls'文本开头的元素
ele2 = ele1.ele('.^_cls')
因为只加 .
时默认是精确匹配元素属性 class
,所以如果某元素有多个类名,必须写 class
属性的完整值(类名的顺序也不能变)。如果需要只匹配多个类名中的一个,可以使用模糊匹配符 :
。
# 精确查找class属性为`p_cls1 p_cls2 `的元素
ele2 = ele1.ele('.p_cls1 p_cls2 ')
# 模糊查找class属性含有类名 'p_cls2' 的元素
ele2 = ele1.ele('.:p_cls2')
若仍需要更复杂的匹配方式,请使用多属性匹配符。
单属性匹配符 @
表示某个属性,只匹配一个属性。
@
关键字只有一个简单功能,就是匹配@
后面的内容,不再对后面的字符串进行解析。因此即使后面的字符串也存在@
或@@
,也作为要匹配的内容对待。所以只要是多属性匹配,包括第一个属性在内的所有属性都必须用@@
开头。
注意
如果属性中包含特殊字符(如包含@
),用这个方式不能正确匹配到,需使用 css selector 方式查找。且特殊字符要用\
转义。
# 查找name属性为row1的元素
ele2 = ele1.ele('@name=row1')
# 查找name属性包含row文本的元素
ele2 = ele1.ele('@name:row')
# 查找name属性以row开头的元素
ele2 = ele1.ele('@name^row')
# 查找有name属性的元素
ele2 = ele1.ele('@name')
# 查找没有任何属性的元素
ele2 = ele1.ele('@')
# 查找email属性为abc@def.com的元素,有多个@也不会重复处理
ele2 = ele1.ele('@email=abc@def.com')
# 属性中有特殊字符的情形,匹配abc@def属性等于v的元素
ele2 = ele1.ele('css:div[abc\@def="v"]')
多属性与匹配符 @@
匹配同时符合多个条件的元素时使用,每个条件前面添加@@
作为开头。
注意
- 匹配文本或属性中出现
@@
、@|
、@!
时,不能使用多属性匹配,需改用 xpath 的方式。- 如果属性中包含特殊字符(如包含
@
),用这个方式不能正确匹配到,需使用 css selector 方式查找。且特殊字符要用\
转义。
# 查找name属性为row1且class属性包含cls文本的元素
ele2 = ele1.ele('@@name=row1@@class:cls')
@@
可以与下文介绍的tag
配合使用:
ele = page.ele('tag:div@@class=p_cls@@name=row1')