0


告别 Selenium 时代!新的高效丝滑网页自动化库

告别 Selenium 时代!新的高效丝滑网页自动化库

  • 公众号:人生只不过是一场投资

作为一名玩 Python 自动化多年的老玩家,浏览器自动化是家常便饭。过去一直在用 Selenium 网站自动化,最近想更新一些项目代码,但总觉得这 se 各种问题,度娘后发现 DrissionPage,试用之后,我只能说:真香!

DrissionPage 同样是一款基于 Python 的网页自动化工具,不仅可以像 Selenium、Playwright、Puppeteer一样控制浏览器进行操作,还能直接收发数据包,甚至可以将两者结合使用,兼顾便利性和效率。

让我眼前一亮的是 DrissionPage 的以下几个特点:

  • 无需 WebDriver: 不同版本的浏览器告别繁琐的驱动下载和配置,开箱即用。
  • 速度飞快: 基于 CDP底层架构全面优化,Google 开发的,用来操控浏览器的工具和规范。。
  • 操作便捷: 可以跨 iframe 查找元素,无需切入切出;支持同时操作多个标签页,即使标签页为非激活状态,无需切换。
  • 避免与服务器过度交互:可以直接读取浏览器缓存来保存图片,无需用 GUI 点击另存或者其他结合下载。
  • 截图自由:可以对整个网页截图,包括视口外的部分(90以上版本浏览器支持)。
  • 功能强大: 支持处理非 open 状态的 shadow-root,轻松应对复杂网页结构。

安装 & 升级

# 安装
pip install DrissionPage

# 升级
pip install DrissionPage --upgrade

webdriver 和 bot 检测

from DrissionPage import ChromiumOptions
from DrissionPage import ChromiumPage

co = ChromiumOptions()
co.set_paths(browser_path=r'D:\Chrome\chrome.exe')
page = ChromiumPage(co)# 该网站可以检测浏览器自动化是否为正常用户行为
page.get('https://bot.sannysoft.com/')print(page.html)

该库的层次(对象关系)图

├─ SessionPage
|     └─ SessionElement
|           └─ SessionElement
├─ ChromiumPage
|     ├─ ChromiumTab
||     └─ ChromiumElement
||     └─ SessionElement
|     ├─ ChromiumFrame
||     └─ ChromiumElement
||     └─ SessionElement
|     ├─ ChromiumElement
||     └─ ChromiumElement
||     └─ SessionElement
|     └─ ChromiumShadowElement
|           └─ ChromiumElement
|           └─ SessionElement
├─ WebPage
|     ├─ ChromiumTab
||     └─ ChromiumElement
||     └─ SessionElement
|     ├─ ChromiumFrame
||     └─ ChromiumElement
||     └─ SessionElement
|     ├─ ChromiumElement
||     └─ ChromiumElement
||     └─ SessionElement
|     ├─ ChromiumShadowElement
||     └─ ChromiumElement
||     └─ SessionElement
|     └─ SessionElement
|           └─ SessionElement
├─ SessionOptions
└─ ChrmoiumOptions

主要对象

主页面对象有 3 种,它们通常是程序的入口:

  • ChromiumPage:单纯用于操作浏览器的页面对象。
  • WebPage:整合浏览器控制和收发数据包于一体的页面对象。
  • SessionPage:单纯用于收发数据包的页面对象。

衍生对象:

  • ChromiumTab:ChromiumPage生成的标签页对象。
  • WebPageTab:WebPage生成的标签页对象。
  • ChromiumFrame<iframe> 元素对象。
  • ChromiumElement:浏览器元素对象。
  • SessionElement:静态元素对象。
  • ShadowRoot:shadow-root 元素对象。

示例:淘宝网登录

from DrissionPage import ChromiumOptions
from DrissionPage import ChromiumPage

co = ChromiumOptions()# 设置我的 Chrome 浏览器路径。# - 我的浏览器是自己解压的绿色包,如果你是下载的 exe 文件安装的,不用写。
co.set_paths(browser_path=r'D:\Chrome\chrome.exe')
page = ChromiumPage(co)# page.get('https://login.taobao.com')# 查找元素:xpath 的方式# - 语法:x 代表 xpath, 是 xpath 的简写。
account = page.ele('x://input[@id="fm-login-id"]')
account.input(123456)
password = page.ele('x://input[@id="fm-login-password"]')
password.input(987654)# 查找元素:css 的方式# - 语法:c 代表 css, 是 css 的简写。
login_btn = page.ele('c:fm-button fm-submit password-login')
login_btn.click()

浏览器页面信息

"""返回当前页面html文本"""
page.html

"""返回当前页面title"""
page.title

"""当返回内容是json格式时,返回对应的字典,非json格式时返回None"""
page.json

"""返回user agent"""
page.user_agent

"""返回所控制的浏览器版本号"""
page.browser_version

"""把当前页面保存为文件,如果path和name参数都为None,只返回文本
:param path: 保存路径,为None且name不为None时保存在当前路径
:param name: 文件名,为None且path不为None时用title属性值
:param as_pdf: 为Ture保存为pdf,否则为mhtml且忽略kwargs参数
:param kwargs: pdf生成参数
:return: as_pdf为True时返回bytes,否则返回文件文本
"""
page.save(path=None, name=None, as_pdf=False,**kwargs)

浏览器状态信息

"""返回当前页面url"""
page.url

"""地址和端口号"""
page.address
# - 输出:127.0.0.1:9222"""返回浏览器进程 id"""
page.process_id

浏览器窗口信息

"""返回页面总宽高,格式:(宽, 高)"""
page.rect.size
# - 返回:格式 - (宽, 高)。tuple[int, int]"""返回窗口大小"""
page.rect.window_size
# - 返回:格式 - (宽, 高)。tuple[int, int]"""返回视口在屏幕中坐标,左上角为(0, 0)"""
page.rect.window_location
# - 返回:左上角为 (0, 0)。tuple[int, int]

ele 元素状态信息

"""返回元素是否出现在视口中,以元素click_point为判断"""
ele.states.is_in_viewport
# 返回 bool"""返回元素是否整个都在视口内"""
ele.states.is_whole_in_viewport
# 返回 bool"""是否仍可用。可判断 WebPage 的 d 模式下是否因为刷新而导致元素失效"""
ele.states.is_alive
# 返回 bool"""表单单选或多选元素是否被选中"""
ele.states.is_checked
# 返回 bool"""返回 select 元素是否被选择"""
ele.states.is_selected
# 返回 bool"""返回元素是否可用"""
ele.states.is_enabled
# 返回 bool"""返回元素是否显示"""
ele.states.is_displayed
# 返回 bool"""返回元素是否被覆盖,与是否在视口中无关,如被覆盖返回覆盖元素的backend id,否则返回False"""
ele.states.is_covered

"""返回元素是否可被模拟点击,从是否有大小、是否可用、是否显示、是否响应点击判断,不判断是否被遮挡"""
ele.states.is_clickable

保存元素(特色功能)

DrissionPage 特色功能,直接读取浏览器缓存,返回元素上 src 属性所使用的资源,base64 可转为 bytes 类型,其他无资源返回 None,有返回资源字符串。例如:图片、视频等。

src

"""返回元素src资源,base64的可转为bytes返回,其它返回str
:param timeout: 等待资源加载的超时时间(秒)
:param base64_to_bytes: 为True时,如果是base64数据,转换为bytes格式
:return: 有资源返回 str ,否则 None
"""
ele.src(timeout=None, base64_to_bytes=True)

save

保存 src 方法获取到的资源到路径

"""保存图片或其它有src属性的元素的资源
:param path: 文件保存路径,为None时保存到当前文件夹
:param name: 文件名称,为None时从资源url获取
:param timeout: 等待资源加载的超时时间(秒)
:param rename: 遇到重名文件时是否自动重命名
:return: 返回保存路径
"""
ele.save(path=None, name=None, timeout=None, rename=True)

元素交互

点击

# 左单击,两者同理# - by_js 为 False 且元素不可用、不可见 返回 False,其他 True。
ele.click(by_js=False, timeout=1.5, wait_stop=True)
ele.click.left(by_js=False, timeout=1.5, wait_stop=True)# 右单击
ele.click.right()# 中键单击# - 当 get_tab=True ,“ChromiumPage”返回“ChromiumTab”对象# - “WebPage”返回“WebPageTab”
ele.click.middle(get_tab=True)# - 参数为 False 返回 None # 左键多次点击,默认 2 次
ele.click.multi(time=2)"""带偏移量点击本元素,相对于左上角坐标。不传入x或y值时点击元素中间点
:param offset_x: 相对元素左上角坐标的x轴偏移量
:param offset_y: 相对元素左上角坐标的y轴偏移量
:param button: 点击哪个键,可选 left, middle, right, back, forward
:param count: 点击次数
:return: None
"""
ele.click.at(offset_x:float=None, offset_y:float=None, button:str='left', count:int=1)

模拟文件上传

模拟点击上传(或浏览器显示拖动上传的元素)

"""触发上传文件选择框并自动填入指定路径
:param file_paths: 文件路径,如果上传框支持多文件,可传入列表或字符串,字符串时多个文件用回车分隔
:param by_js: 是否用js方式点击,逻辑与click()一致
:return: None
"""
ele.click.to_upload(file_paths, by_js)

模拟文件下载

模拟点击下载

"""点击触发下载
:param save_path: 保存路径,为None保存在原来设置的,如未设置保存到当前路径
:param rename: 重命名文件名
:param suffix: 指定文件后缀
:param new_tab: 该下载是否在新tab中触发
:param by_js: 是否用js方式点击,逻辑与click()一致
:param timeout: 等待下载触发的超时时间,为None则使用页面对象设置
:return: DownloadMission对象
"""
ele.click.to_download(save_path=None, rename=None, suffix='left', new_tab=1, by_js=False, timeout=None)

点击返回新 tab 对象

"""
点击后等待新tab出现并返回其对象
:param by_js: 是否使用js点击,逻辑与click()一致
:return: 新标签页对象,如果没有等到新标签页出现则抛出异常
"""
ele.click.for_new_tab()

input 框输入、清空

"""清空元素文本
:param by_js: 是否用js方式清空,为False则用全选+del模拟输入删除
:return: None
"""
ele.clear(by_js=False)"""输入文本或组合键,也可用于输入文件路径到input元素(路径间用\n间隔)
:param vals: 文本值或按键组合
:param clear: 输入前是否清空文本框
:param by_js: 是否用js方式输入,不能输入组合键
:return: None
"""
ele.input(vals=False, clear=False, by_js=False)

输入组合键示例

内置 6 个常用组合键,分别是:

  • CTRL_A
  • CTRL_C
  • CTRL_X
  • CTRL_V
  • CTRL_Z
  • CTRL_Y
from DrissionPage.common import Keys

# ctrl + a + delete
ele.input((Keys.CTRL,'a', Keys.DEL))# 全选
ele.input(Keys.CTRL_A)

焦点

# 使元素获取焦点
ele.focus()

元素滚动

# 滚动到顶部
ele.scroll.to_top()# 滚动到底部
ele.scroll.to_bottom()# 垂直中间位置,水平不变
ele.scroll.to_half()# 滚动到最右边
ele.scroll.to_rightmost()# 滚动到最左边
ele.scroll.to_leftmost()# 滚动到指定位置# - x: int 必填,水平位置# - y: int 必填,垂直位置
ele.scroll.to_location(x, y)# 向上滚动 200 像素
ele.scroll.up(200)# 向下滚动 200 像素
ele.scroll.down(200)# 向左滚动 200 像素
ele.scroll.left(200)# 向右滚动 200 像素
ele.scroll.right(200)# 滚动页面使自己可见
ele.scroll.to_see()# 尽量滚动到可视口正中
ele.scroll.to_center()

页面对象等待

等待进入加载状态

"""等待页面开始加载
:param timeout: 超时时间,为None时使用页面timeout属性
:param raise_err: 等待失败时是否报错,为None时根据Settings设置
:return: 是否等待成功
"""
page.wait.load_start(timeout=None, raise_err=None)

等待页面加载完成

  • 此功能仅用于等待页面主 document 加载,不能用于等待 js 加载的变化。
  • 除非配置文件中 load_modeNoneget() 方法已内置等待加载完成,后面无须添加等待。
"""等待页面加载完成
:param timeout: 超时时间,为None时使用页面timeout属性
:param raise_err: 等待失败时是否报错,为None时根据Settings设置
:return: 是否等待成功
"""
page.wait.doc_loaded(timeout=None, raise_err=None)

等待元素被加载到 DOM

  • 等待元素加载到DOM,可等待全部或任意一个
"""可等待全部或任意一个
:param locators: 要等待的元素,输入定位符,用list输入多个
:param timeout: 超时时间,默认读取页面超时时间
:param any_one: 是否等待到一个就返回
:param raise_err: 等待失败时是否报错,为None时根据Settings设置
:return: 成功返回True,失败返回False
"""
page.wait.eles_loaded(locators, timeout=None, any_one=False, raise_err=None)

等待元素变成显示状态

"""
:param loc_or_ele: 要等待的元素,可以是已有元素、定位符
:param timeout: 超时时间,默认读取页面超时时间
:param raise_err: 等待失败时是否报错,为None时根据Settings设置
:return: 是否等待成功
"""
page.wait.ele_displayed(loc_or_ele, timeout=None, raise_err=None)

等待元素变成隐藏状态

"""
:param loc_or_ele: 要等待的元素,可以是已有元素、定位符
:param timeout: 超时时间,默认读取页面超时时间
:param raise_err: 等待失败时是否报错,为None时根据Settings设置
:return: 是否等待成功
"""
page.wait.ele_hidden(loc_or_ele, timeout=None, raise_err=None)

等待元素从DOM中删除

"""
:param loc_or_ele: 要等待的元素,可以是已有元素、定位符
:param timeout: 超时时间,默认读取页面超时时间
:param raise_err: 等待失败时是否报错,为None时根据Settings设置
:return: 是否等待成功
"""
page.wait.ele_deleted(loc_or_ele, timeout=None, raise_err=None)

等待浏览器下载开始,可将其拦截

"""
:param timeout: 超时时间,None使用页面对象超时时间
:param cancel_it: 是否取消该任务
:return: 成功返回任务对象,失败返回False
"""
page.wait.download_begin(timeout=None, cancel_it=False)

等待新标签页出现

"""
:param timeout: 等待超时时间,为None则使用页面对象timeout属性
:param raise_err: 等待失败时是否报错,为None时根据Settings设置
:return: 等到新标签页返回其id,否则返回False
"""
page.wait.new_tab(timeout=None, raise_err=None)

等待title变成包含或不包含指定文本

"""
:param text: 用于识别的文本
:param exclude: 是否排除,为True时当title不包含text指定文本时返回True
:param timeout: 超时时间
:param raise_err: 等待失败时是否报错,为None时根据Settings设置
:return: 是否等待成功
"""
page.wait.title_change(text, exclude=False, timeout=None, raise_err=None)

等待url变成包含或不包含指定文本

"""
:param text: 用于识别的文本
:param exclude: 是否排除,为True时当url不包含text指定文本时返回True
:param timeout: 超时时间
:param raise_err: 等待失败时是否报错,为None时根据Settings设置
:return: 是否等待成功
"""
page.wait.url_change(text, exclude=False, timeout=None, raise_err=None)

元素等待的方法

等待元素从dom显示

"""
:param timeout: 超时时间,为None使用元素所在页面timeout属性
:param raise_err: 等待失败时是否报错,为None时根据Settings设置
:return: 是否等待成功
"""
ele.wait.displayed(timeout=None, raise_err=None)

等待元素从dom隐藏

"""
:param timeout: 超时时间,为None使用元素所在页面timeout属性
:param raise_err: 等待失败时是否报错,为None时根据Settings设置
:return: 是否等待成功
"""
ele.wait.hidden(timeout=None, raise_err=None)

等待当前元素变成不可用或从DOM移除

"""
:param timeout: 超时时间,为None使用元素所在页面timeout属性
:param raise_err: 等待失败时是否报错,为None时根据Settings设置
:return: 是否等待成功
"""
ele.wait.disabled_or_deleted(timeout=None, raise_err=None)

等待当前元素被遮盖

"""
:param timeout: 超时时间,为None使用元素所在页面timeout属性
:param raise_err: 等待失败时是否报错,为None时根据Settings设置
:return: 是否等待成功
"""
ele.wait.covered(timeout=None, raise_err=None)

等待当前元素不被遮盖

"""
:param timeout: 超时时间,为None使用元素所在页面timeout属性
:param raise_err: 等待失败时是否报错,为None时根据Settings设置
:return: 是否等待成功
"""
ele.wait.not_covered(timeout=None, raise_err=None)

等待当前元素变成可用

"""
:param timeout: 超时时间,为None使用元素所在页面timeout属性
:param raise_err: 等待失败时是否报错,为None时根据Settings设置
:return: 是否等待成功
"""
ele.wait.enabled(timeout=None, raise_err=None)

等待当前元素变成不可用

"""
:param timeout: 超时时间,为None使用元素所在页面timeout属性
:param raise_err: 等待失败时是否报错,为None时根据Settings设置
:return: 是否等待成功
"""
ele.wait.disabled(timeout=None, raise_err=None)

等待当前元素停止运动

"""
:param timeout: 超时时间,为None使用元素所在页面timeout属性
:param gap: 检测间隔时间
:param raise_err: 等待失败时是否报错,为None时根据Settings设置
:return: 是否等待成功
"""
ele.wait.stop_moving(timeout=None, gap=.1, raise_err=None)

等待当前元素可被点击

"""
:param wait_moved: 是否等待元素运动结束
:param timeout: 超时时间,为None使用元素所在页面timeout属性
:param raise_err: 等待失败时是否报错,为None时根据Settings设置
:return: 是否等待成功
"""
ele.wait.clickable(wait_moved=True, timeout=None, raise_err=None)

等待当前元素变成不可用或从DOM移除

"""
:param timeout: 超时时间,为None使用元素所在页面timeout属性
:param raise_err: 等待失败时是否报错,为None时根据Settings设置
:return: 是否等待成功
"""
ele.wait.disabled_or_deleted(timeout=None, raise_err=None)

标签页管理

关闭Page管理的标签页

page.close()

关闭指定 Tab

"""关闭传入的标签页,默认关闭当前页。可传入多个
:param tabs_or_ids: 要关闭的标签页对象或id,可传入列表或元组,为None时关闭当前页
:param others: 是否关闭指定标签页之外的
:return: None
"""
page.close_tabs(tabs_or_ids=None, others=False)

激活标签页使其处于最前面

"""
:param tab_or_id: 标签页对象或id,为None表示当前标签页
:return: None
"""
page.set.tab_to_front(tab_or_id=None)

iframe

获取页面中一个frame对象

"""
:param loc_ind_ele: 定位符、iframe序号、ChromiumFrame对象,序号从1开始,可传入负数获取倒数第几个
:param timeout: 查找元素超时时间(秒)
:return: ChromiumFrame对象
"""
page.get_frame(loc_ind_ele, timeout=None)# 两个方式都一样:
iframe = page('#sss')
iframe = page.get_frame(iframe)# 两个方式都一样:
ele = iframe('首页')
ele = iframe.ele('首页')print(ele)

获取所有符合条件的frame对象

"""
:param locator: 定位符,为None时返回所有
:param timeout: 查找超时时间(秒)
:return: ChromiumFrame对象组成的列表
"""
page.get_frames(locator=None, timeout=None)

官方文档

更多:


本文转载自: https://blog.csdn.net/diy520just/article/details/139252126
版权归原作者 ょ镜花う水月 所有, 如有侵权,请联系我们删除。

“告别 Selenium 时代!新的高效丝滑网页自动化库”的评论:

还没有评论