DrissionPage:类似 selenuium 的网页自动化工具。这是一个基于 Python 的网页自动化工具,支持 Chromium 内核浏览器。它将控制浏览器和收发请求两大功能合二为一,并提供了统一、简洁的接口。
环境
操作系统:Windows、Linux 或 Mac。
python 版本:3.6 及以上
支持应用:Chromium 内核浏览器(如 Chrome、Edge),electron 应用
github - DrissionPage
文档 - DrissionPage
DrissionPageJava: DrissionPage的Jave版本
SaossionPage: Drissionpage 库的兄弟版 SaossionPage 简称骚神
元素定位助手 和 代码生成助手: https://gitee.com/haiyang0726/SaossionPage/releases
DataRecorder
数据写入
查找元素,无需切入切出
看作普通元素,获取后可直接在其中查找元素,逻辑更清晰open
状态的 shadow-root可能之所以不被检测到,是因为DrissionPage的底层基于cdp协议(Chrome DevTools Protocol)),
以下是懒神推荐读的cdp代码 https://chromedevtools.github.io/devtools-protocol/
什么是cdp
众所周知F12能够打开开发者工具Q进行调试,开发者工具即DevTools大家应该都知道,那么ChromeDevTools就是用的CDP协议来跟浏览器进行交互的调试等一系列操作的。CDP是通过RESTfulAPI提供了对浏览器内部运行情况的访问,可以通过这些API来控制Chrome浏览器的行为,来做到与DevTools类似的功能:获取页面信息、监控网络活动、执行JS等操作。cdp协议简称chrome调试协议,是基于scoket(websocketQ、usb、adb)消息的jsonrpc协议。用来调用chrome内部的方法实现js,css,dom的开发调试。可以将实现了cdp协议的应用看做rpc调用的服务端(chrome,puppeteer),将调试面板看做rpc调用的客户端(devtools)。
pip install DrissionPage
#更新较快,快进行更新
pip install DrissionPage --upgrade
默认配置在这里
D:\ruanjian\PY\Miniconda3\Lib\site-packages\DrissionPage\_configs\config.ini
[paths]
download_path =
tmp_path =
[chromium_options]
address = 127.0.0.1:9222
browser_path = chrome
arguments = ['--no-default-browser-check', '--disable-suggestions-ui', '--no-first-run', '--disable-infobars', '--disable-popup-blocking', '--hide-crash-restore-bubble', '--disable-features=PrivacySandboxSettings4']
extensions = []
prefs = {'profile.default_content_settings.popups': 0, 'profile.default_content_setting_values': {'notifications': 2}}
flags = {}
load_mode = normal
user = Default
auto_port = False
system_user_path = False
existing_only = False
[session_options]
headers = {'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/603.3.8 (KHTML, like Gecko) Version/10.1.2 Safari/603.3.8', 'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 'connection': 'keep-alive', 'accept-charset': 'GB2312,utf-8;q=0.7,*;q=0.7'}
[timeouts]
base = 10
page_load = 30
script = 30
[proxies]
http =
https =
[others]
retry_times = 3
retry_interval = 2
SessionPage
对象和WebPage
对象的 s 模式,可用收发数据包的形式访问网页。
顾名思义,SessionPage
是一个使用使用Session
(requests 库)对象的页面,它使用 POM 模式封装了网络连接和 html 解析功能,使收发数据包也可以像操作页面一样便利。
并且,由于加入了本库独创的查找元素方法,使数据的采集便利性远超 requests + beautifulsoup 等组合。
如果需要在使用前进行一些配置,可使用SessionOptions。它是专门用于设置Session对象初始状态的类,内置了常用的配置。详细使用方法见“启动配置”一节。
从指定 ini 文件创建
以上方法是使用默认 ini 文件中保存的配置信息创建对象,你可以保存一个 ini 文件到别的地方,并在创建对象时指定使用它。
from DrissionPage import SessionPage, SessionOptions
# 创建配置对象时指定要读取的ini文件路径
so = SessionOptions(ini_path=r'./config1.ini')
# 使用该配置对象创建页面
page = SessionPage(session_or_options=so)
不使用 ini 文件
可以用以下方法,指定不使用 ini 文件的配置,而把配置写在代码中。
from DrissionPage import SessionPage, SessionOptions
so = SessionOptions(read_file=False) # read_file设为False
so.set_retry(5)
page = SessionPage(so)
当需要使用多个页面对象共同操作一个页面时,可在页面对象创建时接收另一个页面间对象传递过来的Session对象,以达到多个页面对象同时使用一个Session对象的效果。
# 创建一个页面
page1 = SessionPage()
# 获取页面对象内置的Session对象
session = page1.session
# 在第二个页面对象初始化时传递该对象
page2 = SessionPage(session_or_options=session)
get()
方法语法与 requests 的get()
方法一致,在此基础上增加了连接失败重试功能。与 requests 不一样的是,它不返回Response
对象。
参数名称 | 类型 | 默认值 | 说明 |
---|---|---|---|
url |
str |
必填 | 目标 url |
show_errmsg |
bool |
False |
连接出错时是否显示和抛出异常 |
retry |
int |
None |
重试次数,为None 时使用页面参数,默认 3 |
interval |
float |
None |
重试间隔(秒),为None 时使用页面参数,默认 2 |
timeout |
float |
None |
加载超时时间(秒) |
**kwargs |
- | None |
连接所需其它参数,具体见 requests 用法 |
读取本地文件
get()
的url
参数可指向本地文件,实现本地 html 解析。
from DrissionPage import SessionPage
page = SessionPage()
page.get(r'D:\demo.html')
访问在线网页
from DrissionPage import SessionPage
page = SessionPage()
url = 'https://www.baidu.com'
headers = {'referer': 'gitee.com'}
cookies = {'name': 'value'}
proxies = {'http': '127.0.0.1:1080', 'https': '127.0.0.1:1080'}
page.get(url, headers=headers, cookies=cookies, proxies=proxies)
post 方式请求页面。用法与get()一致。返回bool
# 向 data 参数传入字符串
page.post(url, data='abc=123')
# 向 data 参数传入字典
page.post(url, data={'abc': '123'})
# 向 json 参数传入字符串
page.post(url, json='abc=123')
# 向 json 参数传入字典
page.post(url, json={'abc': '123'})
page.json()
ChromiumPage
对象和WebPage
对象的 d 模式,可操控浏览器。本章介绍ChromiumPage
。
顾名思义,ChromiumPage
是 Chromium 内核浏览器的页面,它用 POM 方式封装了操控网页所需的属性和方法。
使用它,我们可与网页进行交互,如调整窗口大小、滚动页面、操作弹出框等等。
通过从中获取的元素对象,我们还可以跟页面中的元素进行交互,如输入文字、点击按钮、选择下拉菜单等等。
甚至,我们可以在页面或元素上运行 JavaScript 代码、修改元素属性、增删元素等。
除了与页面和元素的交互,ChromiumPage
还扮演着浏览器控制器的角色,可以说,一个ChromiumPage
对象,就是一个浏览器。
它可以对标签页进行管理,可以对下载任务进行控制。可以为每个标签页生成独立的页面对象(ChromiumTab
),以实现多标签页同时操作,而无需切入切出。
会使用默认配置,自动生成页面对象
只要这个浏览器不关闭,下次运行程序时会接管同一个浏览器继续操作(配置的 ip: port 信息不变)。这种方式极大地方便了程序的调试,使程序不必每次重新开始,可以单独调试某个功能。
from DrissionPage import ChromiumPage
# 创建对象同时启动浏览器,如果浏览器已经存在,则接管它
page = ChromiumPage()
页面对象创建时,只要指定的地址(ip: port)已有浏览器在运行,就会直接接管。无论浏览器是下面哪种方式启动的。
如果想要同时操作多个浏览器,或者自己在使用其中一个上网,同时控制另外几个跑自动化,就需要给这些被程序控制的浏览器设置单独的端口和用户文件夹,否则会造成冲突。
from DrissionPage import ChromiumPage, ChromiumOptions
co = ChromiumOptions().set_paths(local_port=9999, user_data_path='./user_data')
page = ChromiumPage(addr_or_opts=co)
page.get('https://www.baidu.com')
ChromiumOptions对象的auto_port()
方法,可以指定程序每次使用空闲的端口和临时用户文件夹创建浏览器。也是每个浏览器要使用独立的ChromiumOptions对象。但这种方法创建的浏览器不能重复使用。
# 初始化Chromium浏览器选项
co = ChromiumOptions().auto_port()
# 创建Chromium页面对象
page = ChromiumPage(co)
这种情况下用户不能打开一个浏览器使用
初始默认配置下,程序会为每个使用的端口创建空的用户目录,并且每次接管都使用,这样可以有效避免浏览器冲突。
有些时候我们希望使用系统安装的浏览器的默认用户文件夹。以便复用用户信息和插件等。
from DrissionPage import ChromiumPage, ChromiumOptions
co = ChromiumOptions().use_system_user_path()
page = ChromiumPage(co)
使用 ini 文件
把这个配置记录到 ini 文件,就不用每次使用都配置。
from DrissionPage import ChromiumOptions
ChromiumOptions().use_system_user_path().save()
ChromiumOptions对象下的方法
[!NOTE] set_argument():用于设置启动参数。
- arg [str] 必填 启动参数名称
- value[str|None|False]=None 参数的值。带值的参数传入属性值,没有值的传入None。如传入False,删除该参数。
[!NOTE] remove_argument()
Contents
[!NOTE] clear_arguments()
Contents
# 设置启动时最大化
co.set_argument('--start-maximized')
# 设置初始窗口大小
co.set_argument('--window-size', '800,600')
# 使用来宾模式打开浏览器
co.set_argument('--guest')
#禁用通知警告
co.set_argument('--disable-notifications')
#禁止所有弹出窗口
co.set_pref(arg='profile.default_content_settings.popups', value='0')
#阻止“自动保存密码”的提示气泡
co.set_pref('credentials_enable_service', False)
#阻止“要恢复页面吗?Chrome未正确关闭”的提示气泡
co.set_argument('--hide-crash-restore-bubble')
#通知用户他们的浏览器是由自动测试控制的。
co.set_argument('--enable-automation')
set_browser_path()
set_tmp_path()
set_local_port()
set_address()
auto_port()
set_user_data_path()
use_system_user_path()
set_cache_path()
existing_only()
使用插件
add_extension()
remove_extensions()
参数
ChromiumOptions()对象.方法名
运行参数设置
set_timeouts(base,page_load,script):用于设置几种超时时间,以秒为单位
set_load_mode():用于设置网页加载策略。加载策略是指强制页面停止加载的时机,如加载完 DOM 即停止,不加载图片资源等,以提高自动化效率。
'normal'
:阻塞进程,等待所有资源下载完成(默认)'eager'
:DOM 就绪即停止加载'none'
:网页连接成功即停止加载set_proxy():用于设置浏览器代理。
常用设置
set_paths():快捷的路径设置函数
C:\Program Files\Google\Chrome\Application\chrome.exe
一般来说用户文件夹的名称是 User Data
。对于默认情况下的 Windows 中的 Chrome 浏览器来说,此文件夹位于 %USERPROFILE%\AppData\Local\Google\Chrome\User Data\
实际路径请在浏览器输入 chrome://version/
,查阅其中的个人资料路径
或者叫用户配置路径
。
set_tmp_path():用于设置临时文件存放路径。
auto_port():设置是否使用自动分配的端口,启动一个全新的浏览器。
set_user_data_path()此方法用于设置用户文件夹路径。用户文件夹用于存储当前登陆浏览器的账号在使用浏览器时留下的痕迹,包括设置选项等。
use_system_user_path():设置是否使用系统安装的浏览器默认用户文件夹
existing_only():设置是否仅使用已启动的浏览器,如连接目标浏览器失败,会抛出异常,不会启动新浏览器。
address为要控制的浏览器地址,格式为 ip:port,默认为’127.0.0.0:9222’。类型:str
browser_path:该属性返回浏览器可执行文件的路径。类型:str
user_data_path
该属性返回用户数据文件夹路径。
类型:str
tmp_path:该属性返回临时文件夹路径,可用于保存自动分配的用户文件夹路径。类型:str
download_path
该属性返回默认下载路径文件路径。
类型:str
user:该属性返回用户配置文件夹名称。类型:str
load_mode该属性返回页面加载策略。有’normal’、‘eager’、'none’三种 ,类型:str
timeouts:该属性返回超时设置。包括三种:‘base’、‘page_load’、‘script’。类型:dict
print(co.timeouts)
输出:
{
'base': 10,
'page_load': 30,
'script': 30
}
retry_times该属性返回连接失败时的重试次数。类型:int
retry_interval该属性返回连接失败时的重试间隔(秒)。类型:float
proxy该属性返回代理设置。类型:str
arguments该属性以list形式返回浏览器启动参数。类型:list
extensions该属性以list形式返回要加载的插件路径。类型:list
preferences该属性返回用户首选项配置。类型:dict
system_user_path该属性返回是否使用系统按照的浏览器的用户文件夹。类型:bool
is_existing_only该属性返回是否仅使用已打开的浏览器。类型:bool
is_auto_port:该属性返回是否仅使用自动分配端口和用户文件夹路径。类型:bool
ChromiumPage
对象和WebPage
对象的 d 模式都能控制浏览器访问网页。这里只对ChromiumPage
进行说明
参数名称 | 类型 | 默认值 | 说明 |
---|---|---|---|
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()
停止加载。
这样提供给用户非常大的自由度,可等到关键数据包或元素出现就主动停止页面加载,大幅提升执行效率。
加载完成是指主文档完成,并不包括由 js 触发的加载和重定向的加载。 当文档加载完成,程序就判断加载完毕,此后发生的重定向或 js 加载数据需用其它逻辑处理。
配置对象中设置
from DrissionPage import ChromiumOptions, ChromiumPage
co = ChromiumOptions().set_load_mode('none')
page = ChromiumPage(co)
运行中设置
from DrissionPage import ChromiumPage
page = ChromiumPage()
page.set.load_mode.none()
none
模式技巧示例 1,配合监听器
跟监听器配合,可在获取到需要的数据包时,主动停止加载。
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) # 打印数据包正文
示例 2,配合元素查找
跟元素查找配合,可在获取到某个指定元素时,主动停止加载。
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
示例 2,配合页面特征
可等待到页面到达某种状态时,主动停止加载。比如多级跳转的登录,可等待 title 变化到最终目标网址时停止。
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() # 主动停止加载
成功访问网页后,可使用ChromiumPage
自身属性和方法获取页面信息。
操控浏览器除了ChromiumPage
,还有ChromiumTab
和ChromiumFrame
两种页面对象分别对应于标签页对象和元素对象,后面会有单独章节介绍。
html:返回当前页面 html 文本。
json:把请求内容解析成 json。
title:返回当前页面title文本。
user_agent:返回当前页面 user agent 信息。
browser_version:返回当前浏览器版本号。
save():把当前页面保存为文件,同时返回保存的内容。
参数名称 | 类型 | 默认值 | 说明 |
---|---|---|---|
path |
str Path |
None |
保存路径,为None 且name 不为None 时保存到当前路径 |
name |
str |
None |
保存的文件名,为None 且path 不为None 时使用 title 值 |
as_pdf |
bool |
False |
为Ture 保存为 pdf,否则保存为 mhtml 且忽略kwargs 参数 |
**kwargs |
多种 | 无 | pdf 生成参数 |
url:返回当前访问的 url。
address:返回当前对象控制的页面地址和端口。
tab_id:返回当前标签页的 id。
process_id:返回浏览器进程 id。
states.is_loading:返回页面是否正在加载状态。
states.is_alive:返回页面是否仍然可用,标签页已关闭则返回False。
'loading'
:表示文档还在加载中'interactive'
:DOM 已加载,但资源未加载完成'complete'
:所有内容已完成加载rect.size
:以tuple返回页面大小,格式:(宽, 高)。ect.window_size
:以tuple返回窗口大小,格式:(宽, 高)。rect.window_location
:以tuple
返回窗口在屏幕上的坐标,左上角为(0, 0)。rect.window_state
:以返回窗口当前状态,有'normal'
、'fullscreen'
、'maximized'
、 'minimized'
几种。tuple
返回视口大小,不含滚动条,格式:(宽, 高)。rect.viewport_size_with_scrollbar
:以tuple
返回浏览器窗口大小,含滚动条,格式:(宽, 高)。rect.page_location
:以tuple
返回页面左上角在屏幕中坐标,左上角为(0, 0)。rect.viewport_location
:以tuple
返回视口在屏幕中坐标,左上角为(0, 0)。 timeout
timeouts
retry_times
retry_interval
load_mode
cookies()
:返回 cookies 信息。
session_storage()
:用于获取 sessionStorage 信息,可获取全部或单个项。
local_storage()
:用于获取 localStorage 信息,可获取全部或单个项。
driver
:返回当前页面对象使用的Driver对象。
wait.load_start()
:等待页面进入加载状态后。
wait.doc_loaded()
wait.ele_loaded()
wait.ele_displayed()
wait.ele_hidden()
wait.ele_deleted()
wait.download_begin()
wait.upload_paths_inputted()
wait.new_tab()
wait.title_change()
wait.url_change()
wait()
一个 Tab 对象(ChromiumTab
和WebPageTab
)控制一个浏览器的标签页,是页面控制的主要单位。
一个标签页也可以被多个 Tab 对象同时控制(需禁用单例)。
DrissionPage 支持多 tab 对象共存,对象之间互不影响,而且标签页无需激活即可操作。
多标签页用法
标签页总览
tabs_count
tab_ids
参数名称 | 类型 | 默认值 | 说明 |
---|---|---|---|
url |
str None |
None |
新标签页访问的网址,不传入则新建空标签页 |
new_window |
bool |
False |
是否在新窗口打开标签页 |
background |
bool |
False |
是否不激活新标签页,如new_window 为True 则无效 |
new_context |
bool |
False |
是否创建新的上下文,为True 则打开一个无痕模式的新窗口,新窗口与其它窗口不共用 cookies |
tab = page.get_tab() # 获取Page对象控制的标签页的Tab对象(即Page和Tab对象同时控制一个标签页)
tab = page.get_tab(1) # 获取列表中第1个标签页的对象
tab = page.get_tab('5399F4ADFE3A27503FFAA56390344EE5') # 获取列表中指定id标签页的对象
tab = page.get_tab(url='baidu.com') # 获取所有url中带 'baidu.com' 的标签页对象
from DrissionPage import ChromiumPage
page = ChromiumPage()
page.get('https://www.baidu.com')
page.new_tab('https://www.baidu.com')
tabs = page.get_tabs(url='baidu.com')
print(tabs)
# 打开了一个标签页
ele.click()
# 获取最新标签页对象
tab = page.latest_tab # 与page.get_tab(0)效果一致
默认情况下,Tab 对象是单例的,即一个标签页只有一个对象,即使重复使用get_tab()
,获取的都是同一个对象。
这主要是防止新手不理解机制,反复创建多个连接导致资源耗费。
实际上允许多个 Tab 对象同时操作一个标签页,每个负责不同的工作。比如一个执行主逻辑流程,另外的监视页面,处理各种弹窗。
要允许多例,可用Settings
设置:
from DrissionPage.common import Settings
Settings.singleton_tab_obj = False
from DrissionPage import ChromiumPage
from DrissionPage.common import Settings
page = ChromiumPage()
page.new_tab()
page.new_tab()
# 未启用多例:
tab1 = page.get_tab(1)
tab2 = page.get_tab(1)
print(id(tab1), id(tab2))
# 启用多例:
Settings.singleton_tab_obj = False
tab1 = page.get_tab(1)
tab2 = page.get_tab(1)
print(id(tab1), id(tab2))
对象获取对象的方法有两种,可用获取普通元素的方式获取,或者用
get_frame()
方法获取。推荐优先使用get_frame()
方法,因为当作普通元素获取时,IDE 无法正确识别获取到的是元素。
# 使用定位符获取
iframe = page.get_frame('#sss')
# 获取第2个iframe
iframe = page.get_frame(1)
get_frame()
get_frames():获取页面中多个符合条件的或
对象。获取所有
会很慢,而且浪费资源,一般使用获取需要用到的就好。
普通元素方式
内元素在内查找
# 使用定位符获取
iframe = page.get_frame('#sss')
ele = iframe('首页')
print(ele)
#
页面跨查找
如果元素的网址和主页面是同域的,我们可以直接用页面对象查找
内部元素,而无需先获取
ChromiumFrame
对象:
ele = page('首页')
print(ele)
#
许多网页的数据来自接口,在网站使用过程中动态加载,如使用 JS 加载内容的翻页列表。
这些数据通常以 json 形式发送,浏览器接收后,对其进行解析,再加载到 DOM 相应位置。
做数据采集的时候,我们往往从 DOM 中去获取解析后数据的,可能存在数据不全、加载响应不及时、难以判断加载完成等问题。
如果我们可以拿到浏览器收发的数据包,根据数据包状态判断下一步操作,甚至直接获取数据,岂不是爽爆了?
DrissionPage 每个页面对象(包括 Tab 和 Frame 对象)内置了一个监听器,专门用于抓取浏览器数据包。可以提供等待和捕获指定数据包,实时返回指定数据包功能。
等待并获取
from DrissionPage import ChromiumPage
page = ChromiumPage()
page.get('https://gitee.com/explore/all') # 访问网址,这行产生的数据包不监听
page.listen.start('gitee.com/explore') # 开始监听,指定获取包含该文本的数据包
for _ in range(5):
page('@rel=next').click() # 点击下一页
res = page.listen.wait() # 等待并获取一个数据包
print(res.url) # 打印数据包url
实时获取
from DrissionPage import ChromiumPage
page = ChromiumPage()
page.listen.start('gitee.com/explore') # 开始监听,指定获取包含该文本的数据包
page.get('https://gitee.com/explore/all') # 访问网址
i = 0
for packet in page.listen.steps():
print(packet.url) # 打印数据包url
page('@rel=next').click() # 点击下一页
i += 1
if i == 5:
break
以上两个函数的参数一样
参数名称 | 类型 | 默认值 | 说明 |
---|---|---|---|
targets |
str list tuple set |
None |
要匹配的数据包 url 特征,可用列表指定多个,为True 时获取所有 |
is_regex |
bool |
None |
设置的 target 是否正则表达式,为None 时保持原来设置 |
method |
str list tuple set |
None |
设置监听的请求类型,可指定多个,默认('GET', 'POST') ,为True 时监听所有,为None 时保持原来设置 |
res_type |
str list tuple set |
None |
设置监听的 ResourceType 类型,可指定多个,为True 时监听所有,为None 时保持原来设置 |
listen.wait():用于等待符合要求的数据包到达指定数量。所有符合条件的数据包都会存储到队列,wait()实际上是逐个从队列中取结果,不用担心页面已刷走而丢包。
参数名称 | 类型 | 默认值 | 说明 |
---|---|---|---|
count |
int |
1 |
需要捕捉的数据包数量 |
timeout |
float None |
None |
超时时间,为None 无限等待 |
fit_count |
bool |
True |
是否必需满足总数要求,如超时,为True 返回False ,为False 返回已捕捉到的数据包 |
raise_err |
bool |
None |
超时时是否抛出错误,为None 时根据Settings 设置,如不抛出,超时返回False |
返回类型 | 说明 |
---|---|
DataPacket |
count 为1 且未超时,返回一个数据包对象 |
List[DataPacket] |
count 大于1 ,未超时或fit_count 为False ,返回数据包对象组成的列表 |
False |
超时且fit_count 为True 时 |
listen.steps():此方法返回一个可迭代对象,用于for循环,每次循环可从中获取到的数据包。可实现实时获取并返回数据包。如果timeout超时,会中断循环。
参数名称 | 类型 | 默认值 | 说明 |
---|---|---|---|
count |
int |
None |
需捕获的数据包总数,为None 表示无限 |
timeout |
float None |
None |
每个数据包等待时间,为None 表示无限等待 |
gap |
int |
1 |
每接收到多少个数据包返回一次数据 |
返回类型 | 说明 |
---|---|
DataPacket |
gap 为1 时,返回一个数据包对象 |
List[DataPacket] |
gap 大于1 ,返回数据包对象组成的列表 |
listen.wait_silent():用于等待所有指定的请求完成。
参数名称 | 类型 | 默认值 | 说明 |
---|---|---|---|
timeout |
float None |
None |
等待时间,为None 表示无限等待 |
targets_only |
bool |
False |
是否只等待targets 指定的请求结束 |
limit |
int |
0 |
剩下多少个连接时视为结束 |
返回类型 | 说明 |
---|---|
bool |
是否等待成功 |
DataPacket
对象是获取到的数据包结果对象,包含了数据包各种信息。
对象属性
属性名称 | 数据类型 | 说明 |
---|---|---|
tab_id |
str |
产生这个请求的标签页的 id |
frameId |
str |
产生这个请求的框架 id |
target |
str |
产生这个请求的监听目标 |
url |
str |
数据包请求网址 |
method |
str |
请求类型 |
is_failed |
bool |
是否连接失败 |
resourceType |
str |
资源类型 |
request |
Request |
保存请求信息的对象 |
response |
Response |
保存响应信息的对象 |
fail_info |
FailInof |
保存连接失败信息的对象 |
wait_extra_info()
有些数据包有extra_info
数据,但这些数据可能会迟于数据包返回,用这个方法可以等待这些数据加载到数据包对象。
参数名称 | 类型 | 默认值 | 说明 |
---|---|---|---|
timeout |
float None |
None |
超时时间,None 为无限等待 |
返回类型 | 说明 |
---|---|
bool |
是否等待成功 |
wait_extra_info()
Request
对象是DataPacket
对象内用于保存请求信息的对象,有以下属性:
属性名称 | 数据类型 | 说明 |
---|---|---|
url |
str |
请求的网址 |
method |
str |
请求类型 |
headers |
CaseInsensitiveDict |
以大小写不敏感字典返回 headers 数据 |
cookies |
List[dict] |
返回发送的 cookies |
postData |
str dict |
post 类型的请求所提交的数据,json 以dict 格式返回 |
除以上常用属性,还有以下属性,自行体会:
urlFragment、hasPostData、postDataEntries、mixedContentType、initialPriority、referrerPolicy、isLinkPreload、trustTokenParams、isSameSite
DataPacket.respose
Response
对象是DataPacket
对象内用于保存响应信息的对象,有以下属性:
属性名称 | 数据类型 | 说明 |
---|---|---|
url |
str |
请求的网址 |
headers |
CaseInsensitiveDict |
以大小写不敏感字典返回 headers 数据 |
body |
str bytes dict |
如果是 json 格式,自动进行转换,如果时图片格式,进行 base64 转换,其它格式直接返回文本 |
raw_body |
str |
未被处理的 body 文本 |
status |
int |
请求状态 |
statusText |
str |
请求状态文本 |
除以上属性,还有以下属性,自行体会:
headersText、mimeType、requestHeaders、requestHeadersText、connectionReused、connectionId、remoteIPAddress、remotePort、fromDiskCache、fromServiceWorker、fromPrefetchCache、encodedDataLength、timing、serviceWorkerResponseSource、responseTime、cacheStorageCacheName、protocol、alternateProtocolUsage、securityState、securityDetails
FailInfo
对象是DataPacket
对象内用于保存连接失败信息的对象,有以下属性:
属性名称 | 数据类型 | 说明 |
---|---|---|
errorText |
str |
错误信息文本 |
canceled |
bool |
是否取消 |
blockedReason |
str |
拦截原因 |
corsErrorStatus |
str |
cors 错误状态 |
动作链可以在浏览器上完成一系列交互行为,如鼠标移动、鼠标点击、键盘输入等。
ChromiumPage、WebPage、ChromiumTab、ChromiumFrame对象支持使用动作链。可以链式操作,也可以分开执行,每个动作执行即生效,无需perform()。这些操作皆为模拟,真正的鼠标不会移动,因此可以多个标签页同时操作。
有两种方式可以使用动作链,两者区别是,前者会等待页面加载完毕再执行,后者不会。
from DrissionPage import ChromiumPage
from DrissionPage.common import Actions
#1.使用内置actions属性
page = ChromiumPage()
page.get('https://www.baidu.com')
page.actions.move_to('#kw').click().type('DrissionPage')
page.actions.move_to('#su').click()
#2.使用新对象
page = ChromiumPage()
ac = Actions(page)
page.get('https://www.baidu.com')
ac.move_to('#kw').click().type('DrissionPage')
ac.move_to('#su').click()
[!NOTE]- move_to (): 此方法用于移动鼠标到元素中点,或页面上的某个绝对坐标。可设置偏移量,当带偏移量时,偏移量相对于元素左上角坐标。
初始化参数 | 类型 | 默认值 | 说明 |
---|---|---|---|
ele_or_loc |
ChrmoiumElement / str / Tuple[int, int] |
必填 | 元素对象、文本定位符或绝对坐标,坐标为 tuple (int, int) 形式 |
offset_x |
int |
0 |
x 轴偏移量,向右为正,向左为负 |
offset_y |
int |
0 |
y 轴偏移量,向下为正,向上为负 |
duration |
float |
0.5 |
拖动用时,传入 0 即瞬间到达 |
返回类型 | Actions | 0 | 动作链对象本身 |
move()
up()
down()
left()
right()
click()
r_click()
m_click()
db_click()
hold()
release()
r_hold()
r_release()
m_hold()
m_release()
scroll()
key_down()
key_up()
input()
type()
页面对象的get_screenshot()
方法对页面进行截图,可对整个网页、可见网页、指定范围截图。
下面三个参数三选一,优先级:as_bytes
>as_base64
>path
。
参数名称 | 类型 | 默认值 | 说明 |
---|---|---|---|
path |
str Path |
None |
保存图片的路径,为None 时保存在当前文件夹,如包含文件名的完整路径,name 参数无效。 |
name |
str |
None |
完整文件名,后缀可选'jpg' 、'jpeg' 、'png' 、'webp' ,为None 时以用 jpg 格式 |
as_bytes |
str True |
None |
是否以字节形式返回图片,可选'jpg' 、'jpeg' 、'png' 、'webp' 、None 、True 不为 None 时path 参数无效 为 True 时选用 jpg 格式 |
as_base64 |
str True |
None |
是否以 base64 形式返回图片,可选'jpg' 、'jpeg' 、'png' 、'webp' 、None 、True 不为 None 时path 参数无效 为 True 时选用 jpg 格式 |
full_page |
bool |
False |
是否整页截图,为True 截取整个网页,为False 截取可视窗口 |
left_top |
Tuple[int, int] |
None |
截取范围左上角坐标 |
right_bottom |
Tuple[int, int] |
None |
截取范围右下角坐标 |
返回类型 | 说明 |
---|---|
bytes |
as_bytes 生效时返回图片字节 |
str |
as_bytes 和as_base64 为None 时返回图片完整路径 |
str |
as_base64 生效时返回 base64 格式的字符串 |
screencast
:可以录取屏幕图片或视频。 设置录制模式
设置存放路径
screencast.start()
screencast.stop()
注意事项
示例
from DrissionPage import WebPage, ChromiumOptions
class Obj(object):
def __init__(self, page):
self.page = page
def main(self):
pass
if __name__ == '__main__':
co = ChromiumOptions().use_system_user_path()
page = WebPage(mode='d', chromium_options=co)
ob = Obj(page)
ob.main()
定位符语法简化
定位语法都有其简化形式
页面和元素对象都实现了__call__()
方法,所以page.ele(‘…’)可简化为page(‘…’)
查找方法都支持链式操作
原写法 | 简化写法 | 说明 |
---|---|---|
@id |
# |
表示 id 属性,简化写法只在语句最前面且单独使用时生效 |
@class |
. |
表示 class 属性,简化写法只在语句最前面且单独使用时生效 |
text |
tx |
按文本匹配 |
@text() |
@tx() |
按文本查找与 @ 或 @@ 配合使用时 |
tag |
t |
按标签类型匹配 |
xpath |
x |
用 xpath 方式查找元素 |
css |
c |
用 css selector 方式查找元素 |
# 查找tag为div的元素
ele = page.ele('tag:div') # 原写法
ele = page('t:div') # 简化写法
# 用xpath查找元素
ele = page.ele('xpath://xxxxx') # 原写法
ele = page('x://xxxxx') # 简化写法
# 查找text为'something'的元素
ele = page.ele('text=something') # 原写法
ele = page('tx=something') # 简化写法
# 根据 class 或 id 查找
page.ele('#ele_id') # 等价于 page.ele('@id=ele_id')
page.ele('#:ele_id') # 等价于 page.ele('@id:ele_id')
page.ele('.ele_class') # 等价于 page.ele('@class=ele_class')
page.ele('.:ele_class') # 等价于 page.ele('@class:ele_class')
# 根据 tag name 查找
page.ele('tag:li') # 查找第一个 li 元素
page.eles('tag:li') # 查找所有 li 元素
# 根据 tag name 及属性查找
page.ele('tag:div@class=div_class') # 查找 class 为 div_class 的 div 元素
相对定位参数简化
相对定位时,有时需要获取当前元素后某个元素,而不关心该元素是什么类型,
ele2 = ele1.parent(2)
ele2 = ele1.next(2)('tx=xxxxx')
ele2 = ele1.before(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')
# 定位div元素
ele2 = ele1.ele('tag:div')
# 定位class属性为p_cls的p元素
ele2 = ele1.ele('tag:p@class=p_cls')
# 定位文本为"第二行"的p元素
ele2 = ele1.ele('tag:p@text()=第二行')
# 定位class属性为p_cls且文本为“第二行”的p元素
ele2 = ele1.ele('tag:p@@class=p_cls@@text()=第二行')
# 定位class属性为p_cls或文本为“第二行”的p元素
ele2 = ele1.ele('tag:p@|class=p_cls@|text()=第二行')
# 查找直接文本节点包含“二行”字符串的p元素
ele2 = ele1.ele('tag:p@text():二行')
# 查找内部文本节点包含“二行”字符串的p元素
ele2 = ele1.ele('tag:p@@text():二行')
# 查找class属性为p_cls的元素
ele2 = ele1.ele('.p_cls')
# 查找class属性'_cls'文本开头的元素
ele2 = ele1.ele('.^_cls')
# 精确查找class属性为`p_cls1 p_cls2 `的元素
ele2 = ele1.ele('.p_cls1 p_cls2 ')
#如果某元素有多个类名,必须写 class 属性的完整值(类名的顺序也不能变)
# 模糊查找class属性含有类名 'p_cls2' 的元素
ele2 = ele1.ele('.:p_cls2')
# 查找name属性为row1的元素
ele2 = ele1.ele('@name=row1')
# 查找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"]')
# 查找name属性为row1且class属性包含cls文本的元素
ele2 = ele1.ele('@@name=row1@@class:cls')
ele = page.ele('tag:div@@class=p_cls@@name=row1')
# 查找id属性为one或id属性为two的元素
ele2 = ele1.ele('@|id=one@|id=two')
ele = page.ele('tag:div@|class=p_cls@|name=row1')
# 匹配arg1等于abc且arg2不等于def的元素
page.ele('@@arg1=abc@!arg2=def')
# 匹配arg1等于abc或arg2不等于def的div元素
page.ele('t:div@|arg1=abc@!arg2=def')
# 匹配arg1不等于abc
page.ele('@!arg1=abc')
# 匹配没有arg1属性的元素
page.ele('@!arg1')
# 查找文本为“第二行”的元素
ele2 = ele1.ele('text=第二行')
# 查找文本包含“第二”的元素
ele2 = ele1.ele('text:第二')
# 与上一行一致
ele2 = ele1.ele('第二')
# 匹配包含 文本的元素
ele2 = ele1.ele('第\u00A0二') # 需将 转为\u00A0
text
在作为属性查找条件是改为text()
,是为了避免遇到名为text
的属性时产生冲突。# 查找文本为“第二行”的元素
ele2 = ele1.ele('@text()=第二行')
# 查找文本包含“第二行”的元素
ele2 = ele1.ele('@text():二行')
# 查找文本以“第二”开头且class属性为p_cls的元素
ele2 = ele1.ele('@@text()^第二@@class=p_cls')
# 查找文本为“二行”且没有任何属性的元素(因第一个 @@ 后为空)
ele2 = ele1.ele('@@@@text():二行')
# 查找直接子文本包含“二行”字符串的元素
ele = page.ele('@text():二行')
css:
与css=
效果一致,没有css^
和css$
语法。# 查找 div 元素
ele2 = ele1.ele('css:.div')
# 查找 div 子元素元素,这个写法是本库特有,原生不支持
ele2 = ele1.ele('css:>div')
# 查找后代中第一个 div 元素
ele2 = ele1.ele('xpath:.//div')
# 和上面一行一样,查找元素的后代时,// 前面的 . 可以省略
ele2 = ele1.ele('xpath://div')
# 使用xpath获取div元素的class属性(页面元素无此功能)
ele_class_str = ele1.ele('xpath://div/@class')
from DrissionPage.common import By
# 查找id为one的元素
loc1 = (By.ID, 'one')
ele = page.ele(loc1)
# 按 xpath 查找
loc2 = (By.XPATH, '//p[@class="p_cls"]')
ele = page.ele(loc2)
[!NOTE] parent(level_or_loc=1,index=1)获取父级元素
- level_or_loc[int|str|Tuple[str, str]]:第几级父元素,从
1
开始,或用定位符在祖先元素中进行筛选- index [int] :当
level_or_loc
传入定位符,使用此参数选择第几个结果,从当前元素往上级数;当level_or_loc
传入数字时,此参数无效
# 获取 ele1 的第二层父元素
ele2 = ele1.parent(2)
# 获取 ele1 父元素中 id 为 id1 的元素
ele2 = ele1.parent('#id1')
child()
此方法返回当前元素的一个直接子节点,可指定筛选条件和第几个。参数名称 | 类型 | 默认值 | 说明 |
---|---|---|---|
locator |
str Tuple[str, str] int |
'' |
用于筛选节点的查询语法,为int 类型时index 参数无效 |
index |
int |
1 |
查询结果中的第几个,从1 开始,可输入负数表示倒数 |
timeout |
float |
None |
无实际作用 |
ele_only |
bool |
True |
是否只查找元素,为False 时把文本、注释节点也纳入查找范围 |
参数名称 | 类型 | 默认值 | 说明 |
---|---|---|---|
locator |
str Tuple[str, str] |
'' |
用于筛选节点的查询语法 |
timeout |
float |
None |
无实际作用 |
ele_only |
bool |
True |
是否只查找元素,为False 时把文本、注释节点也纳入查找范围 |
参数名称 | 类型 | 默认值 | 说明 |
---|---|---|---|
locator |
str Tuple[str, str] int |
'' |
用于筛选节点的查询语法,为int 类型时index 参数无效 |
index |
int |
1 |
查询结果中的第几个,从1 开始,可输入负数表示倒数 |
timeout |
float |
None |
无实际作用 |
ele_only |
bool |
True |
是否只查找元素,为False 时把文本、注释节点也纳入查找范围 |
# 获取 ele1 前面第一个兄弟元素
ele2 = ele1.prev()
# 获取 ele1 前面第 3 个兄弟元素
ele2 = ele1.prev(3)
# 获取 ele1 前面第 3 个 div 兄弟元素
ele2 = ele1.prev(3, 'tag:div')
# 获取 ele1 前面第一个文本节点的文本
txt = ele1.prev(1, 'xpath:text()')
next()
获取后面的同级节点,也叫兄弟节点:after()
:在后面文档中查找节点afters()
:返回当前元素后面符合条件的全部节点组成的列表,可用查询语法筛选。查找范围不限同级节点,而是整个 DOM 文档page('账号').after('t:input').input('123')
page('密码').after('t:input').input('456')
属性或方法 | 说明 |
---|---|
html |
此属性返回元素的 outerHTML 文本 |
inner_html |
此属性返回元素的 innerHTML 文本 |
tag |
此属性返回元素的标签名 |
text |
此属性返回元素内所有文本组合成的字符串 |
raw_text |
此属性返回元素内原始文本 |
texts() |
此方法返回元素内所有直接子节点的文本,包括元素和文本节点 |
comments |
此属性以列表形式返回元素内的注释 |
attrs |
此属性以字典形式返回元素所有属性及值 |
attr() |
此方法返回元素某个 attribute 属性值 |
link |
此方法返回元素的 href 属性或 src 属性 |
page |
此属性返回元素所在的总控页面对象 |
xpath |
此属性返回当前元素在页面中 xpath 的绝对路径 |
css_path |
此属性返回当前元素在页面中 css selector 的绝对路径 |
状态信息藏在states属性中。元素对象.states
states.is_in_viewport
:返回元素是否在视口中,以元素可以接受点击的点为判断。states.is_whole_in_viewport
:返回元素是否整个在视口中。states.is_alive
:返回当前元素是否仍可用。用于判断 d 模式下是否因页面刷新而导致元素失效。states.is_checked
:返回表单单选或多选元素是否选中。states.is_selected
:返回
元素中的项是否选中。states.is_enabled
:以布尔值返回元素是否可用。states.is_displayed
:返回元素是否可见。states.is_covered
:返回元素是否被其它元素覆盖。如被覆盖,返回覆盖元素的 id,否则返回False
states.has_rect
:返回元素是否拥有大小和位置信息,有则返回四个角在页面上的坐标组成的列表,没有则返回False。 rect.size
rect.location
rect.midpoint
rect.click_point
rect.viewport_location
rect.viewport_midpoint
rect.viewport_click_point
rect.screen_location
rect.screen_midpoint
rect.screen_click_point
rect.corners
rect.viewport_corners
rect.viewport_rect
[!NOTE] src():返回元素
src
属性所使用的资源。
- timeout=None:等待资源加载超时时间,为
None
时使用元素所在页面timeout
属性- base64_to_bytes= True:为
True
时,如果是 base64 数据,转换为bytes
格式- 返回类型:base64 的可转为
bytes
返回,其它的以str
返回。无资源的返回None
。
[!NOTE] save():保存
src()
方法获取到的资源到文件。
- path[str|Path]=None 文件保存路径,为None时保存到当前文件夹
- name=[str]=None 文件名称,需包含后缀,为None时从资源 url 获取
- timeout float None 等待资源加载超时时间,为None时使用元素所在页面timeout属性
- rename bool True 遇到重名文件时是否自动重命名
import ddddocr
code = page('#randCode_icon').src()
ocr = ddddocr.DdddOcr(show_ad=False)
res = ocr.classification(code)
tag
html
inner_html
page
parent_ele
states.is_enabled
states.is_alive
用于获取验证码人机验证码
元素对象.get_screenshot()
:对元素进行截图。as_bytes
>as_base64
>path
。参数名称 | 类型 | 默认值 | 说明 |
---|---|---|---|
path |
str Path |
None |
保存图片的路径,为None 时保存在当前文件夹,包含文件名的完整路径,name 参数无效。 |
name |
str |
None |
完整文件名,后缀可选'jpg' 、'jpeg' 、'png' 、'webp' ,为None 时以用 jpg 格式 |
as_bytes |
str True |
None |
是否以字节形式返回图片,可选'jpg' 、'jpeg' 、'png' 、'webp' 、None 、True 不为 None 时path 和as_base64 参数无效 为 True 时选用 jpg 格式 |
as_base64 |
str True |
None |
是否以 base64 形式返回图片,可选'jpg' 、'jpeg' 、'png' 、'webp' 、None 、True 不为 None 时path 参数无效 为 True 时选用 jpg 格式 |
scroll_to_center |
bool |
True |
截图前是否滚动到视口中央 |
返回类型 | 说明 |
---|---|
bytes |
as_bytes 生效时返回图片字节 |
str |
as_bytes 和as_base64 为None 时返回图片完整路径 |
str |
as_base64 生效时返回 base64 格式的字符串 |
保存元素
参数名称 | 类型 | 默认值 | 说明 |
---|---|---|---|
vals |
Any |
False |
文本值或按键组合 对文件上传控件时输入路径字符串或其组成的列表 |
clear |
bool |
False |
输入前是否清空文本框 |
by_js |
bool |
False |
是否用 js 方式输入,为True 时不能输入组合键 |
并发编程-py
当你有多个链接需要打开时,使用多线程并发多开标签页
from concurrent.futures import ThreadPoolExecutor
with ThreadPoolExecutor(max_workers=3) as executor:
# 通过executor的 map 获取已经完成的task的值
for data in executor.map(get_html, urls):
print("get {} page".format(data))
#坑
ImportError: DLL load failed while importing _sqlite3: 找不到指定的模块。
python 内置了这个sqlite3.dll模块,而anaconda没有
把sqlite3.dll放到python环境中的DLLs目录下
C:\Users\wenke\.conda\envs\drission\DLLs
在pyinstaller的xxx.spec文件中添加DLLs目录
binaries=[('C:\\Users\\wenke\\.conda\\envs\\drission\\DLLs','.')],
pyinstaller main.spec