由于 selenium 的缺点过多,pyppeteer 使用较为复杂,也可以使用 DrissionPage 库来进行自动化测试。
DrissionPage 的优点有:
DrissionPage需要的环境为:
使用 pip 安装 DrissionPage:
pip install DrissionPage
DrissionPage 包含三种主要页面类。根据需要在其中选择使用。
ChromiumPage。from DrissionPage import ChromiumPageSessionPage。from DrissionPage import SessionPageWebPage是功能最全面的页面类,既可控制浏览器,也可收发数据包。from DrissionPage import WebPage另外 DrissionPage 还有一些配置工具类
| 类名 | 描述 | 引入方式 |
|---|---|---|
ChromiumOptions | 用于设置浏览器启动参数。这些参数只有在启动浏览器时有用,接管已存在的浏览器时是不生效的。 | from DrissionPage import ChromiumOptions |
SessionOptions | 用于配置SessionPage或WebPages 模式的连接参数。 | from DrissionPage import SessionOptions |
Settings | 用于设置全局运行配置,如找不到元素时是否抛出异常等。 | from DrissionPage.common import Settings |
Keys | 键盘按键类,用于键入 ctrl、alt 等按键。 | from DrissionPage.common import Keys |
Actions | 动作链,用于执行一系列动作。在浏览器页面对象中已有内置,无如特殊需要无需主动导入。 | from DrissionPage.common import Actions |
By | 与 selenium 一致的By类,便于项目迁移。 | from DrissionPage.common import By |
wait_until | 可等待传入的方法结果为真 | from DrissionPage.common import wait_until |
make_session_ele | 从 html 文本生成ChromiumElement对象 | from DrissionPage.common import make_session_ele |
configs_to_here | 把配置文件复制到当前路径 | from DrissionPage.common import configs_to_here |
get_blob | 获取指定的 blob 资源 | from DrissionPage.common import get_blob |
ElementNotFoundError | 异常类,全部在路径DrissionPage.errors里 | from DrissionPage.errors import ElementNotFoundError |
另外 DrissionPage 还有一些衍生对象,例如 Tab、Element 等对象是由 Page 对象生成的,开发过程中需要类型判断时需要导入这些类型。
from DrissionPage.items import SessionElement
from DrissionPage.items import ChromiumElement
from DrissionPage.items import ShadowRoot
from DrissionPage.items import NoneElement
from DrissionPage.items import ChromiumTab
from DrissionPage.items import WebPageTab
from DrissionPage.items import ChromiumFrame
如果只使用收发数据包功能,无需任何准备工作。
如果要控制浏览器,需设置浏览器路径。程序默认设置控制 Chrome,所以下面用 Chrome 演示。如果要使用 Edge 或其它 Chromium 内核浏览器,设置方法是一样的。
from DrissionPage import ChromiumPage
page = ChromiumPage()
page.get('http://g1879.gitee.io/DrissionPageDocs')

方法一:新建一个临时 py 文件,并输入以下代码,填入您电脑里的 Chrome 浏览器可执行文件路径,然后运行。
from DrissionPage import ChromiumOptions
path = r'D:\Chrome\Chrome.exe' # 请改为你电脑内Chrome可执行文件路径
ChromiumOptions().set_browser_path(path).save()
这段代码会把浏览器路径记录到配置文件,今后启动浏览器皆以新路径为准。另外,如果是想临时切换浏览器路径以尝试运行和操作是否正常,可以去掉 .save(),以如下方式结合第1️⃣步的代码。
from DrissionPage import ChromiumPage, ChromiumOptions
path = r'D:\Chrome\Chrome.exe' # 请改为你电脑内Chrome可执行文件路径
co = ChromiumOptions().set_browser_path(path)
page = ChromiumPage(co)
page.get('http://g1879.gitee.io/DrissionPageDocs')
方法二:在命令行输入以下命令(路径改成自己电脑里的):
dp -p D:\Chrome\chrome.exe注意要先使用 cd 命令定位到项目路径
DrissionPage 的三个主要的类 ChromiumPage 、SessionPage 和 WebPage 分别用于主要的三种模式:浏览器模式、数据包模式、混合模式。
DrissionPage 可以类似于 selenium 一样直接控制浏览器。
from DrissionPage import ChromiumPage
# 创建页面对象,并启动或接管浏览器
page = ChromiumPage()
# 跳转到登录页面
page.get('https://gitee.com/login')
# 定位到账号文本框,获取文本框元素
ele = page.ele('#user_login')
# 输入对文本框输入账号
ele.input('您的账号')
# 定位到密码文本框并输入密码
page.ele('#user_password').input('您的密码')
# 点击登录按钮
page.ele('@value=登 录').click()
DrissionPage 可以类似于 requests 一样直接收发数据包获取页面代码,并使用特定的解析语法解析文本获取数据。
from DrissionPage import SessionPage
# 创建页面对象
page = SessionPage()
# 爬取3页
for i in range(1, 4):
# 访问某一页的网页
page.get(f'https://gitee.com/explore/all?page={i}')
# 获取所有开源库元素列表
links = page.eles('.title project-namespace-path')
# 遍历所有元素
for link in links:
# 打印链接信息
print(link.text, link.link)
DrissionPage 还能在两种模式间切换执行。切换模式是用来应付登录检查很严格的网站,可以用浏览器处理登录,再转换模式用收发数据包的形式来采集数据。
from DrissionPage import WebPage
# 创建页面对象
page = WebPage()
# 访问网址
page.get('https://gitee.com/explore')
# 查找文本框元素并输入关键词
page('#q').input('DrissionPage')
# 点击搜索按钮
page('t:button@tx():搜索').click()
# 等待页面加载
page.wait.load_start()
# 切换到收发数据包模式
page.change_mode()
# 获取所有行元素
items = page('#hits-list').eles('.item')
# 遍历获取到的元素
for item in items:
# 打印元素文本
print(item('.title').text)
print(item('.desc').text)
print()
用ChromiumPage()创建页面对象。根据不同的配置,可以接管已打开的浏览器,也可以启动新的浏览器。程序结束时,被打开的浏览器不会主动关闭,以便下次运行程序时使用(由VSCode启动的会被关闭)。新手在使用无头模式时需注意,程序关闭后其实浏览器进程还在,只是看不见。
ChromiumPage和WebPage对象为单例,每个浏览器只能有一个该对象。对同一个浏览器重复使用ChromiumPage获取的都是同一个对象。
from DrissionPage import ChromiumPage
page = ChromiumPage()
创建ChromiumPage对象时会在指定端口启动浏览器,或接管该端口已有浏览器。默认情况下,程序使用 9222 端口,浏览器可执行文件路径为’chrome’。如路径中没找到浏览器可执行文件,Windows 系统下程序会在注册表中查找路径。
ChromiumPage创建时可接收三个参数:
| 初始化参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| addr_or_opts | str int ChromiumOptions | None | 浏览器启动配置或接管信息。传入 ‘ip: port’ 字符串、端口数字或ChromiumOptions对象时按配置启动或接管浏览器;为None时使用配置文件配置启动浏览器 |
| tab_id | str | None | 要控制的标签页 id,为None则控制激活的标签页 |
| timeout | float | None | 整体超时时间,为None则从配置文件中读取,默认10 |
如果需要已指定方式启动浏览器,可使用ChromiumOptions。它是专门用于设置浏览器初始状态的类,内置了常用的配置。
# 导入 ChromiumOptions
from DrissionPage import ChromiumPage, ChromiumOptions
# 创建浏览器配置对象,指定浏览器路径
co = ChromiumOptions().set_browser_path(r'D:\chrome.exe')
# 设置不加载图片、静音、禁用js
co.no_imgs(True).mute(True)no_js(True)
co.incognito() # 匿名模式(无痕模式)
co.headless() # 无头模式
# 另外也支持 Chromium 内核浏览器的一系列启动配置参数
co.set_argument('--no-sandbox') # 无沙盒模式
co.set_argument('--window-size', '800,600') # 设置初始窗口大小
co.set_argument('--start-maximized') # 设置启动时最大化
co.set_argument('--guest') # 使用来宾模式打开浏览器
# 已经配置的参数也可以删除
co.remove_argument('--start-maximized')
co.remove_argument('--window-size')
# 用该配置创建页面对象
page = ChromiumPage(addr_or_opts=co)
get() 方法用于跳转到一个网址,
from DrissionPage import ChromiumPage
page = ChromiumPage()
page.get('https://www.baidu.com')
该方法可以接收5个参数,并返回一个 bool 值,表示是否连接成功
| 参数名称 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| url | str | 必填 | 目标 url,可指向本地文件路径 |
| show_errmsg | bool | False | 连接出错时是否显示和抛出异常 |
| retry | int | None | 重试次数,为None时使用页面参数,默认 3 |
| interval | float | None | 重试间隔(秒),为None时使用页面参数,默认 2 |
| timeout | float | None | 加载超时时间(秒) |
加载模式是指程序在页面加载阶段的行为模式,有以下三种:
normal():常规模式,会等待页面加载完毕,超时自动重试或停止,默认使用此模式eager():加载完 DOM 或超时即停止加载,不加载页面资源none():超时也不会自动停止,除非加载完成前两种模式下,页面加载过程会阻塞程序,直到加载完毕才执行后面的操作。none()模式下,只在连接阶段阻塞程序,加载阶段可自行根据情况执行stop_loading()停止加载。这样提供给用户非常大的自由度,可等到关键数据包或元素出现就主动停止页面加载,大幅提升执行效率。
可以在配置对象中进行设置:
from DrissionPage import ChromiumOptions, ChromiumPage
co = ChromiumOptions().set_load_mode('none')
page = ChromiumPage(co)
也可以在运行中设置
from DrissionPage import ChromiumPage
page = ChromiumPage()
page.set.load_mode.eager()
page.get('https://g1879.gitee.io/drissionpagedocs')
from DrissionPage import ChromiumPage
page = ChromiumPage()
page.set.load_mode.none() # 设置加载模式为none
page.listen.start('api/getkeydata') # 指定监听目标并启动监听
page.get('http://www.hao123.com/') # 访问网站
packet = page.listen.wait() # 等待数据包
page.stop_loading() # 主动停止加载
print(packet.response.body) # 打印数据包正文
from DrissionPage import ChromiumPage
page = ChromiumPage()
page.set.load_mode.none() # 设置加载模式为none
page.get('http://www.hao123.com/') # 访问网站
ele = page.ele('中国日报') # 查找text包含“中国日报”的元素
page.stop_loading() # 主动停止加载
print(ele.text) # 打印元素text
from DrissionPage import ChromiumPage
page = ChromiumPage()
page.set.load_mode.none() # 设置加载模式为none
page.get('http://www.hao123.com/') # 访问网站
page.wait.title_change('hao123') # 等待title变化出现目标文本
page.stop_loading() # 主动停止加载
有一些常用的属性和方法能够获取页面的信息
htmlhtml 文本。返回类型:strjsonSessionPage 对象而不是 ChromiumPage。titletitle文本。返回类型:struser_agentuser agent 信息。返回类型:strsave(path:Union[str,Path]=None, name:str=None, as_pdf:bool=False,**kwargs)->Union[str, bytes]path、name和as_pdf 三个参数。如果path和name参数都为None,只返回内容,不保存文件。path为None保存到当前路径,name为None则使用 title 的值。as_pdf为True保存为pdf文件,默认False保存为 mhtml。另外如果 as_pdf 为True返回值为文件字节数据。urlurl。返回类型:strtab_idstrstates.is_loadingboolstates.is_aliveFalse。states.ready_state'connecting': 网页连接中,'loading':表示文档还在加载中,'interactive':DOM 已加载,但资源未加载完成,'complete':所有内容已完成加载。states.has_alertcookies(as_dict:bool=False, all_domains:bool=False, all_info:bool=False)->Union[dict, list]False:as_dict为True时返回字典类型,为False返回 cookie 组成的列表;all_domains是否返回所有 cookies,为False只返回当前 url 的;all_info返回的 cookies 是否包含所有信息,False时只包含name、value、domain信息session_storage(item:str=None)->Union[dict, str]local_storage(item:str=None)->Union[dict, str]session_storage()。一个 Tab 对象(ChromiumTab和WebPageTab)控制一个浏览器的标签页,是页面控制的主要单位。ChromiumPage和WebPage也控制一个标签页,只是它们增加了一些浏览器总体控制功能。
get(url:str, show_errmsg:bool=False, retry:int=None, interval:float=None, timeout:float=None)->boolback(steps:int=1)forward(steps:int=1)back()refresh()False 的 bool 参数,刷新时是否忽略缓存。stop_loading()set.blocked_urls(urls:Union[str, list, tuple, None])'*',传入 None 时清空已设置项。例如不加载css文件:page.set.blocked_urls('*.css*')run_js(script, *args, as_expr:bool=False, timetout:float=None)->Anyscript是js脚本文本或脚本路径;*args为传入js的参数,按顺序对应为arguments[0]、arguments[1]…;as_expr是否作为表达式运行,为True时args参数无效;timetout是脚本超时时间,为None则使用页面timeouts.script设置。返回值为脚本执行结果。run_js_loaded(script, *args, as_expr:bool=False, timetout:float=None)->Anyrun_js()set.cookies(cookies:Union[RequestsCookieJar, list, tuple, str, dict])cookie。可以接收CookieJar、list、tuple、str、dict格式的cookies。set.cookies.clear()set.cookies.remove(name:str, url:str=None, domain:str=None, path:str=None)set.session_storage(item:str, value:Union[str, False])item是要设置的项,value为str时是设置的值,为False时表示删除该项。set.local_storage(item:str, value:Union[str, False])set.session_storage()scroll.to_top()scroll.to_bottom()scroll.to_see(loc_or_ele:Union[str, tuple, ChromiumElement], center:Union[bool, None]=None)loc_or_ele是元素的定位信息,可以是元素、定位符;参数center是否尽量滚动到页面正中,为None时如果被遮挡,则滚动到页面正中。set.scroll.wait_complete(on_off:bool=True)handle_alert(accept:Union[bool, None]=True, send:str=None, timeout:float=None, next_one:bool=False)->Union[str, False]False。也可只获取提示框文本而不处理提示框。 还可以处理下一个出现的提示框。参数accept为提示框的处理方式,True表示确认,False表示取消,None不会按按钮但依然返回文本值;send处理 prompt 提示框时输入文本;timeout等待提示框出现的超时时间,为None时使用页面整体超时时间;next_one是否处理下一个出现的弹窗,为True时timeout参数无效。返回值为提示框内容文本或False未等到提示框。set.auto_handle_alert(on_off:bool=True, accept:bool=True)on_off表示开关设置,accept表示处理使用确定还是取消。quit(timeout:float=5, force:bool=True)Page对象而不能用于Tag对象。参数timeout为等待浏览器关闭超时时间(秒);force为关闭超时是否强制终止进程。DrissionPage使用了一套简洁的语法用于查找元素,支持内置等待、链式查找,同时也支持 css selector、xpath、selenium 原生的 loc 元组。
ele() 和 eles()ele() 方法和 eles() 方法类似,区别在于后者返回多个元素列表,前者可以接收一个默认值为 1 的index参数,表示返回获取的第几个匹配元素(可以为负值表示倒数)。
parent() 、child() 和 children()这三种方法为查找元素的父子元素,参数中可以接收查询语法。parent() 查询中可以接收数字,表示第几级父元素。child() 接收参数 index,表示返回第几个匹配到的元素。
next()、nexts() 和 prev()、prevs()这四种方法用于查找当前元素的前 / 后的同级节点。可以使用查询语法,单个查询也支持查找第几个同级节点。
after()、afters() 和 before()、befores()这四种方法用于查找当前元素之前之后的节点,范围不止同级节点,而是整个 DOM 文档。
匹配模式指某个查询中匹配条件的方式,有精确匹配、模糊匹配、匹配开头、匹配结尾四种。大部分查找语法默认使用精确匹配。
=ele = page.ele('@name=row1'):ele = page.ele('@name:row1')^ele = page.ele('@name^ro')$ele = page.ele('@name$w1')DrissionPage 支持css selector、xpath、selenium 原生的 loc 元组,这里只列举单独使用的查找语法。
#ele1 = page.ele('#one'),在ele1元素内查找id属性包含ne文本的元素:ele2 = ele1.ele('#:ne') ..:。例如:查找class属性为p_cls的元素:ele2 = ele1.ele('.p_cls');精确查找class属性为p_cls1 p_cls2 的元素:ele2 = ele1.ele('.p_cls1 p_cls2 ');模糊查找class属性含有类名 ‘p_cls2’ 的元素:ele2 = ele1.ele('.:p_cls2')@ele2 = ele1.ele('@name=row1');查找有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"]')@@@@作为开头。例如:查找name属性为row1且class属性包含cls文本的元素ele2 = ele1.ele('@@name=row1@@class:cls')@|@|作为开头。用法与@@一致,需注意不能同 @@ 同时出现在语句中。@!@@或@|混用,也可单独使用。textele2 = ele1.ele('text=第二行');查找文本包含“第二”的元素ele2 = ele1.ele('text:第二')。text()@或@@配合使用。tag@、@@或@|配合使用。tag:与tag=效果一致,没有tag^和tag$语法。csscss:与css=效果一致,没有css^和css$语法。xpathxpath:与xpath=效果一致,没有xpath^和xpath$语法。另外,元素对象的ele()支持完整的 xpath 语法,如能使用 xpath 直接获取元素属性(字符串类型)。page.ele((By.XPATH, '//p[@class="p_cls"]')) 虽然 frame 也能够使用 ele() 方法查找到,并且可以穿透 iframe 标签进行操作(页面对象支持跨级查找,元素对象并不支持),但是不建议这样使用。建议使用 Page 对象的 get_frame() 方法来获取和操作 frame元素。
例如:
iframe = page.get_frame(1)iframe = page.get_frame('#theFrame')对于 frame 元素,可以看为一个特殊的元素对象,或页面对象来对待。可以同时操作多个 frame,无需切换。
为进一步精简代码,定位语法都可以用简化形式来表示,使语句更短,链式操作时更清晰。
| 原操作 | 简化后 | 说明 |
|---|---|---|
page.ele() | page() | 页面对象定位精简 |
page.ele(@id=xxx) | page(#xxx) | id查找精简 |
page.ele(@class=xxx) | page(.xxx) | class查找精简 |
page.ele(text=xxx) | page(tx=xxx) | text查找精简 |
page.ele(@text()=xxx) | page(@tx()=xxx) | text()查找精简 |
page.ele(tag:img) | page(t:img) | tag 查找精简 |
page.ele(xpath://xxxx) | page(x://xxxx) | xpath查找精简 |
page.ele(css:xxx) | page(c:xxxx) | css 查找精简 |
一些常用的属性和方法能够获取常用的元素数据信息:
tagtextattrsattre()linkpagestyle()states.is_checkedstates.is_selected元素中的项是否选中。states.is_enabledstates.is_displayedsrc()src属性所使用的资源。base64 的可转为bytes返回,其它的以str返回。无资源的返回None。click()click.right()click.at()click.to_upload()click.to_download()clear()input()from DrissionPage.common import Keys,例如:ele.input((Keys.CTRL, 'a', Keys.DEL)) drag()drag_to()hover()run_js()run_js()