SessionPageSetter增加多项配置

This commit is contained in:
g1879 2023-02-14 11:39:15 +08:00
parent d951dd84c9
commit f0011e2df3
91 changed files with 152 additions and 13578 deletions

View File

@ -206,7 +206,7 @@ class ChromiumDriver(object):
if self._stopped.is_set():
return False
if not self._started:
raise RuntimeError("Driver未在运行。")
return True
self.status = self._STOPPED_
self._stopped.set()

View File

@ -1,5 +1,5 @@
[paths]
chromedriver_path =
chromedriver_path = D:\python\projects\DrissionPage\DrissionPage\chromedriver.exe
download_path =
[chrome_options]

View File

@ -102,10 +102,10 @@ class SessionOptions(object):
@property
def max_redirects(self) -> int: ...
def _sets(self, arg: str, val: Any) -> None: ...
def set_max_redirects(self, times: Union[int, None]) -> SessionOptions: ...
def _sets(self, arg: str, val: Any) -> None: ...
def save(self, path: str = None) -> str: ...
def save_to_default(self) -> str: ...

View File

@ -355,6 +355,13 @@ class SessionPageSetter(object):
def __init__(self, page):
self._page = page
def timeout(self, second):
"""设置连接超时时间
:param second: 秒数
:return: None
"""
self._page.timeout = second
def cookies(self, cookies):
"""为Session对象设置cookies
:param cookies: cookies信息
@ -363,18 +370,100 @@ class SessionPageSetter(object):
set_session_cookies(self._page.session, cookies)
def headers(self, headers):
"""设置通用的headers设置的headers值回逐个覆盖原有的不会清理原来的
"""设置通用的headers
:param headers: dict形式的headers
:return: None
"""
headers = CaseInsensitiveDict(headers)
for i in headers:
self._page.session.headers[i] = headers[i]
self._page.session.headers = CaseInsensitiveDict(headers)
def header(self, attr, value):
"""设置headers中一个项
:param attr: 设置名称
:param value: 设置值
:return: None
"""
self._page.session.headers[attr.lower()] = value
def user_agent(self, ua):
"""设置user agent"""
"""设置user agent
:param ua: user agent
:return: None
"""
self._page.session.headers['user-agent'] = ua
def proxies(self, http, https=None):
"""设置proxies参数
:param http: http代理地址
:param https: https代理地址
:return: None
"""
proxies = None if http == https is None else {'http': http, 'https': https or http}
self._page.session.proxies = proxies
def auth(self, auth):
"""设置认证元组或对象
:param auth: 认证元组或对象
:return: None
"""
self._page.session.auth = auth
def hooks(self, hooks):
"""设置回调方法
:param hooks: 回调方法
:return: None
"""
self._page.session.hooks = hooks
def params(self, params):
"""设置查询参数字典
:param params: 查询参数字典
:return: None
"""
self._page.session.params = params
def verify(self, on_off):
"""设置是否验证SSL证书
:param on_off: 是否验证 SSL 证书
:return: None
"""
self._page.session.verify = on_off
def cert(self, cert):
"""SSL客户端证书文件的路径(.pem格式),或(cert, key)元组
:param cert: 证书路径或元组
:return: None
"""
self._page.session.cert = cert
def stream(self, on_off):
"""设置是否使用流式响应内容
:param on_off: 是否使用流式响应内容
:return: None
"""
self._page.session.stream = on_off
def trust_env(self, on_off):
"""设置是否信任环境
:param on_off: 是否信任环境
:return: None
"""
self._page.session.trust_env = on_off
def max_redirects(self, times):
"""设置最大重定向次数
:param times: 最大重定向次数
:return: None
"""
self._page.session.max_redirects = times
def add_adapter(self, url, adapter):
"""添加适配器
:param url: 适配器对应url
:param adapter: 适配器对象
:return: None
"""
self._page.session.mount(url, adapter)
class DownloadSetter(object):
"""用于设置下载参数的类"""

View File

@ -8,6 +8,8 @@ from typing import Any, Union, Tuple, List
from DownloadKit import DownloadKit
from requests import Session, Response
from requests.adapters import HTTPAdapter
from requests.auth import HTTPBasicAuth
from requests.cookies import RequestsCookieJar
from requests.structures import CaseInsensitiveDict
@ -169,12 +171,36 @@ class SessionPageSetter(object):
def __init__(self, page: SessionPage):
self._page: SessionPage = ...
def timeout(self, second: Union[int, float]) -> None: ...
def cookies(self, cookies: Union[RequestsCookieJar, list, tuple, str, dict]) -> None: ...
def headers(self, headers: dict) -> None: ...
def header(self, attr: str, value: str) -> None: ...
def user_agent(self, ua: str) -> None: ...
def proxies(self, http, https=None) -> None: ...
def auth(self, auth: Union[Tuple[str, str], HTTPBasicAuth, None]) -> None: ...
def hooks(self, hooks: Union[dict, None]) -> None: ...
def params(self, params: Union[dict, None]) -> None: ...
def verify(self, on_off: Union[bool, None]) -> None: ...
def cert(self, cert: Union[str, Tuple[str, str], None]) -> None: ...
def stream(self, on_off: Union[bool, None]) -> None: ...
def trust_env(self, on_off: Union[bool, None]) -> None: ...
def max_redirects(self, times: Union[int, None]) -> None: ...
def add_adapter(self, url: str, adapter: HTTPAdapter) -> None: ...
class DownloadSetter(object):
def __init__(self, page: Union[SessionPage, WebPage, ChromiumPage]):

View File

@ -2,7 +2,7 @@
DrissionPage 是一个基于 python 的网页自动化工具。
它既能控制浏览器,也能收发数据包,甚至能把两者合而为一
它既能控制浏览器,也能收发数据包,甚至能把两者合而为一
可兼顾浏览器自动化的便利性和 requests 的高效率。
@ -10,7 +10,15 @@ DrissionPage 是一个基于 python 的网页自动化工具。
它的语法简洁而优雅,代码量少,对新手友好。
***
---
<a href='https://gitee.com/g1879/DrissionPage/stargazers'><img src='https://gitee.com/g1879/DrissionPage/badge/star.svg?theme=dark' alt='star'></img></a> <a href='https://gitee.com/g1879/DrissionPage/members'><img src='https://gitee.com/g1879/DrissionPage/badge/fork.svg?theme=dark' alt='fork'></img></a>
项目地址:[gitee](https://gitee.com/g1879/DrissionPage) | [github](https://github.com/g1879/DrissionPage)
您的星星是对我最大的支持💖
---
支持系统Windows、Linux、Mac
@ -18,17 +26,13 @@ python 版本3.6 及以上
支持浏览器Chromium 内核浏览器(如 Chrome 和 Edge
***
---
<a href='https://gitee.com/g1879/DrissionPage/stargazers'><img src='https://gitee.com/g1879/DrissionPage/badge/star.svg?theme=dark' alt='star'></img></a> <a href='https://gitee.com/g1879/DrissionPage/members'><img src='https://gitee.com/g1879/DrissionPage/badge/fork.svg?theme=dark' alt='fork'></img></a>
项目地址:[gitee](https://gitee.com/g1879/DrissionPage) | [github](https://github.com/g1879/DrissionPage)
**📖 使用文档:** [点击查看](http://g1879.gitee.io/drissionpagedocs)
**交流QQ群** 897838127
**联系邮箱:** g1879@qq.com
**📖 使用文档:** [点击查看](http://g1879.gitee.io/drissionpagedocs)
---
# 📕 背景
@ -40,10 +44,14 @@ python 版本3.6 及以上
以前的版本是对 selenium 进行重新封装实现的。从 3.0 开始,作者另起炉灶,对底层进行了重新开发,摆脱对 selenium 的依赖,增强了功能,提升了运行效率。
---
# 💡 理念
简洁!易用 !方便!
---
# ☀️ 特性和亮点
作者经过长期实践,踩过无数坑,总结出的经验全写到这个库里了。
@ -94,6 +102,8 @@ python 版本3.6 及以上
- 还有很多细节,这里不一一列举,欢迎实际使用中体验:)
---
# 🌟 简单演示
## ⭐ **与 selenium 代码对比**
@ -277,19 +287,27 @@ Git 命令学习 https://oschina.gitee.io/learn-git-branching/
Git 命令学习
```
---
# 🛠 使用方法
[点击跳转到使用文档](http://g1879.gitee.io/drissionpage)
---
# 🔖 版本历史
[点击查看版本历史](http://g1879.gitee.io/drissionpage/#/%E7%89%88%E6%9C%AC%E5%8E%86%E5%8F%B2)
[点击查看版本历史](http://g1879.gitee.io/drissionpagedocs/10_history/)
---
# 🖐🏻 免责声明
请勿将 DrissionPage 应用到任何可能会违反法律规定和道德约束的工作中,请友善使用 DrissionPage遵守蜘蛛协议不要将 DrissionPage 用于任何非法用途。如您选择使用 DrissionPage
即代表您遵守此协议,作者不承担任何由于您违反此协议带来任何的法律风险和损失,一切后果由您承担。
---
# ☕ 请我喝咖啡
如果本项目对您有所帮助,不妨请作者我喝杯咖啡

View File

View File

@ -1,434 +0,0 @@
# v3.1.3
- `ChromiumPage`添加`latest_tab`属性
- `WebPage`初始化删除`tab_id`参数
- 修复页面未加载完可能获取到空元素的问题
- 修复新标签页重定向时获取文档不正确问题
- 增强稳定性
# v3.1.1
- 增强下载功能
- `ChromiumPage`也可以使用内置下载器下载文件
- 可拦截并接管浏览器下载任务
- 新增`download_set`属性对下载参数进行设置
- 增加`wait_download_begin()`方法
- 改进浏览器启动设置
- 优化 ini 文件结构
- 新增`ChromiumOptions`取代`DriverOptions`,完全摆脱对 selenium 的依赖
- 新增自动分配端口功能
- 优化`SessionOptions`设计,增加一系列设置参数的方法
- 改进对用户配置文件的设置
- 对部分代码进行重构
- 优化页面对象启动逻辑
- 优化配置类逻辑
- 优化项目结构
- 细节
- 上传文件时支持传入相对路径
- bug 修复
- 修复`get_tab()`出错问题
- 修复新浏览器第一次新建标签页时不正确切换的问题
- 修复关闭当前标签页出错问题
- 修复改变浏览器窗口大小出错问题
# v3.0.34
- `WebPage`删除`check_page()`方法
- `DriverOptions``easy_set``set_paths()`增加`browser_path`参数
- `DriverOptions`增加`browser_path`属性
- `ChromiumFrame`现在支持页面滚动
- 改进滚动到元素功能
- 修改`SessionElement`相对定位参数顺序
- `SessionPage`也可以从 ini 文件读取 timeout 设置
- ini 文件中`session_options`增加`timeout`
- `SessionOptions`增加`timeout`属性、`set_timeout()`方法
- 优化和修复一些问题
# v3.0.31
- `run_script()``run_async_script()`更名为`run_js``run_async_js()`
- 返回的坐标数据全部转为`int`类型组成的`tuple`
- 修改注释
# v3.0.30
- 元素增加`m_click()`方法
- 动作链增加`type()``m_click()``r_hold()``r_release()``m_hold()``m_release()`方法
- 动作链的`on_ele`参数可接收文本定位符
- `WebPage``SessionPage``ChromiumPage`增加`set_headers()`方法
# v3.0.28
- 各种大小、位置信息从`dict`改为用`tuple`返回
- 改进`ChromiumFrame`
- 修复小窗时定位不准问题,修复 iframe 内元素无法获取 s_ele() 问题
- 增加`wait_loading`方法和参数
- 其它优化和问题修复
# v3.0.22
- `change_mode()`增加`copy_cookies`参数
- 调整`WebPage`生成的元素对象的`prev()``next()``before()``after()`参数顺序
- 修复读取页面时小概率失效问题
- 用存根文件取代类型注解
# v3.0.20
重大更新。推出`WebPage`,重新开发底层逻辑,摆脱对 selenium 的依赖,增强了功能,提升了运行效率。支持 chromium 内核的浏览器(如 chrome 和 edge。比`MixPage`有以下优点:
- 无 webdriver 特征,不会被网站识别
- 无需为不同版本的浏览器下载不同的驱动
- 运行速度更快
- 可以跨 iframe 查找元素,无需切入切出
- 把 iframe 看作普通元素,获取后可直接在其中查找元素,逻辑更清晰
- 可以同时操作浏览器中的多个标签页,即使标签页为非激活状态
- 可以直接读取浏览器缓存来保持图片,无需用 GUI 点击保存
- 可以对整个网页截图包括视口外的部分90以上版本浏览器支持
其它更新:
- 增加`ChromiumTab``ChromiumFrame`类用于处理 tab 和 frame 元素
- 新增与`WebPage`配合的动作链接`ActionChains`
- ini 文件和`DriverOption`删除`set_window_rect`属性
- 浏览器启动配置实现对插件的支持
- 浏览器启动配置实现对`experimental_options``prefs`属性支持
# v2.7.3
- 页面对象和元素对象的`screenshot_as_bytes()`方法合并到`screenshot()`
- `input()`方法接收非文本参数时自动转成文本输入
# v2.7.2
- d 模式页面和元素对象增加`screenshot_as_bytes()`方法
# v2.7.1
- DriverPage
- 增加`get_session_storage()``get_local_storage()``set_session_storage()``set_local_storage()``clean_cache()`方法
- `run_cdp()``cmd_args`参数改为`**cmd_args`
- 关闭 driver 时会主动关闭 chromedriver.exe 的进程
- 优化关闭浏览器进程逻辑
# v2.6.2
- d 模式增加`stop_loading()`方法
- 优化完善监听器功能
# v2.6.0
- 新增`Listener`
- 可监听浏览器数据包
- 可异步监听
- 可实现每监听到若干数据包执行操作
- 放弃对selenium4.1以下的支持
- 解决使用新版浏览器时出现的一些问题
# v2.5.9
- 优化 s 模式创建连接的逻辑
# v2.5.7
- 列表元素`select()``deselect()`等方法添加`timeout`参数,可等待列表元素加载
- 优化了对消息提示框的处理
- `drag()``drag_to()`不再检测是否拖拽成功,改成返回`None`
- `DriverOptions`对象从父类继承的方法也支持链式操作
- 其它优化和问题修复
# v2.5.5
- `DriverPage`添加`run_cdp()`方法
- `get()``post()`方法删除`go_anyway`参数
- 连接重试默认不打印提示
# v2.5.0
- 用 DownloadKit 库替代原来的`download()`方法,支持多线程并发
- `DriverPage`增加`set_ua_to_tab()`方法
- 删除`scroll_to()`方法
- 其它优化和问题修复
# v2.4.3
- `wait_ele()``to_frame()``scroll_to()`改用类的方式,避免使用字符串方式选择功能
- `scroll_to()`方法改为`scroll`属性
- 滚动页面或元素增加`to_location()`方式
- 优化`Select`类用法
# v2.3.3
- `DriverPage`添加`forward()`方法
- `DriverPage``close_current_tab()`改为`close_tabs()`,可一次过关闭多个标签页
- `DriverPage`添加`run_async_script()`
- `DriverPage`添加`timeouts`属性
- `DriverPage`添加`set_timeouts()`方法
- `DriverElement`添加`scroll_to()`方法,可在元素内滑动滚动条
- `DriverOptions`添加`set_page_load_strategy()`方法
- ini 文件增加`page_load_strategy``set_window_rect``timeouts`三个属性
- 其它优化和问题修复
# v2.2.1
- 新增基于页面布局的相对定位方法`left()``right()``below()``above()``near()``lefts()``rights()``belows()``aboves()``nears()`
- 修改基于 DOM 的相对定位方法:删除`parents()`方法,`parent`属性改为 `parent()`方法,`next`属性 改为`next()`方法,`prev`属性改为`prev()`方法,`nexts()``prevs()`
方法改为返回多个对象
- 增加`after()``before()``afters()``before()`等基于 DOM 的相对定位方法
- 定位语法增加`@@``@@-`语法,用于同时匹配多个条件和排除条件
- 改进`ShadowRootElement`功能,现在在 shadow-root 下查找元素可用完全版的定位语法。
- `DriverElement``after``before`属性改为`pseudo_after``pseudo_before`
- `DriverElement``input()`增加`timeout`参数
- `DriverElement``clear()`增加`insure_clear`参数
- 优化`DriverElement``submit()`方法
- `DriverPage`增加`active_ele`属性,获取焦点所在元素
- `DriverPage``get_style_property()`改名为`style()`
- `DriverPage``hover()`增加偏移量参数
- `DriverPage``current_tab_num`改名为`current_tab_index`
- `DriverPage``to_frame()`方法返回页面对象自己,便于链式操作
- 优化自动下载 driver 逻辑
- `set_paths()`增加`local_port`参数
- 默认使用`9222`端口启动浏览器
- 其它优化和问题修复
# v2.0.0
- 支持从`DriverElement`或 html 文本生成`SessionElement`,可把 d 模式的页面信息爬取速度提高几个数量级(使用新增的`s_ele()``s_eles()`方法)
- 支持随时隐藏和显示浏览器进程窗口(只支持 Windows 系统)
- s 模式和 d 模式使用相同的提取文本逻辑d 模式提取文本效率大增
- `input()`能自动检测以确保输入成功
- `click()`支持失败后不断重试,可用于确保点击成功及等待页面遮罩层消失
- 对 linux 和 mac 系统路径问题做了修复
- `download()`能更准确地获取文件名
- 其它稳定性和效率上的优化
# v1.11.7
- `SessionOptions`增加`set_headers()`方法
- 调整`MixPage`初始化参数
- `click()`增加`timeout`参数,修改逻辑为在超时时间内不断重试点击。可用于监视遮罩层是否消失
- 处理`process_alert()`增加`timeout`参数
- 其他优化和问题修复
# v1.11.0
- `set_property()`方法改名为`set_prop`
- 增加`prop()`
- `clear()`改用 selenium 原生
- 增加`r_click()``r_click_at()`
- `input()`返回`None`
- 增加`input_txt()`
# v1.10.0
- 优化启动浏览器的逻辑
- 用 debug 模式启动时可读取启动参数
- 完善`select`标签处理功能
- `MixPage`类的`to_iframe()`改名为`to_frame()`
- `MixPage`类的`scroll_to()`增加`'half'`方式,滚动半页
- Drission 类增加`kill_browser()`方法
# v1.9.0
- 元素增加`click_at()`方法,支持点击偏移量
- `download()`支持重试
- 元素`input()`允许接收组合键,如`ctrl+a`
- 其它优化
# v1.8.0
- 添加`retry_times``retry_interval`属性,可统一指定重连次数
- 元素对象增加`raw_text`属性
- 元素查找字符串支持极简模式,用`x`表示`xpath``c`表示`css``t`表示`tag``tx`表示`text`
- s 模式元素`text`尽量与 d 模式保持一致
- 其它完善和问题修复
# v1.7.7
- 创建`WebDriver`时可自动下载 chromedriver.exe
- 修复获取不到`content-type`时会出现的问题
# v1.7.1
- d 模式如指定了调试端口,可自动启动浏览器进程并接入
- 去除对 cssselect 库依赖
- 提高查找元素效率
- 调整获取元素 xpath 和 css_path 逻辑
# v1.7.0
- 优化`cookies`相关逻辑
- `MixPage`增加`get_cookies()``set_cookies()`方法
- 增加`SessionOptions`
- 浏览文件`DriverElement`增加`remove_attr()`方法
- 修复`MixPage`初始化时`Session`导入`cookies`时的问题
- `MixPage``close_other_tabs()`方法现在可以接收列表或元组以保留多个 tab
- 其它优化
# v1.6.1
- 增加`.``#`方式用于查找元素,相当于`@Class``@id`
- easy_set 增加识别 chrome 版本并自动下载匹配的 chromedriver.exe 功能
- 改进配置功能
- 修复 shadow-root 方面的问题
# v1.5.4
- 优化获取编码的逻辑
- 修复下载不能显示进度的问题
# v1.5.2
- 修复获取 html 时会把元素后面的文本节点带上的问题
- 修复获取编码可能出现的错误
- 优化`download()`和获取编码代码
# v1.5.1
- 修复获取编码可能出现的 bug
# v1.5.0
- s 模式使用 lxml 库代替 requests_html 库
- 可直接调用页面对象和元素对象获取下级元素,`element('@id=ele_id')`等价于`element.ele('@id=ele_id')`
- `nexts()``prevs()`方法可获取文本节点
- 可获取伪元素属性及文本
- 元素对象增加`link``inner_html`属性
- 各种优化
# v1.4.0
- d 模式使用 js 通过`evaluate()`方法处理 xpath放弃使用 selenium 原生的方法,以支持用 xpath 直接获取文本节点、元素属性
- d 模式增加支持用 xpath 获取元素文本、属性
- 优化和修复小问题
# v1.3.0
- 可与 selenium 代码无缝对接
- 下载功能支持 post 方式
- 元素添加`texts`属性,返回元素内每个文本节点内容
- s 模式增加支持用 xpath 获取元素文本、属性
# v1.2.1
- 优化修复网页编码逻辑
- `download()`函数优化获取文件名逻辑
- 优化`download()`获取文件大小逻辑
- 优化`MixPage`对象关闭 session 逻辑
# v1.2.0
- 增加对 shadow-root 的支持
- 增加自动重试连接功能
- `MixPage`可直接接收配置
- 修复一些 bug
# v1.1.3
- 连接有关函数增加是否抛出异常参数
- s 模式判断编码优化
- d 模式`check_page()`优化
- 修复`run_script()`遗漏`args`参数的问题
# v1.1.1
- 删除`get_tabs_sum()``get_tab_num()`函数,以属性`tabs_count``current_tab_num`代替
- 增加`current_tab_handle``tab_handles`属性
- `to_tab()``close_other_tabs()`函数可接收`handle`
- `create_tab()`可接收一个 url 在新标签页打开
- 其它优化和 bug 修复
# v1.1.0
- 元素对象增加 xpath 和 css path 路径属性
- 修复 driver 模式下元素对象用 css 方式不能获取直接子元素的问题selenium 的锅)
- s 模式下现在能通过 xpath 定位上级元素
- 优化 d 模式兄弟元素、父级元素的获取效率
- 优化标签页处理功能
- 其它小优化和修复
# V1.0.5
- 修复切换模式时 url 出错的 bug
# V1.0.3
- `DriverOptions`支持链式操作
- `download()`函数增加参数处理遇到已存在同名文件的情况,可选跳过、覆盖、自动重命名
- `download()`函数重命名调整为只需输入文件名,不带后缀名,输入带后缀名也可自动识别
# V1.0.1
- 增强拖拽功能和 chrome 设置功能
# V0.14.0
- `Drission`类增加代理设置和修改
# V0.12.4
- `click()``by_js`可接收`False`
- 修复一些 bug
# V0.12.0
- 增加`tag:tagName@arg=val`查找元素方式
- `MixPage`增加简易方式创建对象方式
# V0.11.0
- 完善`easy_set`的函数
- 元素增加多级定位函数
# v0.10.2
- 完善`attr``attrs`功能
# v0.10.1
- 增加`set_headless()`以及`to_iframe()`兼容全部原生参数
# v0.9.4
- 修复 bug
# v0.9.0
- 增加了元素拖拽和处理提示框功能
# v0.8.4
- 基本完成

View File

@ -1,112 +0,0 @@
这里介绍一些本库内置了人性化设计。
# ✔️ 无处不在的等待
网络环境不稳定,很多时候程序须要稍微等待一下才能继续运行。等待太少,会导致程序出错,等待太多,又会浪费时间。为了解决这些问题,本库在大量须要等待的部分内置了超时功能,并且可以随时灵活设置,大幅降低程序复杂性。
- 查找元素内置等待。可以为每次查找元素单独设定等待时间。有些页面会不定期出现提示信息,如果一律等待会太浪费时间,可以独立设置一个很短的超时时间,避免浪费。
- 等待下拉列表选项。很多下拉列表使用 js 加载,本库选择下拉列表时,会自动等待列表项出现。
- 等待弹窗。有时预期的 alert 未必立刻出现,本库处理 弹窗消息时也可以设置等待。
- 等待元素状态改变。使用`wait_ele()`方法可等待元素出现、消失、删除等状态。
- 点击功能也内置等待,如遇元素被遮挡可不断重试点击。
- 设置页面加载时限及加载策略。有时不需要完整加载页面资源,可根据实际须要设置加载策略。
---
# ✔️ 自动重试连接
在访问网站时,由于网络不稳定可能导致连接异常。本库设置了连接自动重试功能,当网页连接异常,会默认重试 3 次。当然也可以手动设置次数和间隔。
```python
page.get('xxxxxx', retry=5, interval=3) # 出错是重试 5 次,每次间隔 3 秒
```
---
# ✔️ 极简的定位语法
本库制定了一套简洁高效的查找元素语法,支持链式操作,支持相对定位。与 selenium 繁琐的语法相比简直不要太方便。
而且每次查找内置等待,可以独立设置每次查找超时时间。
同是设置了超时等待的查找,对比一下:
```python
# 使用 selenium
element = WebDriverWait(driver, 10).until(ec.presence_of_element_located((By.XPATH, '//*[contains(text(), "some text")]')))
# 使用 DrissionPage
element = page('some text', timeout=10)
```
---
# ✔️ 无需切入切出,逻辑清晰
使用过 selenium 的人都知道selenium 同一时间只能操作一个标签页或`<iframe>`元素,要操作其它标签页,或`<iframe>`元素,须要用`switch_to()`
方法切换,操作后还要切换回来。如果是多级`<iframe>`,还要逐级切入,相当麻烦。
DrissionPage 则无需这些麻烦的操作,它把每个标签页和`<iframe>`都看作独立的对象,可以同时并发操作,而且可以直接跨多层`<iframe>`获取里面的元素,然后直接处理,非常方便。
对比一下,获取 2 层`<iframe>`内一个 id 为`'div1'`的元素:
```python
# 使用 selenium
driver.switch_to.frame(0)
driver.switch_to.frame(0)
ele = driver.find_element(By.ID, 'div1')
driver.switch_to.default_content()
# 使用 DrissionPage
ele = page('#div1')
```
多标签页同时操作selenium 无此功能:
```python
tab1 = page.get_tab(page.tabs[0])
tab2 = page.get_tab(page.tabs[1])
tab1.get('https://www.baidu.com')
tab2.get('https://www.163.com')
```
---
# ✔️ 高度集成的便利功能
很多操作方法集成了常用功能,如`chick()`中内置`by_js`参数,可以直接改用 js 方式点击,而无需写独立的 js 语句。
---
# ✔️ 强大的下载功能
DrissionPage 内置一个下载工具,可实现大文件分块多线程下载文件。并且可以直接读取缓存数据保存图片,而无需控制页面作另存操作。
```python
img = page('tag:img')
img.save('img.png') # 直接保存图片到文件夹
```
---
# ✔️ 更多便捷的功能
- 可对整个网页截图,包括视口外的部分
- 每次运行程序可以反复使用已经打开的浏览器,无需每次从头运行
- s 模式访问网页时会自动纠正编码,无须手动设置
- s 模式在连接时会自动根据当前域名自动填写`Host``Referer`属性
- 下载工具支持多种方式处理文件名冲突、自动创建目标路径、断链重试等
- `MixPage`可自动下载合适版本的 chromedriver免去麻烦的配置
- 支持直接获取`after``before`伪元素的内容

View File

@ -1,38 +0,0 @@
以下代码实现一模一样的功能,对比两者的代码量:
🔸 获取元素内容
```python
url = 'https://baike.baidu.com/item/python'
# 使用 requests
from lxml import etree
headers = {'User-Agent':'Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.118 Safari/537.36'}
response = requests.get(url, headers = headers)
html = etree.HTML(response.text)
element = html.xpath('//h1')[0]
title = element.text
# 使用 DrissionPage
page = WebPage('s')
page.get(url)
title = page('tag:h1').text
```
?> **Tips:** <br>DrissionPage 自带默认 headers
🔸 下载文件
```python
url = 'https://www.baidu.com/img/flexible/logo/pc/result.png'
save_path = r'C:\download'
# 使用 requests
r = requests.get(url)
with open(f'{save_path}\\img.png', 'wb') as fd:
for chunk in r.iter_content():
fd.write(chunk)
# 使用 DrissionPage
page.download(url, save_path, 'img') # 支持重命名,处理文件名冲突
```

View File

@ -1,97 +0,0 @@
以下代码实现一模一样的功能,对比两者的代码量:
🔸 用显性等待方式查找第一个文本包含 some text 的元素。
```python
# 使用 selenium
element = WebDriverWait(driver).until(ec.presence_of_element_located((By.XPATH, '//*[contains(text(), "some text")]')))
# 使用 DrissionPage
element = page('some text')
```
🔸 跳转到第一个标签页
```python
# 使用 selenium
driver.switch_to.window(driver.window_handles[0])
# 使用 DrissionPage
page.to_tab(0)
```
🔸 按文本选择下拉列表
```python
# 使用 selenium
from selenium.webdriver.support.select import Select
select_element = Select(element)
select_element.select_by_visible_text('text')
# 使用 DrissionPage
element.select('text')
```
🔸 拖拽一个元素
```python
# 使用 selenium
ActionChains(driver).drag_and_drop(ele1, ele2).perform()
# 使用 DrissionPage
ele1.drag_to(ele2)
```
🔸 滚动窗口到底部(保持水平滚动条不变)
```python
# 使用 selenium
driver.execute_script("window.scrollTo(document.documentElement.scrollLeft, document.body.scrollHeight);")
# 使用 DrissionPage
page.scroll.to_bottom()
```
🔸 设置 headless 模式
```python
# 使用 selenium
options = webdriver.ChromeOptions()
options.add_argument("--headless")
# 使用 DrissionPage
set_headless(True)
```
🔸 获取伪元素内容
```python
# 使用 selenium
text = webdriver.execute_script('return window.getComputedStyle(arguments[0], "::after").getPropertyValue("content");', element)
# 使用 DrissionPage
text = element.pseudo_after
```
🔸 获取 shadow-root
新版 selenium 已可直接获取 shadow-root但生成的 ShadowRoot 对象功能实在是太少了。
```python
# 使用 selenium
shadow_element = webdriver.execute_script('return arguments[0].shadowRoot', element)
# 使用 DrissionPage
shadow_element = element.shadow_root
```
🔸 用 xpath 直接获取属性或文本节点(返回文本)
```python
# 使用 selenium
相当复杂
# 使用 DrissionPage
class_name = element('xpath://div[@id="div_id"]/@class')
text = element('xpath://div[@id="div_id"]/text()[2]')
```

View File

@ -1,27 +0,0 @@
用浏览器登录网站,然后切换到 requests 读取网页。两者会共享登录信息。
```python
from DrissionPage import WebPage
from time import sleep
# 创建页面对象,默认 d 模式
page = WebPage()
# 访问个人中心页面(未登录,重定向到登录页面)
page.get('https://gitee.com/profile')
# 使用 selenium 输入账号密码登录
page.ele('@id:user_login').input('your_user_name')
page.ele('@id:user_password').input('your_password\n')
sleep(1)
# 切换到 s 模式
page.change_mode()
# 登录后 session 模式的输出
print('登录后title', page.title, '\n')
```
**输出:**
```console
登录后title 个人资料 - 码云 Gitee.com
```

View File

@ -1,22 +0,0 @@
```python
# 接上段代码
foot = page.ele('#footer-left') # 用 id 查找元素
first_col = foot.ele('css:>div') # 使用 css selector 在元素的下级中查找元素(第一个)
lnk = first_col.ele('text:命令学') # 使用文本内容查找元素
text = lnk.text # 获取元素文本
href = lnk.attr('href') # 获取元素属性值
print(text, href, '\n')
# 简洁模式串联查找
text = page('@id:footer-left')('css:>div')('text:命令学').text
print(text)
```
**输出:**
```console
Git 命令学习 https://oschina.gitee.io/learn-git-branching/
Git 命令学习
```

View File

@ -1,11 +0,0 @@
DrissionPage 带一个简便易用的下载器,一行即可实现下载功能。
```python
from DrissionPage import WebPage
url = 'https://www.baidu.com/img/flexible/logo/pc/result.png'
save_path = r'C:\download'
page = WebPage('s')
page.download(url, save_path)
```

View File

@ -1,110 +0,0 @@
# ✔️ 运行环境
操作系统Windows、Linux 或 Mac。
python 版本3.6 及以上
支持浏览器Chromium 内核(如 Chrome 和 Edge
---
# ✔️ 安装
请使用 pip 安装 DrissionPage
```console
pip install DrissionPage
```
---
# ✔️ 升级
```console
pip install DrissionPage --upgrade
```
---
# ✔️ 导入
## 📍 页面类
页面类用于控制浏览器或收发数据包是最主要的工具。DrissionPage 包含三种主要页面类。根据须要在其中选择使用。
`WebPage`是功能最全面的页面类,既可控制浏览器,也可收发数据包:
```python
from DrissionPage import WebPage
```
如果只要控制浏览器,导入`ChromiumPage`
```python
from DrissionPage import ChromiumPage
```
如果只要收发数据包,导入`SessionPage`
```python
from DrissionPage import SessionPage
```
---
## 📍 配置类
很多时候我们须要设置启动参数,可导入以下两个类,但不是必须的。
`ChromiumOptions`类用于设置浏览器启动参数:
```python
from DrissionPage import ChromiumOptions
```
`SessionOptions`类用于设置`Session`对象启动参数:
```
from DrissionPage import SessionOptions
```
---
## 📍 其它工具
有两个我们可能须要用到的工具,需要时可以导入。
动作链,用于模拟一系列键盘和鼠标的操作:
```python
from DrissionPage import ActionChains
```
键盘按键类,用于键入 ctrl、alt 等按键:
```python
from DrissionPage import Keys
```
`easy_set`里保存了一些便捷的 ini 文件设置方法,可选择使用:
```python
from DrissionPage.easy_set import *
```
---
## 📍 旧版页面和配置类
旧版`MixPage`是基于 selenium 封装而成,使用方法与`WebPage`一致。
```python
from DrissionPage import MixPage
```
旧版配置类:
```python
from DrissionPage import DriverOptions
from DrissionPage import Drission
```

View File

@ -1,58 +0,0 @@
在开始之前,我们先进行一些简单设置。
如果只使用收发数据包功能,无须任何准备工作。
如果要控制浏览器,须设置浏览器路径。程序默认设置控制 Chrome所以以下用 Chrome 作为演示。如果要使用 Edge 或其它 Chromium 内核浏览器,设置方法是一样的。
作者发现 92 版的 Chrome 存在一些奇怪的问题,导致有些电脑环境下不能启动,请尽量避免使用。
# ✔️ 执行步骤
## 📍 关闭已打开的 Chrome
多个 Chrome 会造成冲突,如果已有打开的 Chrome 浏览器,请关闭。
后面在"创建页面对象"章节再介绍多 Chrome 浏览器共存的方法。
!>**注意:**<br>如果您使用的是 Edge 浏览器,直接关闭不能清干净进程,请打开任务管理器手动关闭进程。
---
## 📍 尝试启动浏览器
执行以下代码,如果正常启动了浏览器并且访问了百度,说明可直接使用,跳过后面的步骤即可。
```python
from DrissionPage import ChromiumPage
page = ChromiumPage()
page.get('https://www.baidu.com')
```
---
## 📍 设置路径
如果上面的步骤提示出错,请新建一个临时 py 文件,并输入以下代码,填入您电脑里的 Chrome 浏览器 exe 文件路径,然后执行。
!>**注意:**<br>这段代码只要运行一次即可,不要写到正式程序里。
```python
from DrissionPage.easy_set import set_paths
set_paths(browser_path=r'这里修改为您的Chrome浏览器exe文件路径')
```
这段代码会记录 Chrome 浏览器路径到配置文件。
---
## 📍 重试控制浏览器
现在,请重新执行第二步的代码,如果正确访问了百度,说明已经设置完成。
---
# ✔️ 说明
当您完成准备工作后,无须关闭浏览器,后面的上手示例可继续使用当前浏览器。

View File

@ -1,97 +0,0 @@
现在,我们通过一些例子,来直观感受一下 DrissionPage 的工作方式。
本示例演示使用`ChromiumPage`控制浏览器登录 gitee 网站。
# ✔️ 页面分析
网址:[https://gitee.com/login](https://gitee.com/login)
打开网址,按`F12`,我们可以看到页面 html 如下:
![](https://gitee.com/g1879/DrissionPage/raw/master/docs/imgs/gitee_1.jpg)
(图片点击放大)
用户名输入框`id``'user_login'`,密码输入框`id``'user_password'`,登录按钮`value``'登 录'`
我们可以用这三个属性定位这三个元素,然后对其输入数据和点击。
---
# ✔️ 示例代码
您可以把以下代码复制到编辑器,把账号和密码改为您自己的,可直接执行看到运行结果。
```python
from DrissionPage import ChromiumPage
# 创建页面对象,并启动或接管浏览器
page = ChroiumPage()
# 跳转到登录页面
page.get('https://gitee.com/login')
# 定位到账号文本框,获取文本框元素
ele = page.ele('#user_login')
# 输入对文本框输入账号
ele.input('您的账号')
# 定位到密码文本框并输入密码
page.ele('#user_password').input('您的密码')
# 点击登录按钮
page.ele('@value=登 录').click()
```
---
# ✔️ 示例详解
我们逐行解读代码:
```python
from DrissionPage import ChromiumPage
```
↑ 首先,我们导入用于控制浏览器的类`ChromiumPage`
```python
page = ChromiumPage()
```
↑ 接下来,我们创建一个`ChromiumPage`对象。
```python
page.get('https://gitee.com/login')
```
`get()`方法用于访问参数中的网址。它会等待页面完全加载,再继续执行后面的代码。您也可以修改等待策略,如等待 DOM 加载而不等待资源下载,就停止加载,这将在后面的章节说明。
```python
ele = page.ele('#user_login')
```
`ele()`方法用于查找元素,它返回一个`ChromiumElement`对象,用于操作元素。
`'#user_login'`是定位符文本,`#`意思是按`id`属性查找元素。
值得一提的是,`ele()`内置了等待,如果元素未加载,它会执行等待,直到元素出现或到达时限。默认超时时间 10 秒。
```python
ele.input('您的账号')
```
`input()`方法用于对元素输入文本。
```python
page.ele('#user_password').input('您的密码')
```
↑ 我们也可以进行链式操作,获取元素后直接输入文本。
```python
page.ele('@value=登 录').click()
```
↑ 输入账号密码后,以相同的方法获取按钮元素,并对其执行点击操作。
不同的是,这次通过其`value`属性作为查找条件。`@`表示按属性名查找。
到这里,我们已完成了自动登录 gitee 网站的操作。

View File

@ -1,98 +0,0 @@
本示例演示用`SessionPage`已收发数据包的方式采集 gitee 网站数据。
本示例不使用浏览器。
# ✔️ 页面分析
网址:[https://gitee.com/explore/all](https://gitee.com/explore/all)
这个示例的目标,要获取所有库的名称和链接,为避免对网站造成压力,我们只采集 3 页。
打开网址,按`F12`,我们可以看到页面 html 如下:
![](https://gitee.com/g1879/DrissionPage/raw/master/docs/imgs/gitee_2.jpg)
(图片点击放大)
从 html 代码中可以看到,所有开源项目的标题都是`class`属性为`'title project-namespace-path'``<a>`元素。我们可以遍历这些`<a>`元素,获取它们的信息。
同时,我们观察到,列表页网址是以页数为参数访问的,如第一页 url 为`https://gitee.com/explore/all?page=1`,页数就是`page`参数。因此我们可以通过修改这个参数访问不同的页面。
---
# ✔️ 示例代码
以下代码可直接运行并查看结果:
```python
from DrissionPage import SessionPage
# 创建页面对象
page = SessionPage()
# 爬取3页
for i in range(1, 4):
# 访问某一页的网页
p.get(f'https://gitee.com/explore/all?page={i}')
# 获取所有开源库<a>元素列表
links = p.eles('.title project-namespace-path')
# 遍历所有<a>元素
for link in links:
# 打印链接信息
print(link.text, link.link)
```
**输出:**
```console
小熊派开源社区/BearPi-HM_Nano https://gitee.com/bearpi/bearpi-hm_nano
明月心/PaddleSegSharp https://gitee.com/raoyutian/PaddleSegSharp
RockChin/QChatGPT https://gitee.com/RockChin/QChatGPT
TopIAM/eiam https://gitee.com/topiam/eiam
以下省略。。。
```
---
# ✔️ 示例详解
我们逐行解读代码:
```python
from DrissionPage import SessionPage
```
↑ 首先,我们导入用于收发数据包的页面类`SessionPage`
```python
page = SessionPage()
```
↑ 接下来,我们创建一个`SessionPage`对象。
```python
for i in ranage(1, 4):
    p.get(f'https://gitee.com/explore/all?page={i}')
```
↑ 然后我们循环 3 次,以构造每页的 url每次都用`get()`方法访问该页网址。
```python
links = p.eles('.title project-namespace-path')
```
↑ 访问网址后,我们用页面对象的`eles()`获取页面中所有`class`属性为`'title project-namespace-path'`的元素对象。
`eles()`方法用于查找多个符合条件的元素,返回由它们组成的`list`
这里查找的条件是`class`属性,`.`表示按`class`属性查找元素。
```python
for link in links:
print(link.text, link.link)
```
↑ 最后,我们遍历获取到的元素列表,获取每个元素的属性并打印出来。
`.text`获取元素的文本,`.link`获取元素的`href``src`属性。

View File

@ -1,123 +0,0 @@
这个示例演示`WebPage`如何切换控制浏览器和收发数据包两种模式。
通常,切换模式是用来应付登录检查很严格的网站,可以用浏览器处理登录,再转换模式用收发数据包的形式来采集数据。
但是这种场景须要有对应的账号,不便于演示。演示使用浏览器在百度搜索,然后转换到收发数据包的模式来读取数据。虽然现实使用意义不大,但可以了解其工作模式。
# ✔️ 页面分析
网址:[https://www.baidu.com](https://www.baidu.com)
打开网址,按`F12`,我们可以看到页面 html 如下:
![](https://gitee.com/g1879/DrissionPage/raw/master/docs/imgs/baidu_1.jpg)
(图片点击放大)
输入框`<input>`元素`id`属性为`'kw'`,“百度一下”按钮`<input>`元素`id`属性为`'su'`
输入关键词搜索后,再查看页面 html
![](https://gitee.com/g1879/DrissionPage/raw/master/docs/imgs/baidu_2.jpg)
(图片点击放大)
通过分析 html 代码,我们可以看出,每个结果的标题都存在于`<h3>`元素里。因此,我们可以获取页面中所有`<h3>`元素,再遍历获取其信息。
---
# ✔️ 示例代码
您可以直接运行以下代码:
```python
from DrissionPage import WebPage
# 创建页面对象
page = WebPage()
# 访问网址
page.get('https://www.baidu.com')
# 查找文本框元素并输入关键词
page('#kw').input('DrissionPage')
# 点击搜索按钮
page('#su').click(wait_loading=True)
# 切换到收发数据包模式
page.change_mode()
# 获取所有<h3>元素
links = page.eles('tag:h3')
# 遍历获取到的元素
for link in links:
# 打印元素文本
print(link.text)
```
**输出:**
```console
DrissionPage: 一个整合了selenium和requests_html的模块,...
python采集库DrissionPage- 腾讯云开发者社区-腾讯云
DrissionPage- Web应用开发 - 青少年人工智能资源与创新...
DrissionPage-demos: 使用DrissionPage爬取常见网站的示例。
DrissionPagev1.9.0 已经发布,WEB 自动化测试集成工具_程...
DrissionPage首页、文档和下载 - WEB 自动化测试集成工具 ...
DrissionPage- Gitee
DrissionPage—— Web 自动化测试集成工具 - OSCHINA - ...
DrissionPagev1.10.0 已经发布,WEB 自动化测试集成工具 |...
DrissionPagev2.2.1 发布,WEB 自动化测试集成工具
```
---
# ✔️ 示例详解
我们逐行解读代码:
```python
from DrissionPage import WebPage
```
↑ 首先,我们导入页面对象`WebPage`类。
```python
page = WebPage()
```
↑ 接下来,我们创建一个`WebPage`对象。
```python
page.get('https://www.baidu.com')
```
↑ 然后控制浏览器访问百度。
```python
page('#kw').input('DrissionPage')
page('#su').click(wait_loading=True)
```
↑ 再通过模拟输入的方式输入关键词,模拟点击搜索按钮。
这里查找元素的方法上两个示例已经讲过,不再细说。
`click()`方法里面的`wait_loading`参数用于等待页面进入加载状态,避免操作过快出现异常。
```python
page.change_mode()
```
`change_mode()`方法用于切换工作模式,从当前控制浏览器的模式切换到收发数据包模式。
切换的时候程序会在新模式重新访问当前 url。
```python
links = page.eles('tag:h3')
```
↑ 切换后,我们可以用与控制浏览器一致的语法,获取页面元素,这里`eles()`方法是获取页面中所有`<h3>`元素,它返回这些元素对象组成的列表。`tag:`是查找条件,表示查找某种类型的元素。
```python
for link in links:
print(link.text)
```
↑ 最后,我们遍历这些元素,并逐个打印它们包含的文本。

View File

@ -1,270 +0,0 @@
本节讲解 DrissionPage 的一些基本概念。了解它大概的构成。
如果您觉得有点懵,可直接跳过本节。
# ✔️ 网页自动化
网页自动化的形式通常有两种,它们各有优劣:
- 直接向服务器发送数据包,获取需要的数据
- 控制浏览器跟网页进行交互
前者轻量级,速度快,便于多线程、分布式部署,如 requests 库。但当数据包构成复杂,甚至加入加密技术时,开发过程烧脑程度直线上升。
鉴于此DrissionPage 以页面为单位将两者整合,对 Chromium 协议 和 requests 进行了重新封装,实现两种模式的互通,并加入常用的页面和元素控制功能,可大幅降低开发难度和代码量。
用于操作浏览器的对象叫 Driverrequests 用于管理连接的对象叫 SessionDrission 就是它们两者的合体。Page 表示以 POM 模式封装。
在旧版本,本库是通过对 selenium 和 requests 的重新封装实现的。
从 3.0 版开始,作者另起炉灶,用 chromium 协议自行实现了 selenium 全部功能,从而摆脱了对 selenium 的依赖,功能更多更强,运行效率更高,开发更灵活。
如果您想了解旧版,请查阅“旧版使用方法”章节。
---
# ✔️ 主要对象
无论是控制浏览器,还是收发数据包,其操作逻辑是一致的,即先创建页面对象,通过页面对象控制浏览器或收发数据包,再从浏览器或返回的数据中获取元素对象,从而实现数据的获取或页面的控制。因此,最主要的对象就是两种:页面对象,及其生成的元素对象。
常用的页面对象有 3 种:
- `ChromiumPage`:单纯用于操作浏览器的页面对象
- `SessionPage`:单纯用于收发数据包的页面对象
- `WebPage`:整合浏览器控制和收发数据包于一体的页面对象
## 📍 `ChromiumPage`
`ChromiumPage`是用于操作浏览器的页面对象,它仅用于操作浏览器,而不能收发数据包。支持 Chroium 内核浏览器,如 Chrome、Edge 等。创建页面对象时,程序会自动启动浏览器,如果指定端口已存在浏览器,就接管该浏览器。
!>**注意:**<br>尝试以下代码前,请先关闭已经打开的 Chrome 浏览器。<br>如果启动失败,请查看“入门指南”里“准备工作”一节配置浏览器路径。
```python
from DrissionPage import ChromiumPage
# 创建页面对象
page = ChromiumPage()
# 控制浏览器访问百度
page.get('https://www.baidu.com') # 使用页面对象访问
# 定位输入框并输入关键字
page.ele('#kw').input('DrissionPage')
# 点击“百度一下”按钮
page.ele('@value=百度一下').click()
```
---
## 📍 `ChromiumElement`
`ChromiumElemnet`对象是浏览器页面元素对象,可对浏览器中的元素进行点击、文本输入、拖拽、运行 js 脚本等操作,也可以基于这个元素查找其下级或周围的元素。
```python
# 获取id为'kw'的元素对象
ele = page('#kw')
# 点击元素
ele.click()
# 输入文本
ele.input('some text')
# 获取 class 属性
attr = ele.attr('class')
# 设置 style 属性
ele.set_attr('style', 'display:none;')
# 获取其子元素中所有 a 元素
links = ele.eles('tag:a')
```
除了最常用的`ChromiumElement`对象,浏览器还会产生`ChromiumFrame``ChromiumShadowRootElement``ChromiumTab`对象,详细用法见相关章节。
---
## 📍 `SessionPage`
`SessionPage`是用于收发数据包的页面对象,它仅用于收发数据包,而不能操作浏览器。
```python
from DrissionPage import SessionPage
# 创建页面对象
page = SessionPage()
# 访问百度
page.get('https://www.baidu.com')
# 获取元素对象
ele = page('#kw')
# 打印元素html
print(ele.html)
```
**输出:**
```console
<input id="kw" name="wd" class="s_ipt" value="" maxlength="255" autocomplete="off">
```
---
## 📍 `SessionElement`
`SessionElement`对象是`SessionPage`产生的元素对象,可以读取元素信息,或基于它进行下级元素查找、相对定位其它元素,但不能执行点击等操作。
这种对象解析效率非常高,当浏览器页面太复杂时,可把`ChromiumElement`元素转换为`SessionElement`进行解析,提高速度。转换的同时可以执行下级元素的查找。
```python
# 获取元素 tag 属性
tag = ele.tag
# 在元素下查找第一个 name 为 name1 的子元素
ele1 = ele.ele('@name=name1')
```
---
## 📍 `WebPage`
`WebPage`是整合了上面两者的页面对象,既可控制浏览器,又可收发数据包,并且可以在这两者之间共享登录信息。
它有两种工作模式d 模式和 s 模式。d 模式用于控制浏览器s 模式用于收发数据包。`WebPage`可在两种模式间切换,但同一时间只能处于其中一种模式。
在 d 模式时,`WebPage`获取的元素是`ChromiumElement`,在 s 模式时,获取到的元素是`SessionElement`
```python
from DrissionPage import WebPage
# 创建页面对象初始d模式
page = WebPage('d')
# 访问百度
page.get('http://www.baidu.com')
# 定位输入框并输入关键字
page.ele('#kw').input('DrissionPage')
# 点击“百度一下”按钮
page.ele('@value=百度一下').click(wait_loading=True)
# 切换到s模式
page.change_mode()
# 获取所有结果元素
results = page.eles('tag:h3')
# 遍历所有结果
for result in results:
# 打印结果文本
print(result.text)
```
**输出:**
```console
DrissionPage: 一个整合了selenium和requests_html的模块,...
g1879/DrissionPage- GitHub
python采集库DrissionPage- 腾讯云开发者社区-腾讯云
DrissionPage- Web应用开发 - 青少年人工智能资源与创新...
DrissionPage-demos: 使用DrissionPage爬取常见网站的示例。
DrissionPagev1.9.0 已经发布,WEB 自动化测试集成工具_程...
DrissionPage首页、文档和下载 - WEB 自动化测试集成工具 ...
DrissionPage—— Web 自动化测试集成工具 - OSCHINA - ...
DrissionPagev1.10.0 已经发布,WEB 自动化测试集成工具 |...
DrissionPagev2.2.1 发布,WEB 自动化测试集成工具
```
详细使用方法见“创建页面对象”和“操作页面”章节。
---
# ✔️ 工作模式
如上所述,`WebPage`既可控制浏览器也可用数据包方式访问网络数据。它有两种工作方式d 模式和 s 模式。
页面对象可以在这两种模式间切换,两种模式拥有一致的使用方法,但任一时间只能处于其中一种模式。
## 📍 d 模式
d 模式既表示 Driver还有 Dynamic 的意思。
d 模式用于控制浏览器,不仅可以读取浏览器获取到的信息,还能对页面进行操作,如点击、填写、开关标签页、改变元素属性、执行 js 脚本等等。
d 模式功能强大,但运行速度受浏览器制约非常缓慢,而且需要占用大量内存。
---
## 📍 s 模式
s 模式既表示 Session还有 speed、silence 的意思。
s 模式的运行速度比 d 模式快几个数量级,但只能基于数据包进行读取或发送,不能对页面进行操作,不能运行 js。
爬取数据时,如网站数据包较为简单,应首选 s 模式。
---
## 📍 模式切换
`WebPage`对象可以在 d 模式和 s 模式之间切换,这通常用于以下情况:
- 当登录验证很严格,难以解构,如有验证码的时候,用浏览器处理登录,然后转换成 s 模式爬取数据。既避免了处理烧脑的 js又能享受 s 模式的速度。
- 页面数据由 js 产生,且页面结构极其复杂,可以用 d 模式读取页面元素,然后把元素转成 s 模式的元素进行分析。可以极大地提高 d 模式的处理速度。
上述两种情况,第一种是整个页面对象进行模式转换:
```python
page.change_mode()
```
第二种是在 d 模式时仅对部分元素进行转换,如把表格元素转换为 s 模式的元素,在解析时速度比直接解析 d 模式元素快几个数量级。甚至可以把整个页面转换为 s 模式的元素,也是可以的。
```python
# 获取页面上一个 table 元素
ele = page.ele('tag:table')
# 把该元素转换成 s 模式的元素
ele = ele.s_ele()
# 获取所有行元素
rows = ele.eles('tag:tr')
```
---
# ✔️ 结构图
`WebPage`继承自`ChromiumPage``SessionPage`,前者负责控制浏览器,后者负责数据包收发,因此`WebPage`既能控制浏览器,也能收发数据包,并且能在两种模式中共享登录状态。
![](https://gitee.com/g1879/DrissionPage/raw/master/docs/imgs/webpage.jpg)
---
# ✔️ 配置管理
无论 requests 还是浏览器,都通常须要一些配置信息才能正常工作,如长长的`user_agent`、浏览器 exe 文件路径、浏览器配置等。这些代码往往是繁琐而重复的,不利于代码的简洁。
因此DrissionPage 使用配置文件记录常用配置信息,程序会自动读取默认配置文件里的内容。所以,在示例中,通常看不见配置信息的代码。
这个功能支持用户保存不同的配置文件,按情况调研,也可以支持直接把配置写在代码里面,屏蔽读取配置文件。
?> **Tips** <br>当须要打包程序时,必须把配置写到代码里,或打包后手动复制配置文件到运行路径,否则会报错。详见相关章节。
---
# ✔️ 定位符
定位符用于定位页面中的元素,是本库一大特色,能够用非常简明的方式来获取元素,简洁易用。可读性和易用性高于 xpath 等其它方式,并且兼容 xpath、css selector、selenium 定位符。
以下是一组对比:
定位文本包含`'abc'`的元素:
```python
# DrissionPage
ele = page('abc')
# selenium
ele = driver.find_element(By.XPATH, '//*[contains(text(), "abc"]')
```
定位 class 为`'abc'`的元素
```python
# DrissionPage
ele = page('.abc')
# selenium
ele = driver.find_element(By.CLASS_NAME, 'abc')
```
定位 ele 元素的兄弟元素
```python
# DrissionPage
ele1 = ele.next() # 获取后一个元素
ele1 = ele.prev(index=2) # 获取前面第二个元素
# selenium
ele1 = ele.find_element(By.XPATH, './/following-sibling::*') # 获取有i一个元素
ele1 = ele.find_element(By.XPATH, './/preceding-sibling::*[2]') # 获取前面第二个元素
```
显然,本库的定位语句更简洁易懂,还有很多灵活好用的方法,详见“查找元素”章节。

View File

@ -1,46 +0,0 @@
`SessionPage`对象和`WebPage`对象的 s 模式,可用收发数据包的形式访问网页。
本章介绍`SessionPage`
顾名思义,`SessionPage`是一个使用使用`Session`requests 库)对象的页面,它使用 POM 模式封装了网络连接和 html 解析功能,使收发数据包也可以像操作页面一样便利。
并且,由于加入了本库独创的查找元素方法,使数据的采集便利性远超 requests + beautifulsoup 等组合。
`SessionPage`是本库几种页面对象中最简单的,我们先从它开始入手。
我们看个简单的例子,来了解`SessionPage`的工作方式。
---
获取 gitee 推荐项目第一页所有项目。
```python
# 导入
from DrissionPage import SessionPage
# 创建页面对象
page = SessionPage()
# 访问网页
page.get('https://gitee.com/explore/all')
# 在页面中查找元素
items = page.eles('t:h3')
# 遍历元素
for item in items[:-1]:
# 获取当前<h3>元素下的<a>元素
lnk = item('tag:a')
# 打印<a>元素文本和href属性
print(lnk.text, lnk.link)
```
**输出:**
```console
七年觐汐/wx-calendar https://gitee.com/qq_connect-EC6BCC0B556624342/wx-calendar
ThingsPanel/thingspanel-go https://gitee.com/ThingsPanel/thingspanel-go
APITable/APITable https://gitee.com/apitable/APITable
Indexea/ideaseg https://gitee.com/indexea/ideaseg
CcSimple/vue-plugin-hiprint https://gitee.com/CcSimple/vue-plugin-hiprint
william_lzw/ExDUIR.NET https://gitee.com/william_lzw/ExDUIR.NET
anolis/ancert https://gitee.com/anolis/ancert
cozodb/cozo https://gitee.com/cozodb/cozo
后面省略...
```

View File

@ -1,88 +0,0 @@
`SessionPage`对象和`WebPage`对象的 s 模式都能收发数据包,本节只介绍`SessionPage`的创建,在`WebPage`的章节再对其进行介绍。
# ✔️ `SessionPage`初始化参数
`SessionPage`对象是 3 种页面对象中最简单的。
| 初始化参数 | 类型 | 默认值 | 说明 |
|:--------------------:|:-----------------------------:|:------:| --------------------------------------------------------------- |
| `session_or_options` | `Session`<br>`SessionOptions` | `None` | 传入`Session`对象时使用该对象收发数据包;传入`SessionOptions`对象时用该配置创建`Session`对象 |
| `timeout` | `float` | `None` | 连接超时时间,为`None`则从配置文件中读取 |
---
# ✔️ 直接创建
这种方式代码最简洁,程序会从配置文件中读取配置,自动生成页面对象。
```python
from DrissionPage import SessionPage
page = SessionPage()
```
`SessionPage`无须控制浏览器,无须做任何配置即可使用。
!>**注意:**<br>这种方式的程序不能直接打包,因为使用到 ini 文件。可参考“打包程序”一节的方法。
---
# ✔️ 通过配置信息创建
如果须要在使用前进行一些配置,可使用`SessionOptions`。它是专门用于设置`Session`对象初始状态的类,内置了常用的配置。详细使用方法见“启动配置”一节。
## 📍 使用方法
`SessionPage`创建时,将已创建和设置好的`SessionOptions`对象以参数形式传递进去即可。
| 初始化参数 | 类型 | 默认值 | 说明 |
| ----------- | ------ | ------ | ------------------------ |
| `read_file` | `bool` | `True` | 是否从 ini 文件中读取配置信息 |
| `ini_path` | `str` | `None` | 文件路径,为`None`则读取默认 ini 文件 |
!>**注意:**<br> `Session`对象创建后再修改这个配置是没有效果的。
```python
# 导入 SessionOptions
from DrissionPage import SessionPage, SessionOptions
proxies = {'http': 'http://127.0.0.1:1080',
'https': 'http://127.0.0.1:1080'}
# 创建配置对象,并设置代理信息
so = SessionOptions().set_proxies(proxies)
# 用该配置创建页面对象
page = SessionPage(session_or_options=so)
```
?>**Tips**<br>您可以把配置保存到配置文件以后自动读取,详见”启动配置“章节。
---
## 📍 从指定 ini 文件创建
以上方法是使用默认 ini 文件中保存的配置信息创建对象,你可以保存一个 ini 文件到别的地方,并在创建对象时指定使用它。
```python
from DrissionPage import SessionPage, SessionOptinos
# 创建配置对象时指定要读取的ini文件路径
so = SessionOptinos(ini_path=r'./config1.ini')
# 使用该配置对象创建页面
page = SessionPage(session_or_options=so)
```
---
# ✔️ 传递控制权
当须要使用多个页面对象共同操作一个页面时,可在页面对象创建时接收另一个页面间对象传递过来的`Session`对象,以达到多个页面对象同时使用一个`Session`对象的效果。
```python
# 创建一个页面
page1 = SessionPage()
# 获取页面对象内置的Session对象
session = page1.session
# 在第二个页面对象初始化时传递该对象
page2 = SessionPage(session_or_options=session)
```

View File

@ -1,126 +0,0 @@
`SessionPage``WebPage`的 s 模式基于 requests 进行网络连接,因此可使用 requests 内置的所有请求方式,包括`get()``post()``head()``options()``put()`
`patch()``delete()`。不过本库目前只对`get()``post()`做了封装和优化,其余方式可通过调用页面对象内置的`Session`对象使用。这里只对`SessionPage`
进行说明,后面章节再单独介绍`WebPage`
# ✔️ `get()`
`get()`方法语法与 requests 的`get()`方法一致,在此基础上增加了连接失败重试功能。与 requests 不一样的是,它不返回`Response`对象。
| 参数名称 | 类型 | 默认值 | 说明 |
|:-------------:|:----------------:|:-------:| --------------------------- |
| `url` | `str` | 必填 | 目标 url |
| `show_errmsg` | `bool` | `False` | 连接出错时是否显示和抛出异常 |
| `retry` | `int` | `None` | 重试次数,为`None`时使用页面参数,默认 3 |
| `interval` | `int`<br>`float` | `None` | 重试间隔(秒),为`None`时使用页面参数,默认 2 |
| `timeout` | `int`<br>`float` | `None` | 加载超时时间(秒) |
| `**kwargs` | - | `None` | 连接所需其它参数,具体见 requests 用法 |
| 返回类型 | 说明 |
|:------:| ----- |
| `bool` | 否连接成功 |
`**kwargs`参数与 requests 中该参数使用方法一致,但有一个特点,如果该参数中设置了某一项(如`headers`),该项中的每个项会覆盖从配置中读取的同名项,而不会整个覆盖。
就是说,如果想继续使用配置中的`headers`信息,而只想修改其中一项,只需要传入该项的值即可。这样可以简化代码逻辑。
实用功能:
- 程序会根据要访问的网址自动在`headers`中加入`Host``Referer`
- 程序会自动从返回内容中确定编码,一般情况无须手动设置
普通访问网页:
```python
from DrissionPage import SessionPage
page = SessionPage()
page.get('http://g1879.gitee.io/drissionpage')
```
使用连接参数访问网页:
```python
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)
```
!>**注意:**<br>s 模式访问时默认设置`redirect`参数为`False`,即访问重定向链接时须手动处理。
---
# ✔️ `post()`
此方法是用 post 方式请求页面。用法与`get()`一致。
| 参数名称 | 类型 | 默认值 | 说明 |
|:-------------:|:---------------:|:-------:| --------------------------- |
| `url` | `str` | 必填 | 目标url |
| `data` | `dict`<br>`str` | `None` | 提交的数据 |
| `json` | `dict`<br>`str` | `None` | 提交的数据 |
| `show_errmsg` | `bool` | `False` | 连接出错时是否显示和抛出异常 |
| `retry` | `int` | `None` | 重试次数,为`None`时使用页面参数,默认 3 |
| `interval` | `float` | `None` | 重试间隔(秒),为`None`时使用页面参数,默认 2 |
| `timeout` | `float` | `None` | 加载超时时间(秒) |
| `**kwargs` | - | `None` | 连接所需其它参数,具体见 requests 用法 |
| 返回类型 | 说明 |
|:------:| ----- |
| `bool` | 否连接成功 |
```python
from DrissionPage import SessionPage
page = SessionPage()
data = {'username': 'xxxxx', 'pwd': 'xxxxx'}
page.post('http://example.com', data=data)
# 或
page.post('http://example.com', json=data)
```
`data`参数和`json`参数都可接收`str``dict`格式数据,即有以下 4 种传递数据的方式:
```python
# 向 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'})
```
具体使用哪种,按服务器要求而定。
---
# ✔️ 其它请求方式
本库只针对常用的 get 和 post 方式作了优化,但也可以通过提取页面对象内的`Session`对象以原生 requests 代码方式执行其它请求方式。
```python
from DrissionPage import SessionPage
page = SessionPage()
# 获取内置的 Session 对象
session = page.session
# 以 head 方式发送请求
response = session.head('https://www.baidu.com')
print(response.headers)
```
**输出:**
```console
{'Accept-Ranges': 'bytes', 'Cache-Control': 'private, no-cache, no-store, proxy-revalidate, no-transform', 'Connection': 'keep-alive', 'Content-Length': '277', 'Content-Type': 'text/html', 'Date': 'Tue, 04 Jan 2022 06:49:18 GMT', 'Etag': '"575e1f72-115"', 'Last-Modified': 'Mon, 13 Jun 2016 02:50:26 GMT', 'Pragma': 'no-cache', 'Server': 'bfe/1.0.8.18'}
```

View File

@ -1,181 +0,0 @@
成功访问网页后,可使用`SessionPage`自身属性和方法获取页面信息。
```python
from DrissionPage import SessionPage
page = SessionPage()
page.get('http://www.baidu.com')
# 获取页面标题
print(page.title)
# 获取页面html
print(page.html)
```
**输出:**
```console
百度一下,你就知道
<!DOCTYPE html>
<!--STATUS OK--><html> <head><meta http-equi...
```
---
# ✔️ 页面信息
## 📍 `url`
此属性返回当前访问的 url。
**返回类型:**`str`
---
## 📍 `url_available`
此属性以布尔值返回当前链接是否可用。
**返回类型:**`bool`
---
## 📍 `title`
此属性返回当前页面`title`文本。
**返回类型:**`str`
---
## 📍 `html`
此属性返回当前页面 html 文本。
**返回类型:**`str`
---
## 📍 `json`
此属性把返回内容解析成 json。
比如请求接口时,若返回内容是 json 格式,用`html`属性获取的话会得到一个字符串,用此属性获取可将其解析成`dict`
**返回类型:**`dict`
---
# ✔️ 运行参数信息
## 📍 `timeout`
此属性返回网络请求超时时间。默认为 10可对其赋值设置。
**返回类型:**`int``float`
```python
# 创建页面对象时指定
page = SessionPage(timeout=5)
# 修改 timeout
page.timeout = 20
```
---
## 📍 `retry_times`
此属性为网络连接失败时的重试次数。默认为 3可对其赋值。
**返回类型:**`int`
```python
# 修改重试次数
page.retry_times = 5
```
---
## 📍 `retry_interval`
此属性为网络连接失败时的重试等待间隔秒数。默认为 2可对其赋值。
**返回类型:**`int``float`
```python
# 修改重试等待间隔时间
page.retry_interval = 1.5
```
---
# ✔️ cookies 信息
## 📍 `cookies`
此属性以`dict`方式返回当前页面所使用的 cookies。
**返回类型:**`dict`
---
## 📍 `get_cookies()`
此方法获取 cookies 并以 cookie 组成的`list`形式返回。
**返回类型:**`dict``list`
| 参数名称 | 类型 | 默认值 | 说明 |
|:-------------:|:------:|:-------:| --------------------------------------- |
| `as_dict` | `bool` | `False` | 是否以字典方式返回结果,为`False`返回 cookie 组成的`list` |
| `all_domains` | `bool` | `False` | 是否返回所有域的 cookies`False`则指返回当前域名的 |
| 返回类型 | 说明 |
|:------:| ------------- |
| `dict` | cookies 字典 |
| `list` | cookies 组成的列表 |
**示例:**
```python
from DrissionPage import SessionPage
page = SessionPage()
page.get('http://www.baidu.com')
page.get('http://gitee.com')
for i in page.get_cookies(as_dict=False, all_domains=True):
print(i)
```
**输出:**
```
{'domain': '.baidu.com', 'domain_specified': True, ......}
......
{'domain': 'gitee.com', 'domain_specified': False, ......}
......
```
---
# ✔️ 内嵌对象
## 📍 `session`
此属性返回当前页面对象使用的`Session`对象。
**返回类型:**`Session`
---
## 📍 `response`
此属性为请求网页后生成的`Response`对象,本库没实现的功能可直接获取此属性调用 requests 库的原生功能。
**返回类型:**`Response`
```python
# 打印连接状态
r = page.response
print(r.status_code)
```

View File

@ -1,718 +0,0 @@
本节介绍如何获取元素对象。
定位元素是自动化最重要的的技能,虽然可在开发者工具直接复制绝对路径,但这样做一来代码冗长,可读性低,二来难以应付动态变化的页面。
因此本库提供一套简洁易用的语法,用于快速定位元素,并且内置等待功能、支持链式查找,减少了代码的复杂性。
定位元素大致分为三种方法:
- 在页面或元素内查找子元素
- 根据 DOM 结构相对定位
- 根据页面布局位置相对定位
# ✔️ 示例
先看一些示例,后面在详细讲解用法。
## 📍 简单示例
假设有这样一个页面,本节示例皆使用此页面:
```html
<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>
```
我们可以用页面对象去获取其中的元素:
```python
# 获取 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')
```
也可以获取到一个元素,然后在它里面或周围查找元素:
```python
# 获取到一个元素div1
div1 = page.ele('#one')
# 在div1内查找所有p元素
p_list = div1.eles('tag:p')
# 获取div1后面一个元素
div2 = div1.next()
```
---
## 📍 实际示例
复制此代码可直接运行查看结果。
```python
from DrissionPage import SessionPage
page = 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)
```
**输出:**
```console
全部推荐项目
前沿技术
智能硬件
IOT/物联网/边缘计算
车载应用
...
```
---
# ✔️ 查找元素方法
## 📍 查找单个元素
🔸 `ele()`
页面对象和元素对象都拥有此方法,用于查找第一个匹配条件的元素。
页面对象和元素对象的`ele()`方法参数名称稍有不同,但用法一样。
?>**Tips**<br>使用 xpath 可直接获取后代元素的属性。
| 参数名称 | 类型 | 默认值 | 说明 |
|:------------------:|:----------------------------------------------:|:------:| -------------------------------------------- |
| `loc_or_str`(元素对象) | `str`<br>`Tuple[str, str]` | 必填 | 元素的定位信息,可以是查询字符串,或 loc 元组 |
| `loc_or_ele`(页面对象) | `str`<br>`SessionElement`<br>`Tuple[str, str]` | 必填 | 元素的定位信息可以是查询字符串、loc 元组或一个`SessionElement`对象 |
| `timeout` | `int`<br>`float` | `None` | 不起实际作用,用于和`ChromiumElement`对应,便于无差别调用 |
| 返回类型 | 说明 |
|:----------------:| --------------------- |
| `SessionElement` | 返回查找到的第一个符合条件的元素对象 |
| `None` | 未找到符合条件的元素时返回`None` |
| `str` | 使用 xpath可直接获取后代元素的属性 |
**示例:**
```python
from DrissionPage import SessionPage
page = SessionPage()
# 在页面内查找元素
ele1 = page.ele('#one')
# 在元素内查找后代元素
ele2 = ele1.ele('第二行')
# 使用 xpath 获取后代中第一个 div 元素的 class 属性(元素内查找可用)
ele_class = ele1.ele('xpath://p/@class')
```
---
## 📍 查找多个元素
🔸 `eles()`
此方法与`ele()`相似,但返回的是匹配到的所有元素组成的列表,用 xpath 获取元素属性时,返回属性文本组成的列表。
| 参数名称 | 类型 | 默认值 | 说明 |
|:------------:|:--------------------------:|:------:| ------------------------------------- |
| `loc_or_str` | `str`<br>`Tuple[str, str]` | 必填 | 元素的定位信息,可以是查询字符串,或 loc 元组 |
| `timeout` | `int`<br>`float` | `None` | 不起实际作用,用于和`ChromiumElement`对应,便于无差别调用 |
| 返回类型 | 说明 |
|:----------------------:| ---------------------------- |
| `List[SessionElement]` | 返回找到的所有`SessionElement`组成的列表 |
| `List[str]` | 使用 xpath可直接获取后代元素的属性 |
**示例:**
```python
# 获取ele元素内的所有p元素
p_eles = ele.eles('tag:p')
# 打印第一个p元素的文本
print(p_eles[0])
```
---
# ✔️ 查找语法
我们使用一套简洁高效的语法去定位元素,大大简化了定位元素的代码量,增强了功能,也兼容 css selector、xpath、selenium 原生的 loc 元组。
🔸 **匹配模式** 指字符串是否完全匹配,有以下两种:
## 📍 精确匹配符 `=`
表示精确匹配,匹配完全符合的文本或属性。
---
## 📍 模糊匹配符 `:`
表示模糊匹配,匹配含有某个字符串的文本或属性。
---
🔸 **关键字** 是出现在定位语句最左边,用于指明该语句以哪种方式去查找元素,有以下这些:
## 📍 id 匹配符 `#`
表示`id`属性,只在语句最前面且单独使用时生效,可配合`=``:`
```python
# 在页面中查找id属性为one的元素
ele1 = page.ele('#one')
# 在ele1元素内查找id属性包含ne文本的元素
ele2 = ele1.ele('#:ne')
```
---
## 📍 class 匹配符 `.`
表示`class`属性,只在语句最前面且单独使用时生效,可配合`=``:`
```python
# 查找class属性为p_cls的元素
ele2 = ele1.ele('.p_cls')
# 查找class属性包含_cls文本的元素
ele2 = ele1.ele('.:_cls')
```
---
## 📍 单属性匹配符 `@`
表示某个属性,只匹配一个属性。
`@`关键字只有一个简单功能,就是匹配`@`后面的内容,不再对后面的字符串进行解析。因此即使后面的字符串也存在`@``@@`,也作为要匹配的内容对待。
!> **注意:**
如果属性中包含特殊字符,如包含`@`,用这个方式不能正确匹配到,须使用 css selector 方式查找。且特殊字符要用`\`转义。
```python
# 查找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"]')
```
---
## 📍 多属性匹配符 `@@`
多属性匹配时使用,个数不限。还能匹配要忽略的元素,匹配文本时也和`@`不一样。
`@@`后跟 - 时,表示 not。如
- `@@-name`表示匹配没有`name`属性的元素
- `@@-name=ele_name`表示匹配`name`属性不为`ele_name`的元素
如有以下情况,不能使用此方式,须改用 xpath 的方式:
- 匹配文本或属性中出现`@@`
- 属性名本身以`-`开头
!> **注意:**<br>如果属性中包含特殊字符,如包含`@`,用这个方式不能正确匹配到,须使用 css selector 方式查找。且特殊字符要用`\`转义。
```python
# 查找name属性为name且class属性包含cls文本的元素
ele2 = ele1.ele('@@name=row1@@class:cls')
# 查找没有class属性的元素
ele2 = ele1.ele('@@-class')
# 查找name属性不包含row1的元素
ele2 = ele1.ele('@@-name:row1')
```
---
## 📍 文本匹配符 `text`
要匹配的文本,查询字符串如开头没有任何关键字,也表示根据传入的文本作模糊查找。
如果元素内有多个直接的文本节点,精确查找时可匹配所有文本节点拼成的字符串,模糊查找时可匹配每个文本节点。
没有任何匹配符时,默认匹配文本。
```python
# 查找文本为“第二行”的元素
ele2 = ele1.ele('text=第二行')
# 查找文本包含“第二”的元素
ele2 = ele1.ele('text:第二')
# 与上一行一致
ele2 = ele1.ele('第二')
```
?> **Tips** <br>若要查找的文本包含`text:` ,可下面这样写,即第一个`text:` 为关键字,第二个是要查找的内容:
```python
ele2 = page.ele('text:text:')
```
---
## 📍 文本匹配符 `text()`
作为查找属性时使用的文本关键字,必须与`@``@@`配合使用。
```python
# 查找文本为“第二行”的元素
ele2 = ele1.ele('@text()=第二行')
# 查找文本包含“第二行”的元素
ele2 = ele1.ele('@text():二行')
# 查找文本为“第二行”且class属性为p_cls的元素
ele2 = ele1.ele('@@text()=第二行@@class=p_cls')
# 查找文本为 some text 且没有任何属性的元素(因第一个 @@ 后为空)
ele2 = ele1.ele('@@@@text():some text')
# 查找直接子文本包含 some text 字符串的元素
ele = page.ele('@text():二行')
# 查找元素内部包含 some text 字符串的元素
ele = page.ele('@@text():二行')
```
须要注意的是,`'text=xxxx'``'@text()=xxxx'`使用上是有细微差别的。
`text=`表示在元素的直接子文本节点中匹配,`@text()=`会忽略一些文本标签,在整个元素的内容里匹配。
---
## 📍 类型匹配符 `tag`
表示元素的标签,只在语句最前面且单独使用时生效,可与`@``@@`配合使用。`tag:``tag=`效果一致。
```python
# 定位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()=第二行')
# 查找直接文本节点包含“二行”字符串的p元素
ele2 = ele1.ele('tag:p@text():二行')
# 查找内部文本节点包含“二行”字符串的p元素
ele2 = ele1.ele('tag:p@@text():二行')
```
!> **注意:** <br>`tag:div@text():text``tag:div@@text():text` 是有区别的,前者只在`div`的直接文本节点搜索,后者搜索`div`的整个内部。
---
## 📍 css selector 匹配符 `css`
表示用 css selector 方式查找元素。`css:``css=`效果一致。
```python
# 查找 div 元素
ele2 = ele1.ele('css:.div')
# 查找 div 子元素元素,这个写法是本库特有,原生不支持
ele2 = ele1.ele('css:>div')
```
---
## 📍 xpath 匹配符 `xpath`
表示用 xpath 方式查找元素。`xpath:``xpath=`效果一致。
在元素中查找时,该方法支持完整的 xpath 语法,能使用 xpath 直接获取元素属性selenium 不支持这种用法。
```python
# 查找 div 元素
ele2 = ele1.ele('xpath:.//div')
# 和上面一行一样,查找元素的后代时,// 前面的 . 可以省略
ele2 = ele1.ele('xpath://div')
# 获取 div 元素的 class 属性,返回字符串
txt = ele1.ele('xpath://div/@class')
```
?> **Tips:** <br>查找元素的后代时selenium 原生代码要求 xpath 前面必须加`.`,否则会变成在全个页面中查找。作者觉得这个设计是画蛇添足,既然已经通过元素查找了,自然应该只查找这个元素内部的元素。所以,用
xpath 在元素下查找时,最前面`//``/`前面的`.`可以省略。
---
## 📍 selenium 的 loc 元组
查找方法能直接接收 selenium 原生定位元组进行查找。
```python
from selenium.webdriver.common.by import By
# 查找id为one的元素
loc1 = (By.ID, 'one')
ele = page.ele(loc1)
# 按 xpath 查找
loc2 = (By.XPATH, '//p[@class="p_cls"]')
ele = page.ele(loc2)
```
---
# ✔️ 相对定位
以下方法可以以某元素为基准,在 DOM 中按照条件获取其兄弟元素、祖先元素、文档前后元素。
除获取元素外,还能通过 xpath 获取任意节点内容,如文本节点、注释节点。这在处理元素和文本节点混排的时候非常有用。
!>**注意:**<br>如果元素在`<iframe>`中,相对定位不能超越`<iframe>`文档。
## 📍 获取父级元素
🔸 `parent()`
此方法获取当前元素某一级父元素,可指定筛选条件或层数。
| 参数名称 | 类型 | 默认值 | 说明 |
|:--------------:|:-----------------------------------:|:---:| --------------------- |
| `level_or_loc` | `int`<br>`str`<br>`Tuple[str, str]` | `1` | 第几级父元素,或定位符用于在祖先元素中查找 |
| 返回类型 | 说明 |
|:----------------:| --------------- |
| `SessionElement` | 获取到的元素对象 |
| `None` | 未获取到结果时返回`None` |
**示例:**
```python
# 获取 ele1 的第二层父元素
ele2 = ele1.parent(2)
# 获取 ele1 父元素中 id 为 id1 的元素
ele2 = ele1.parent('#id1')
```
---
## 📍 获取后面的兄弟元素
🔸 `next()`
此方法返回当前元素后面的某一个同级元素,可指定筛选条件和第几个。
| 参数名称 | 类型 | 默认值 | 说明 |
|:------------:|:--------------------------:|:------:| ----------- |
| `filter_loc` | `str`<br>`Tuple[str, str]` | `''` | 用于筛选元素的查询语法 |
| `index` | `int` | `1` | 查询结果中的第几个 |
| `timeout` | `int`<br>`float` | `None` | 无实际作用 |
| 返回类型 | 说明 |
|:----------------:| --------------- |
| `SessionElement` | 获取到的元素对象 |
| `None` | 未获取到结果时返回`None` |
**示例:**
```python
# 获取 ele1 后面第一个兄弟元素
ele2 = ele1.next()
# 获取 ele1 后面第 3 个兄弟元素
ele2 = ele1.next(3)
# 获取 ele1 后面第 3 个 div 兄弟元素
ele2 = ele1.next('tag:div', 3)
# 获取 ele1 后面第一个文本节点的文本
txt = ele1.next('xpath:text()', 1)
```
---
🔸 `nexts()`
此方法返回当前元素后面全部符合条件的同级元素或节点组成的列表,可用查询语法筛选。
| 参数名称 | 类型 | 默认值 | 说明 |
|:------------:|:--------------------------:|:------:| ----------- |
| `filter_loc` | `str`<br>`Tuple[str, str]` | `''` | 用于筛选元素的查询语法 |
| `timeout` | `int`<br>`float` | `None` | 无实际作用 |
| 返回类型 | 说明 |
|:----------------------:| -------- |
| `List[SessionElement]` | 获取到的元素对象 |
**示例:**
```python
# 获取 ele1 后面所有兄弟元素
eles = ele1.nexts()
# 获取 ele1 后面所有 div 兄弟元素
divs = ele1.nexts('tag:div')
# 获取 ele1 后面的所有文本节点
txts = ele1.nexts('xpath:text()')
```
---
## 📍 获取前面的兄弟元素
🔸 `prev()`
此方法返回当前元素前面的某一个同级元素,可指定筛选条件和第几个。
| 参数名称 | 类型 | 默认值 | 说明 |
|:------------:|:--------------------------:|:------:| ----------- |
| `filter_loc` | `str`<br>`Tuple[str, str]` | `''` | 用于筛选元素的查询语法 |
| `index` | `int` | `1` | 查询结果中的第几个 |
| `timeout` | `int`<br>`float` | `None` | 无实际作用 |
| 返回类型 | 说明 |
|:----------------:| --------------- |
| `SessionElement` | 获取到的元素对象 |
| `None` | 未获取到结果时返回`None` |
**示例:**
```python
# 获取 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()')
```
---
🔸 `prevs()`
此方法返回当前元素前面全部符合条件的同级元素或节点组成的列表,可用查询语法筛选。
| 参数名称 | 类型 | 默认值 | 说明 |
|:------------:|:--------------------------:|:------:| ----------- |
| `filter_loc` | `str`<br>`Tuple[str, str]` | `''` | 用于筛选元素的查询语法 |
| `timeout` | `int`<br>`float` | `None` | 无实际作用 |
| 返回类型 | 说明 |
|:----------------------:| -------- |
| `List[SessionElement]` | 获取到的元素对象 |
**示例:**
```python
# 获取 ele1 前面所有兄弟元素
eles = ele1.prevs()
# 获取 ele1 前面所有 div 兄弟元素
divs = ele1.prevs('tag:div')
```
---
## 📍 在后面文档中查找元素
🔸 `after()`
此方法返回当前元素后面的某一个元素,可指定筛选条件和第几个。这个方法查找范围不局限在兄弟元素间,而是整个 DOM 文档。
| 参数名称 | 类型 | 默认值 | 说明 |
|:------------:|:--------------------------:|:------:| ----------- |
| `filter_loc` | `str`<br>`Tuple[str, str]` | `''` | 用于筛选元素的查询语法 |
| `index` | `int` | `1` | 查询结果中的第几个 |
| `timeout` | `int`<br>`float` | `None` | 无实际作用 |
| 返回类型 | 说明 |
|:----------------:| --------------- |
| `SessionElement` | 获取到的元素对象 |
| `None` | 未获取到结果时返回`None` |
**示例:**
```python
# 获取 ele1 后面第 3 个元素
ele2 = ele1.after(3)
# 获取 ele1 后面第 3 个 div 元素
ele2 = ele1.after('tag:div', 3)
# 获取 ele1 后面第一个文本节点的文本
txt = ele1.after('xpath:text()', 1)
```
---
🔸 `afters()`
此方法返回当前元素后面符合条件的全部元素或节点组成的列表,可用查询语法筛选。这个方法查找范围不局限在兄弟元素间,而是整个 DOM 文档。
| 参数名称 | 类型 | 默认值 | 说明 |
|:------------:|:--------------------------:|:------:| ----------- |
| `filter_loc` | `str`<br>`Tuple[str, str]` | `''` | 用于筛选元素的查询语法 |
| `timeout` | `int`<br>`float` | `None` | 无实际作用 |
| 返回类型 | 说明 |
|:----------------------:| -------- |
| `List[SessionElement]` | 获取到的元素对象 |
**示例:**
```python
# 获取 ele1 后所有元素
eles = ele1.afters()
# 获取 ele1 前面所有 div 元素
divs = ele1.afters('tag:div')
```
---
## 📍 在前面文档中查找元素
🔸 `before()`
此方法返回当前元素前面的某一个元素,可指定筛选条件和第几个。这个方法查找范围不局限在兄弟元素间,而是整个 DOM 文档。
| 参数名称 | 类型 | 默认值 | 说明 |
|:------------:|:--------------------------:|:------:| ----------- |
| `filter_loc` | `str`<br>`Tuple[str, str]` | `''` | 用于筛选元素的查询语法 |
| `index` | `int` | `1` | 查询结果中的第几个 |
| `timeout` | `int`<br>`float` | `None` | 无实际作用 |
| 返回类型 | 说明 |
|:----------------:| --------------- |
| `SessionElement` | 获取到的元素对象 |
| `None` | 未获取到结果时返回`None` |
**示例:**
```python
# 获取 ele1 前面第 3 个元素
ele2 = ele1.before(3)
# 获取 ele1 前面第 3 个 div 元素
ele2 = ele1.before('tag:div', 3)
# 获取 ele1 前面第一个文本节点的文本
txt = ele1.before('xpath:text()', 1)
```
---
🔸 `befores()`
此方法返回当前元素前面全部符合条件的元素或节点组成的列表,可用查询语法筛选。这个方法查找范围不局限在兄弟元素间,而是整个 DOM 文档。
| 参数名称 | 类型 | 默认值 | 说明 |
|:------------:|:--------------------------:|:------:| ----------- |
| `filter_loc` | `str`<br>`Tuple[str, str]` | `''` | 用于筛选元素的查询语法 |
| `timeout` | `int`<br>`float` | `None` | 无实际作用 |
| 返回类型 | 说明 |
|:----------------------:| -------- |
| `List[SessionElement]` | 获取到的元素对象 |
**示例:**
```python
# 获取 ele1 前面所有元素
eles = ele1.befores()
# 获取 ele1 前面所有 div 元素
divs = ele1.befores('tag:div')
```
---
# ✔️ 简化写法
为进一步精简代码,对语法进行了精简
- 定位语法都有其简化形式。
- 页面和元素对象都实现了`__call__()`方法,可直接调用。
- 所有查找方法都支持链式操作
示例:
```python
# 定位到页面中 id 为 table_id 的元素,然后获取它的所有 tr 元素
eles = page('#table_id').eles('t:tr')
# 定位到 class 为 cls 的元素,然后在它里面查找文本为 text 的元素
ele2 = ele1('.cls1')('tx=text')
# 获取 ele1 的 shadow_root 元素,再在里面查找 class 属性为 cls 的元素
ele2 = ele1.sr('.cls')
# 按xpath 查找元素
ele2 = ele1('x://div[@class="ele_class"]')
```
简化写法对应列表
| 原写法 | 简化写法 |
|:--------:|:------:|
| `text` | `tx` |
| `text()` | `tx()` |
| `tag` | `t` |
| `xpath` | `x` |
| `css` | `c` |

View File

@ -1,306 +0,0 @@
`SessionPage`对象和`WebPage` 对象s 模式获取的元素是`SessionElement`,本节介绍其属性。
假设`ele`为以下`div`元素的对象,本节示例均使用该元素:
```html
<div id="div1" class="divs">Hello World!
<p>行元素</p>
<!--这是注释-->
</div>
```
# ✔️ `html`
此属性返回元素的`outerHTML`文本。
**返回类型:**`str`
```python
print(ele.html)
```
**输出:**
```console
<div id="div1" class="divs">Hello World!
<p>行元素</p>
<!--这是注释-->
</div>
```
---
# ✔️ `inner_html`
此属性返回元素的`innerHTML`文本。
**返回类型:**`str`
```python
print(ele.inner_html)
```
**输出:**
```console
Hello World!
<p>行元素</p>
<!--这是注释-->
```
---
# ✔️ `tag`
此属性返回元素的标签名。
**返回类型:**`str`
```python
print(ele.tag)
```
**输出:**
```console
div
```
---
# ✔️ `text`
此属性返回元素内所有文本组合成的字符串。
该字符串已格式化,即已转码,已去除多余换行符,符合人读取习惯,便于直接使用。
**返回类型:**`str`
```python
print(ele.text)
```
**输出:**
```console
Hello World!
行元素
```
---
# ✔️ `raw_text`
此属性返回元素内原始文本。
**返回类型:**`str`
```python
print(ele.raw_text)
```
输出(注意保留了元素间的空格和换行):
```console
Hello World!
行元素
 
 
```
---
# ✔️ `texts()`
此方法返回元素内所有**直接**子节点的文本,包括元素和文本节点。 它有一个参数`text_node_only`,为`True`时则只获取只返回不被包裹的文本节点。这个方法适用于获取文本节点和元素节点混排的情况。
| 参数名称 | 类型 | 默认值 | 说明 |
|:----------------:|:------:|:-------:| --------- |
| `text_node_only` | `bool` | `False` | 是否只返回文本节点 |
| 返回类型 | 说明 |
|:-----------:| ---- |
| `List[str]` | 文本列表 |
**示例:**
```python
print(e.tests())
print(e.texts(text_node_only=True))
```
**输出:**
```console
['Hello World!', '行元素']
['Hello World!']
```
---
# ✔️ `comments`
此属性以列表形式返回元素内的注释。
**返回类型:**`List[str]`
```python
print(ele.comments)
```
**输出:**
```console
[<!--这是注释-->]
```
---
# ✔️ `attrs`
此属性以字典形式返回元素所有属性及值。
**返回类型:**`dict`
```python
print(ele.attrs)
```
**输出:**
```console
{'id': 'div1', 'class': 'divs'}
```
---
# ✔️ `attr()`
此方法返回元素某个`attribute`属性值。它接收一个字符串参数`attr`,返回该属性值文本,无该属性时返回`None`
此属性返回的`src``href`属性为已补充完整的路径。`text`属性为已格式化文本。
**返回类型:**`str``None`
| 参数名称 | 类型 | 默认值 | 说明 |
|:------:|:-----:|:---:| ---- |
| `attr` | `str` | 必填 | 属性名称 |
| 返回类型 | 说明 |
|:------:| ------------- |
| `str` | 属性值文本 |
| `None` | 没有该属性返回`None` |
**示例:**
```python
print(ele.attr('id'))
```
**输出:**
```console
div1
```
---
# ✔️ `link`
此方法返回元素的 href 属性或 src 属性,没有这两个属性则返回`None`
**返回类型:**`str`
```html
<a href='http://www.baidu.com'>百度</a>
```
假设`a_ele`为以上元素的对象:
```python
print(a_ele.link)
```
**输出:**
```console
http://www.baidu.com
```
---
# ✔️ `page`
此属性返回元素所在的页面对象。由 html 文本直接生成的`SessionElement``page`属性为`None`
**返回类型:**`SessionPage``WebPage`
```python
page = ele.page
```
---
# ✔️ `xpath`
此属性返回当前元素在页面中 xpath 的绝对路径。
**返回类型:**`str`
```python
print(ele.xpath)
```
**输出:**
```console
/html/body/div
```
---
# ✔️ `css_path`
此属性返回当前元素在页面中 css selector 的绝对路径。
**返回类型:**`str`
```python
print(ele.css_path)
```
**输出:**
```console
:nth-child(1)>:nth-child(1)>:nth-child(1)
```
---
# ✔️ 实际示例
以下示例可直接运行查看结果:
```python
from DrissionPage import SessionPage
page = SessionPage('s')
page.get('https://gitee.com/explore')
# 获取推荐目录下所有 a 元素
li_eles = page('tag:ul@@text():全部推荐项目').eles('t:a')
# 遍历列表
for i in li_eles:
# 获取并打印标签名、文本、href 属性
print(i.tag, i.text, i.attr('href'))
```
**输出:**
```console
a 全部推荐项目 https://gitee.com/explore/all
a 前沿技术 https://gitee.com/explore/new-tech
a 智能硬件 https://gitee.com/explore/hardware
a IOT/物联网/边缘计算 https://gitee.com/explore/iot
a 车载应用 https://gitee.com/explore/vehicle
以下省略……
```

View File

@ -1,485 +0,0 @@
本节介绍`SessionPage`的启动配置。
我们用`SessionOptions`对象管理`Session`对象初始配置。
!> **注意:** <br>`SessionOptions`仅用于管理启动配置,程序启动后再修改无效。
# ✔️ 创建对象
## 📍 导入
```python
from DrissionPage import SessionOptions
```
---
## 📍 `SessionOptions`
`SessionOptions`对象用于管理`Session`对象的初始化配置。可从配置文件中读取配置来进行初始化。
| 初始化参数 | 类型 | 默认值 | 说明 |
|:-----------:|:---------------:|:------:| ---------------------------------- |
| `read_file` | `bool` | `True` | 是否从 ini 文件中读取配置信息,为`False`则用默认配置创建 |
| `ini_path` | `Path`<br>`str` | `None` | 指定 ini 文件路径,为`None`则读取内置 ini 文件 |
创建配置对象:
```python
from DrissionPage import SessionOptions
so = SessionOptions()
```
默认情况下,`SessionOptions`对象会从 ini 文件中读取配置信息,当指定`read_file`参数为`False`时,则以默认配置创建。
---
# ✔️ 使用方法
创建配置对象后,可调整配置内容,然后在页面对象创建时以参数形式把配置对象传递进去。
```python
from DrissionPage import SessionPage, SessionOptions
# 创建配置对象(默认从 ini 文件中读取配置)
so = SessionOptions()
# 设置代理
so.set_proxies('http://localhost:1080')
# 设置 cookies
cookies = ['key1=val1; domain=xxxx', 'key2=val2; domain=xxxx']
so.set_cookies(cookies)
# 以该配置创建页面对象
page = SessionPage(session_or_options=so)
```
---
# ✔️ 用于设置的方法
## 📍 `set_headers()`
该方法用于设置整个 headers 参数,传入值会覆盖原来的 headers。
| 参数名称 | 类型 | 默认值 | 说明 |
|:--------:|:------:|:---:| -------------- |
| `headers | `dict` | 必填 | 完整的 headers 字典 |
| 返回类型 | 说明 |
| ---------------- | ------ |
| `SessionOptions` | 配置对象本身 |
**示例:**
```python
so.set_headers = {'user-agent': 'Mozilla/5.0 (Macint...', 'connection': 'keep-alive' ...}
```
---
## 📍 `set_a_header()`
该方法用于设置`headers`中的一个项。
| 参数名称 | 类型 | 默认值 | 说明 |
|:-------:|:-----:|:---:| ---- |
| `attr` | `str` | 必填 | 设置名称 |
| `value` | `str` | 必填 | 设置值 |
| 返回类型 | 说明 |
| ---------------- | ------ |
| `SessionOptions` | 配置对象本身 |
**示例:**
```python
so.set_a_header('accept', 'text/html')
so.set_a_header('Accept-Charset', 'GB2312')
```
**输出:**
```
{'accept': 'text/html', 'accept-charset': 'GB2312'}
```
---
## 📍 `remove_a_header()`
此方法用于从`headers`中移除一个设置项。
| 参数名称 | 类型 | 默认值 | 说明 |
|:------:|:-----:|:---:| ------ |
| `attr` | `str` | 必填 | 要删除的设置 |
| 返回类型 | 说明 |
| ---------------- | ------ |
| `SessionOptions` | 配置对象本身 |
**示例:**
```python
so.remove_a_header('accept')
```
---
## 📍 `set_cookies()`
此方法用于设置 cookies 信息,每次设置会覆盖之前所有 cookies 信息。
| 参数名称 | 类型 | 默认值 | 说明 |
|:---------:|:-----------------------------------------------------------:|:---:| ------- |
| `cookies` | `RequestsCookieJar`<br>`list`<br>`tuple`<br>`str`<br>`dict` | 必填 | cookies |
| 返回类型 | 说明 |
| ---------------- | ------ |
| `SessionOptions` | 配置对象本身 |
**示例:**
```python
cookies = ['key1=val1; domain=xxxx', 'key2=val2; domain=xxxx']
so.set_cookies(cookies)
```
---
## 📍 `set_timeout()`
此方法用于设置连接超时属性。
| 参数名称 | 类型 | 默认值 | 说明 |
|:--------:|:----------------:|:---:| ------ |
| `second` | `int`<br>`float` | 必填 | 连接等待秒数 |
| 返回类型 | 说明 |
| ---------------- | ------ |
| `SessionOptions` | 配置对象本身 |
---
## 📍 `set_proxies()`
此方法用于设置代理信息。
| 参数名称 | 类型 | 默认值 | 说明 |
|:-------:|:-----:|:------:| ------------------------------- |
| `http` | `str` | 必填 | http 代理地址 |
| `https` | `str` | `None` | https 代理地址,为`None`时使用`http`参数的值 |
| 返回类型 | 说明 |
| ---------------- | ------ |
| `SessionOptions` | 配置对象本身 |
**示例:**
```python
so.set_proxies('http://127.0.0.1:1080')
```
---
## 📍 `set_paths()`
此方法用于设置路径信息,目前只有`download_path`一种。
| 参数名称 | 类型 | 默认值 | 说明 |
|:---------------:|:---------------:|:---:| -------- |
| `download_path` | `str`<br>`Path` | 必填 | 默认下载保存路径 |
| 返回类型 | 说明 |
| ---------------- | ------ |
| `SessionOptions` | 配置对象本身 |
---
## 📍 `set_auth()`
此方法用于设置认证元组信息。
| 参数名称 | 类型 | 默认值 | 说明 |
|:------:|:--------------------------:|:---:| ------- |
| `auth` | `tuple`<br>`HTTPBasicAuth` | 必填 | 认证元组或对象 |
| 返回类型 | 说明 |
| ---------------- | ------ |
| `SessionOptions` | 配置对象本身 |
---
## 📍 `set_hooks()`
此方法用于设置回调方法。
| 参数名称 | 类型 | 默认值 | 说明 |
|:-------:|:------:|:---:| ---- |
| `hooks` | `dict` | 必填 | 回调方法 |
| 返回类型 | 说明 |
| ---------------- | ------ |
| `SessionOptions` | 配置对象本身 |
---
## 📍 `set_params()`
此方法用于设置查询参数。
| 参数名称 | 类型 | 默认值 | 说明 |
|:--------:|:------:|:---:| ------ |
| `params` | `dict` | 必填 | 查询参数字典 |
| 返回类型 | 说明 |
| ---------------- | ------ |
| `SessionOptions` | 配置对象本身 |
---
## 📍 `set_cert()`
此方法用于设置 SSL 客户端证书文件的路径(.pem格式或 ('cert', 'key') 元组。
| 参数名称 | 类型 | 默认值 | 说明 |
|:------:|:----------------:|:---:| ------- |
| `cert` | `str`<br>`tuple` | 必填 | 证书路径或元组 |
| 返回类型 | 说明 |
| ---------------- | ------ |
| `SessionOptions` | 配置对象本身 |
---
## 📍 `set_verify()`
此方法用于设置是否验证SSL证书。
| 参数名称 | 类型 | 默认值 | 说明 |
|:--------:|:------:|:---:| ----------- |
| `on_off` | `bool` | 必填 | `bool`表示开或关 |
| 返回类型 | 说明 |
| ---------------- | ------ |
| `SessionOptions` | 配置对象本身 |
---
## 📍 `add_adapter()`
此方法用于添加适配器。
| 参数名称 | 类型 | 默认值 | 说明 |
|:---------:|:-------------:|:---:| --------- |
| `url` | `str` | 必填 | 适配器对应 url |
| `adapter` | `HTTPAdapter` | 必填 | 适配器对象 |
| 返回类型 | 说明 |
| ---------------- | ------ |
| `SessionOptions` | 配置对象本身 |
---
## 📍 `set_stream()`
此方法用于设置是否使用流式响应内容。
| 参数名称 | 类型 | 默认值 | 说明 |
|:--------:|:------:|:---:| ----------- |
| `on_off` | `bool` | 必填 | `bool`表示开或关 |
| 返回类型 | 说明 |
| ---------------- | ------ |
| `SessionOptions` | 配置对象本身 |
---
## 📍 `set_trust_env()`
此方法用于设置是否信任环境。
| 参数名称 | 类型 | 默认值 | 说明 |
|:--------:|:------:|:---:| ----------- |
| `on_off` | `bool` | 必填 | `bool`表示开或关 |
| 返回类型 | 说明 |
| ---------------- | ------ |
| `SessionOptions` | 配置对象本身 |
---
## 📍 `set_max_redirects()`
此方法用于设置最大重定向次数。
| 参数名称 | 类型 | 默认值 | 说明 |
|:-------:|:-----:|:---:| ------- |
| `times` | `int` | 必填 | 最大重定向次数 |
| 返回类型 | 说明 |
| ---------------- | ------ |
| `SessionOptions` | 配置对象本身 |
---
# ✔️ 保存设置到文件
您可以把不同的配置保存到各自的 ini 文件,以便适应不同的场景。
!>**注意:**<br>`hooks``adapters`配置是不会保存到文件中的。
## 📍 `save()`
此方法用于保存配置项到一个 ini 文件。
| 参数名称 | 类型 | 默认值 | 说明 |
|:------:|:---------------:|:------:| ------------------------------- |
| `path` | `str`<br>`Path` | `None` | ini 文件的路径, 传入`None`保存到当前读取的配置文件 |
| 返回类型 | 说明 |
| ----- | -------------- |
| `str` | 保存的 ini 文件绝对路径 |
**示例:**
```python
# 保存当前读取的ini文件
so.save()
# 把当前配置保存到指定的路径
so.save(path=r'D:\tmp\settings.ini')
```
---
## 📍 `save_to_default()`
此方法用于保存配置项到固定的默认 ini 文件。默认 ini 文件是指随 DrissionPage 内置的那个。
**参数:** 无
| 返回类型 | 说明 |
| ----- | -------------- |
| `str` | 保存的 ini 文件绝对路径 |
**示例:**
```python
so.save_to_default()
```
---
# ✔️ `SessionOptions`属性
## 📍 `headers`
该属性返回 headers 设置信息。
**类型:**`dict`
---
## 📍 `cookies`
此属性以`list`方式返回 cookies 设置信息。
**类型:**`list`
---
## 📍 `proxies`
此属性返回代理信息。
**类型:**`dict`
**格式:**{'http': 'http://xx.xx.xx.xx:xxxx', 'https': 'http://xx.xx.xx.xx:xxxx'}
---
## 📍 `auth`
此属性返回认证设置。
**类型:**`tuple``HTTPBasicAuth`
---
## 📍 `hooks`
此属性返回回调方法设置。
**类型:**`dict`
---
## 📍 `params`
此属性返回查询参数设置。
**类型:**`dict`
---
## 📍 `verify`
此属性返回是否验证 SSL 证书设置。
**类型:**`bool`
---
## 📍 `cert`
此属性返回 SSL 证书设置。
**类型:**`str``tuple`
---
## 📍 `adapters`
此属性返回适配器设置。
**类型:**`List[HTTPAdapter]`
---
## 📍 `stream`
此属性返回是否使用流式响应设置。
**类型:**`bool`
---
## 📍 `trust_env`
此属性返回是否信任环境设置。
**类型:**`bool`
---
## 📍 `max_redirects`
此属性返回`max_redirects`设置。
**类型:**`int`
---
## 📍 `timeout`
此属性返回连接超时设置。
**类型:**`int``float`
---
## 📍 `download_path`
此属性返回默认下载路径设置。
**类型:**`str`

View File

@ -1,44 +0,0 @@
`ChromiumPage`对象和`WebPage`对象的 d 模式,可操控浏览器。本章介绍`ChromiumPage`
顾名思义,`ChromiumPage`是 Chromium 内核浏览器的页面,它用 POM 方式封装了操控网页所需的属性和方法。
使用它,我们可与网页进行交互,如调整窗口大小、滚动页面、操作弹出框等等。
通过从中获取的元素对象,我们还可以跟页面中的元素进行交互,如输入文字、点击按钮、选择下拉菜单等等。
甚至,我们可以在页面或元素上运行 JavaScript 代码、修改元素属性、增删元素等。
可以说,操控浏览器的绝大部分操作,都可以由`ChromiumPage`及其衍生的对象完成,而它们的功能,还在不断增加。
除了与页面和元素的交互,`ChromiumPage`还扮演着浏览器控制器的角色,可以说,一个`ChromiumPage`对象,就是一个浏览器。
它可以对标签页进行管理,可以对下载任务进行控制。可以为每个标签页生成独立的页面对象(`ChromiumTab`),以实现多标签页同时操作,而无须切入切出。
随着 3.0 版本脱离对 WebDriver 的依赖,作者终于可以放飞自我,为`ChromiumPage`添加各种各样有意思的功能,我们以后会越做越好。
我们看个简单的例子,来了解`CromiumPage`的工作方式。
---
在百度搜搜“Drissionpage”并打印结果。
```python
# 导入
from DrissionPage import ChromiumPage
# 创建对象
page = ChromiumPage()
# 访问网页
page.get('https://www.baidu.com')
# 输入文本
page('#kw').input('DrissionPage')
# 点击按钮
page('#su').click()
# 等待页面跳转
page.wait_loading()
# 获取所有结果
links = page.eles('tag:h3')
# 遍历并打印结果
for link in links:
print(link.text)
```

View File

@ -1,496 +0,0 @@
动作链可以在浏览器上完成一系列交互行为,如鼠标移动、鼠标点击、键盘输入等。
动作链支持`ChromiumPage``WebPage``ChromiumTab`对象。
可以链式操作,也可以分开执行,每个动作执行即生效,无须`perform()`
多个动作可以用链式模式操作:
```python
ac.move_to(ele).click().type('some text')
```
也可以多个操作分开执行:
```python
ac.move_to(ele)
ac.click()
ac.type('some text')
```
这两种方式效果是一样的,每个动作总会依次执行。
# ✔ 创建对象
## 📍 导入
```python
from DrissionPage import ActionChains
```
---
## 📍 `ActionChains`
创建动作链对象非常简单,只要把`WebPage`对象或`ChromiumPage`对象传入即可。动作链只在这个页面上生效。
| 初始化参数 | 类型 | 默认值 | 说明 |
|:------:|:--------------------------------------------:|:---:| ------------ |
| `page` | `ChromiumPage`<br>`WebPage`<br>`ChromiumTab` | 必填 | 动作链要操作的浏览器页面 |
```python
from DrissionPage import ChromiumPage, ActionChains
page = WebPage()
ac = ActionChains(page)
```
---
# ✔ 移动鼠标
## 📍 `move_to()`
此方法用于移动鼠标到元素中点,或页面上的某个绝对坐标。可设置偏移量,当带偏移量时,偏移量相对于元素左上角坐标。
| 初始化参数 | 类型 | 默认值 | 说明 |
|:------------:|:-----------------------------------------------:|:---:| --------------------------------------- |
| `ele_or_loc` | `ChrmoiumElement`<br>`str`<br>`Tuple[int, int]` | 必填 | 元素对象、文本定位符或绝对坐标,坐标为`tuple`(int, int) 形式 |
| `offset_x` | `int` | 0 | x 轴偏移量,向右为正,向左为负 |
| `offset_y` | `int` | 0 | y 轴偏移量,向下为正,向上为负 |
| 返回类型 | 说明 |
|:--------------:| ------- |
| `ActionChains` | 动作链对象本身 |
**示例:** 使鼠标移动到 ele 元素上
```python
ele = page('tag:a')
ac.move_to(ele_or_loc=ele)
```
---
## 📍 `move()`
此方法用于使鼠标相对当前位置移动若干距离。
| 参数名称 | 类型 | 默认值 | 说明 |
|:----------:|:-----:|:---:| ---------------- |
| `offset_x` | `int` | 0 | x 轴偏移量,向右为正,向左为负 |
| `offset_y` | `int` | 0 | y 轴偏移量,向下为正,向上为负 |
| 返回类型 | 说明 |
|:--------------:| ------- |
| `ActionChains` | 动作链对象本身 |
**示例:** 鼠标向右移动 300 像素
```python
ac.move(300, 0)
```
---
## 📍 `up()`
此方法用于使鼠标相对当前位置向上移动若干距离。
| 参数名称 | 类型 | 默认值 | 说明 |
|:-------:|:-----:|:---:| -------- |
| `pixel` | `int` | 必填 | 鼠标移动的像素值 |
| 返回类型 | 说明 |
|:--------------:| ------- |
| `ActionChains` | 动作链对象本身 |
**示例:** 鼠标向上移动 50 像素
```python
ac.up(50)
```
---
## 📍 `down()`
此方法用于使鼠标相对当前位置向下移动若干距离。
| 参数名称 | 类型 | 默认值 | 说明 |
|:-------:|:-----:|:---:| -------- |
| `pixel` | `int` | 必填 | 鼠标移动的像素值 |
| 返回类型 | 说明 |
|:--------------:| ------- |
| `ActionChains` | 动作链对象本身 |
**示例:**
```python
ac.down(50)
```
---
## 📍 `left()`
此方法用于使鼠标相对当前位置向左移动若干距离。
| 参数名称 | 类型 | 默认值 | 说明 |
|:-------:|:-----:|:---:| -------- |
| `pixel` | `int` | 必填 | 鼠标移动的像素值 |
| 返回类型 | 说明 |
|:--------------:| ------- |
| `ActionChains` | 动作链对象本身 |
**示例:**
```python
ac.left(50)
```
---
## 📍 `right()`
此方法用于使鼠标相对当前位置向右移动若干距离。
| 参数名称 | 类型 | 默认值 | 说明 |
|:-------:|:-----:|:---:| -------- |
| `pixel` | `int` | 必填 | 鼠标移动的像素值 |
| 返回类型 | 说明 |
|:--------------:| ------- |
| `ActionChains` | 动作链对象本身 |
**示例:**
```python
ac.right(50)
```
---
# ✔ 鼠标按键
## 📍 `click()`
此方法用于单击鼠标左键,单击前可先移动到元素上。
| 参数名称 | 类型 | 默认值 | 说明 |
|:--------:|:--------------------------:|:------:| -------------- |
| `on_ele` | `ChromiumElement`<br>`str` | `None` | 要点击的元素对象或文本定位符 |
| 返回类型 | 说明 |
|:--------------:| ------- |
| `ActionChains` | 动作链对象本身 |
**示例:**
```python
ac.click('#div1')
```
---
## 📍 `r_click()`
此方法用于单击鼠标右键,单击前可先移动到元素上。
| 参数名称 | 类型 | 默认值 | 说明 |
|:--------:|:--------------------------:|:------:| -------------- |
| `on_ele` | `ChromiumElement`<br>`str` | `None` | 要点击的元素对象或文本定位符 |
| 返回类型 | 说明 |
|:--------------:| ------- |
| `ActionChains` | 动作链对象本身 |
**示例:**
```python
ac.r_click('#div1')
```
---
## 📍 `m_click()`
此方法用于单击鼠标中键,单击前可先移动到元素上。
| 参数名称 | 类型 | 默认值 | 说明 |
|:--------:|:--------------------------:|:------:| -------------- |
| `on_ele` | `ChromiumElement`<br>`str` | `None` | 要点击的元素对象或文本定位符 |
| 返回类型 | 说明 |
|:--------------:| ------- |
| `ActionChains` | 动作链对象本身 |
**示例:**
```python
ac.m_click('#div1')
```
---
## 📍 `hold()`
此方法用于按住鼠标左键不放,按住前可先移动到元素上。
| 参数名称 | 类型 | 默认值 | 说明 |
|:--------:|:--------------------------:|:------:| -------------- |
| `on_ele` | `ChromiumElement`<br>`str` | `None` | 要按住的元素对象或文本定位符 |
| 返回类型 | 说明 |
|:--------------:| ------- |
| `ActionChains` | 动作链对象本身 |
**示例:**
```python
ac.hold('#div1')
```
---
## 📍 `release()`
此方法用于释放鼠标左键,释放前可先移动到元素上。
| 参数名称 | 类型 | 默认值 | 说明 |
|:--------:|:--------------------------:|:------:| -------------- |
| `on_ele` | `ChromiumElement`<br>`str` | `None` | 要释放的元素对象或文本定位符 |
| 返回类型 | 说明 |
|:--------------:| ------- |
| `ActionChains` | 动作链对象本身 |
**示例:** 移动到某元素上然后释放鼠标左键
```python
ac.release('#div1')
```
---
## 📍 `r_hold()`
此方法用于按住鼠标右键不放,按住前可先移动到元素上。
| 参数名称 | 类型 | 默认值 | 说明 |
|:--------:|:--------------------------:|:------:| -------------- |
| `on_ele` | `ChromiumElement`<br>`str` | `None` | 要按住的元素对象或文本定位符 |
| 返回类型 | 说明 |
|:--------------:| ------- |
| `ActionChains` | 动作链对象本身 |
---
## 📍 `r_release()`
此方法用于释放鼠标右键,释放前可先移动到元素上。
| 参数名称 | 类型 | 默认值 | 说明 |
|:--------:|:--------------------------:|:------:| -------------- |
| `on_ele` | `ChromiumElement`<br>`str` | `None` | 要释放的元素对象或文本定位符 |
| 返回类型 | 说明 |
|:--------------:| ------- |
| `ActionChains` | 动作链对象本身 |
---
## 📍 `m_hold()`
此方法用于按住鼠标中键不放,按住前可先移动到元素上。
| 参数名称 | 类型 | 默认值 | 说明 |
|:--------:|:--------------------------:|:------:| -------------- |
| `on_ele` | `ChromiumElement`<br>`str` | `None` | 要按住的元素对象或文本定位符 |
| 返回类型 | 说明 |
|:--------------:| ------- |
| `ActionChains` | 动作链对象本身 |
---
## 📍 `m_release()`
此方法用于释放鼠标中键,释放前可先移动到元素上。
| 参数名称 | 类型 | 默认值 | 说明 |
|:--------:|:--------------------------:|:------:| -------------- |
| `on_ele` | `ChromiumElement`<br>`str` | `None` | 要释放的元素对象或文本定位符 |
| 类型 | 说明 |
|:--------------:| ------- |
| `ActionChains` | 动作链对象本身 |
---
# ✔ 滚动滚轮
## 📍 `scroll()`
此方法用于滚动鼠标滚轮,滚动前可先移动到元素上。
| 参数名称 | 类型 | 默认值 | 说明 |
|:---------:|:--------------------------:|:------:| ------------------- |
| `delta_x` | `int` | 0 | 滚轮 x 轴变化值,向右为正,向左为负 |
| `delta_y` | `str` | 0 | 滚轮 y 轴变化值,向下为正,向上为负 |
| `on_ele` | `ChromiumElement`<br>`str` | `None` | 要滚动的元素对象或文本定位符 |
| 返回类型 | 说明 |
|:--------------:| ------- |
| `ActionChains` | 动作链对象本身 |
---
# ✔ 键盘按键
## 📍 `key_down()`
此方法用于按下键盘按键,特殊字符见 Keys。
| 参数名称 | 类型 | 默认值 | 说明 |
|:-----:|:-----:|:---:| ---- |
| `key` | `str` | 必填 | 按键键值 |
| 返回类型 | 说明 |
|:--------------:| ------- |
| `ActionChains` | 动作链对象本身 |
**示例:** 按下 ctrl 键
```python
from DrissionPage.keys import Keys
ac.key_down(Keys.CTRL)
```
---
## 📍 `key_up()`
此方法用于提起键盘按键,特殊字符见 Keys。
| 参数名称 | 类型 | 默认值 | 说明 |
|:-----:|:-----:|:---:| ---- |
| `key` | `str` | 必填 | 按键键值 |
| 返回类型 | 说明 |
|:--------------:| ------- |
| `ActionChains` | 动作链对象本身 |
**示例:** 提起 ctrl 键
```python
from DrissionPage.keys import Keys
ac.key_up(Keys.CTRL)
```
---
## 📍 `type()`
此方法用于输入一段文本。
| 参数名称 | 类型 | 默认值 | 说明 |
|:------:|:-----:|:---:| ------ |
| `text` | `str` | 必填 | 要输入的文本 |
| 返回类型 | 说明 |
|:--------------:| ------- |
| `ActionChains` | 动作链对象本身 |
**示例:**
```python
ac.type('text')
```
---
# ✔ 等待
## 📍 `wait()`
此方法用于在动作链中插入停顿。
| 参数名称 | 类型 | 默认值 | 说明 |
|:--------:|:-------:|:---:| ---- |
| `second` | `float` | 必填 | 等待秒数 |
| 返回类型 | 说明 |
|:--------------:| ------- |
| `ActionChains` | 动作链对象本身 |
**示例:** 停顿 3 秒
```python
ac.wait(3)
```
---
# ✔ 示例
## 📍 模拟输入 ctrl+a
```python
from DrissionPage import ChromiumPage, ActionChains, Keys
# 创建页面
page = ChromiumPage()
# 创建动作链对象
ac = ActionChains(page)
# 鼠标移动到<input>元素上
ac.move_to('tag:input')
# 点击鼠标,使光标落到元素中
ac.click()
# 按下 ctrl 键
ac.key_down(Keys.CTRL)
# 输入 a
ac.type('a')
# 提起 ctrl 键
ac.key_up(Keys.CTRL)
```
链式写法:
```python
ac.click('tag:input').key_down(Keys.CTRL).type('a').key_up(Keys.CTRL)
```
---
## 📍 拖拽元素
把一个元素向右拖拽 300 像素:
```python
from DrissionPage import ChromiumPage, ActionChains
# 创建页面
page = ChromiumPage()
# 创建动作链对象
ac = ActionChains(page)
# 左键按住元素
ac.hold('#div1')
# 向右移动鼠标300像素
ac.right(300)
# 释放左键
ac.release()
```
把一个元素拖拽到另一个元素上:
```python
ac.hold('#div1').release('#div2')
```

View File

@ -1,621 +0,0 @@
浏览器的启动配置非常繁杂,本库使用`ChromiumOptions`类管理启动配置,并且内置了常用配置的设置接口。
!> **注意:** <br>该对象只能用于浏览器的启动,浏览器启动后,再修改该配置没有任何效果。接管已打开的浏览器时,启动配置也是无效的。
# ✔️ 创建对象
## 📍 导入
```python
from DrissionPage import ChromiumOptions
```
---
## 📍 `ChromiumOptions`
`ChromiumOptions`对象用于管理浏览器初始化配置。可从配置文件中读取配置来进行初始化。
| 初始化参数 | 类型 | 默认值 | 说明 |
|:-----------:|:---------------:|:------:| ---------------------------------- |
| `read_file` | `bool` | `True` | 是否从 ini 文件中读取配置信息,为`False`则用默认配置创建 |
| `ini_path` | `Path`<br>`str` | `None` | 指定 ini 文件路径,为`None`则读取内置 ini 文件 |
创建配置对象:
```python
from DrissionPage import ChromiumOptions
co = ChromiumOptions()
```
默认情况下,`ChromiumOptions`对象会从 ini 文件中读取配置信息,当指定`read_file`参数为`False`时,则以默认配置创建。
---
# ✔️ 使用方法
创建配置对象后,可调整配置内容,然后在页面对象创建时以参数形式把配置对象传递进去,页面对象会根据配置对象的内容对浏览器进行初始化。
```python
from DrissionPage import WebPage, ChromiumOptions
# 创建配置对象(默认从 ini 文件中读取配置)
co = ChromiumOptions()
# 设置不加载图片、静音
co.set_no_imgs(True).set_mute(True)
# 以该配置创建页面对象
page = WebPage(driver_or_options=co)
```
---
# ✔️ 启动参数配置
Chromium 内核浏览器有一系列的启动配置,以`--`开头,可在浏览器创建时传入。如`--headless`无界面模式,`--disable-images`禁用图像等。有些参数只有参数名,有些会带有值。
启动参数非常多,详见:[List of Chromium Command Line Switches « Peter Beverloo](https://peter.sh/experiments/chromium-command-line-switches/)
`set_argument()``remove_argument()`方法用于设置浏览器启动命令行参数。
## 📍 `set_argument()`
此方法用于设置启动参数,带值和不带值的参数项都可以。
| 参数名称 | 类型 | 默认值 | 说明 |
|:-------:|:--------------------------:|:------:| ------------------------------------------------- |
| `arg` | `str` | 必填 | 启动参数名称 |
| `value` | `str`<br>`None`<br>`False` | `None` | 参数的值。带值的参数传入属性值,没有的传入`None`<br>如传入`False`,删除该参数。 |
| 返回类型 | 说明 |
| ----------------- | ------ |
| `ChromiumOptions` | 配置对象本身 |
**示例:** 无值和有值的参数设置
```python
# 设置用无头模式启动浏览器
co.set_argument('--headless')
# 设置浏览器默认 user agent
co.set_argument('--user-agent', 'Mozilla/5.0 (Macintos.....')
```
---
## 📍 `remove_argument()`
此方法用于在启动配置中删除一个启动参数,只要传入参数名称即可,不需要传入值。
| 参数名称 | 类型 | 默认值 | 说明 |
|:-----:|:-----:|:---:| ------------------- |
| `arg` | `str` | 必填 | 参数名称,有值的设置项传入设置名称即可 |
| 返回类型 | 说明 |
| ----------------- | ------ |
| `ChromiumOptions` | 配置对象自身 |
**示例:** 删除无值和有值的参数设置
```python
# 删除--headless参数
co.remove_argument('--headless')
# 删除--user-agent参数
co.remove_argument('--user-agent')
```
---
# ✔️ 插件配置
`add_extension()``remove_extensions()`用于设置浏览器启动时要加载的插件。可以指定数量不限的插件。
## 📍 `add_extension()`
此方法用于添加一个插件到浏览器。
| 参数名称 | 类型 | 默认值 | 说明 |
|:------:|:---------------:|:---:| ---- |
| `path` | `str`<br>`Path` | 必填 | 插件路径 |
| 返回类型 | 说明 |
| ----------------- | ------ |
| `ChromiumOptions` | 配置对象本身 |
?>**Tips**<br>根据作者的经验,把插件文件解压到一个独立文件夹,然后把插件路径指向这个文件夹,会比较稳定。
**示例:**
```python
co.add_extension(r'D:\SwitchyOmega')
```
---
## 📍 `remove_extensions()`
此方法用于移除配置对象中保存的所有插件路径。如须移除部分插件,请移除全部后再重新添加需要的插件。
**参数:** 无
**返回:** 配置对象自身
```python
co.remove_extensions()
```
---
# ✔️ 用户文件配置
除了启动参数,还有大量配置信息保存在 preferences 文件,以下方法用于对用户文件进行设置。
## 📍 `set_user()`
Chromium 浏览器支持多用户配置,我们可以选择使用哪一个。默认为`'Default'`
| 参数名称 | 类型 | 默认值 | 说明 |
|:------:|:-----:|:-----------:| --------- |
| `user` | `str` | `'Default'` | 用户配置文件夹名称 |
| 返回类型 | 说明 |
| ----------------- | ------ |
| `ChromiumOptions` | 配置对象本身 |
**示例:**
```python
co.set_user(user='Profile 1')
```
---
## 📍 `set_pref()`
此方法用于设置用户配置文件里的一个配置项。
在哪里可以查到所有的配置项?作者也没找到,知道的请告知。谢谢。
| 参数名称 | 类型 | 默认值 | 说明 |
|:-------:|:-----:|:---:| ----- |
| `arg` | `str` | 必填 | 设置项名称 |
| `value` | `str` | 必填 | 设置项值 |
| 返回类型 | 说明 |
| ----------------- | ------ |
| `ChromiumOptions` | 配置对象本身 |
**示例:**
```python
co.set_pref(arg='profile.default_content_settings.popups', value='0')
```
---
## 📍 `remove_pref()`
此方法用于在当前配置对象中删除一个`pref`配置项。
| 参数名称 | 类型 | 默认值 | 说明 |
|:-----:|:-----:|:---:| ----- |
| `arg` | `str` | 必填 | 设置项名称 |
| 返回类型 | 说明 |
| ----------------- | ------ |
| `ChromiumOptions` | 配置对象本身 |
**示例:**
```python
co.remove_pref(arg='profile.default_content_settings.popups')
```
---
## 📍 `remove_pref_from_file()`
此方法用于在用户配置文件删除一个配置项。注意与上一个方法不一样,如果用户配置文件中已经存在某个项,用`remove_pref()`是不能删除的,只能用`remove_pref_from_file()`删除。
| 参数名称 | 类型 | 默认值 | 说明 |
|:-----:|:-----:|:---:| ----- |
| `arg` | `str` | 必填 | 设置项名称 |
| 返回类型 | 说明 |
| ----------------- | ------ |
| `ChromiumOptions` | 配置对象本身 |
**示例:**
```python
co.remove_pref_from_file(arg='profile.default_content_settings.popups')
```
---
# ✔️ 运行参数配置
页面对象运行时需要用到的参数,也可以在`ChromiumOptions`中设置。
## 📍 `set_paths()`
此方法用于设置各种路径信息。对有传入值的路径进行设置,为`None`的则无视。
| 参数名称 | 类型 | 默认值 | 说明 |
|:------------------:|:---------------:|:------:| ----------------------------------------------------------- |
| `browser_path` | `str`<br>`Path` | `None` | 浏览器可执行文件路径 |
| `local_port` | `str`<br>`int` | `None` | 浏览器要使用的本地端口号 |
| `debugger_address` | `str` | `None` | 浏览器地址127.0.0.1:9222如与`local_port`一起设置,会覆盖`local_port`的值 |
| `download_path` | `str`<br>`Path` | `None` | 下载文件默认保存路径 |
| `user_data_path` | `str`<br>`Path` | `None` | 用户数据文件夹路径 |
| `cache_path` | `str`<br>`Path` | `None` | 缓存路径 |
| 返回类型 | 说明 |
| ----------------- | ------ |
| `ChromiumOptions` | 配置对象本身 |
**示例:**
```python
co.set_paths(local_port=9333, user_data_path=r'D:\tmp')
```
---
## 📍 `auto_port()`
此方法用于设置是否使用自动分配的端口。
如果设置为`True`,程序会自动寻找一个可用端口,并在系统临时文件夹创建一个文件夹,用于储存浏览器数据。由于端口和用户文件夹都是唯一的,所以用这种方式启动的浏览器不会产生冲突,但也无法多次启动程序时重复接管同一个浏览器。
`set_paths()`方法中`local_port``user_data_path`参数,会和`auto_port()`互相覆盖,即以后调用的为准。
| 参数名称 | 类型 | 默认值 | 说明 |
|:--------:|:------:|:------:| ---------------- |
| `on_off` | `bool` | `True` | 是否开启自动分配端口和用户文件夹 |
| 返回类型 | 说明 |
| ----------------- | ------ |
| `ChromiumOptions` | 配置对象本身 |
**示例:**
```python
co.auto_port(True)
```
!>**注意:**<br>启用此功能后即会获取端口和新建临时用户数据文件夹,若此时用`save()`方法保存配置到 ini 文件ini 文件中的设置会被该端口和文件夹路径覆盖。这个覆盖对使用并没有很大影响。
---
## 📍 `set_timeouts()`
此方法用于设置几种超时时间,以秒为单位。超时用法详见使用方法章节。
| 参数名称 | 类型 | 默认值 | 说明 |
|:----------:|:----------------:|:------:| ------------------------------------------------------------- |
| `implicit` | `int`<br>`float` | `None` | 默认超时时间用于元素等待、alert 等待、`WebPage`的 s 模式连接等等,除以下两个参数的场景,都使用这个设置 |
| `pageLoad` | `int`<br>`float` | `None` | 页面加载超时时间 |
| `script` | `int`<br>`float` | `None` | JavaScript 运行超时时间 |
| 返回类型 | 说明 |
| ----------------- | ------ |
| `ChromiumOptions` | 配置对象本身 |
**示例:**
```python
co.set_timeouts(implicit=10)
```
---
## 📍 `set_page_load_strategy()`
此方法用于设置网页加载策略。
加载策略是指强制页面停止加载的时机,如加载完 DOM 即停止,不加载图片资源等,以提高自动化效率。
无论设置哪种策略,加载时间都不会超过`set_timeouts()``pageLoad`参数设置的时间。
加载策略:
- `'normal'`:阻塞进程,等待所有资源下载完成(默认)
- `'eager'`DOM 就绪即停止加载
- `'none'`:网页连接成功即停止加载
| 参数名称 | 类型 | 默认值 | 说明 |
|:-------:|:-----:|:---:| -------------------------------- |
| `value` | `str` | 必填 | 可接收`'normal'``'eager'``'none'` |
| 返回类型 | 说明 |
| ----------------- | ------ |
| `ChromiumOptions` | 配置对象本身 |
**示例:**
```python
co.set_page_load_strategy('eager')
```
---
## 📍 `set_proxy()`
该方法用于设置浏览器代理。
| 参数名称 | 类型 | 默认值 | 说明 |
|:-------:|:-----:|:---:| --------------------------------------- |
| `proxy` | `str` | 必填 | 格式:协议://ip:port<br>当不指定协议时,默认使用 http 代理 |
| 返回类型 | 说明 |
| ----------------- | ------ |
| `ChromiumOptions` | 配置对象本身 |
**示例:**
```python
co.set_proxy('http://localhost:1080')
```
---
# ✔️ 其它配置
作者将一些常用的配置封装成方法,可以直接调用。
## 📍 `set_headless()`
该方法用于设置是否已无界面模式启动浏览器。
| 参数名称 | 类型 | 默认值 | 说明 |
|:--------:|:------:|:------:| ------------------- |
| `on_off` | `bool` | `True` | `True``False`表示开或关 |
| 返回类型 | 说明 |
| ----------------- | ------ |
| `ChromiumOptions` | 配置对象本身 |
**示例:**
```python
co.set_headless(True)
```
!>**注意:**<br>经实测Chrome 使用无界面模式时,由 js 生成的元素可能不能加载。
---
## 📍 `set_no_imgs()`
该方法用于设置是否禁止加载图片。
| 参数名称 | 类型 | 默认值 | 说明 |
|:--------:|:------:|:------:| ------------------- |
| `on_off` | `bool` | `True` | `True``False`表示开或关 |
| 返回类型 | 说明 |
| ----------------- | ------ |
| `ChromiumOptions` | 配置对象本身 |
**示例:**
```python
co.set_no_imgs(True)
```
---
## 📍 `set_no_js()`
该方法用于设置是否禁用 JavaScript。
| 参数名称 | 类型 | 默认值 | 说明 |
|:--------:|:------:|:------:| ------------------- |
| `on_off` | `bool` | `True` | `True``False`表示开或关 |
| 返回类型 | 说明 |
| ----------------- | ------ |
| `ChromiumOptions` | 配置对象本身 |
**示例:**
```python
co.set_no_js(True)
```
---
## 📍 `set_mute()`
该方法用于设置是否静音。
| 参数名称 | 类型 | 默认值 | 说明 |
|:--------:|:------:|:------:| ------------------- |
| `on_off` | `bool` | `True` | `True``False`表示开或关 |
| 返回类型 | 说明 |
| ----------------- | ------ |
| `ChromiumOptions` | 配置对象本身 |
**示例:**
```python
co.set_mute(True)
```
---
## 📍 `set_user_agent()`
该方法用于设置 user agent。
| 参数名称 | 类型 | 默认值 | 说明 |
|:------------:|:-----:|:---:| ------------ |
| `user_agent` | `str` | 必填 | user agent文本 |
| 返回类型 | 说明 |
| ----------------- | ------ |
| `ChromiumOptions` | 配置对象本身 |
**示例:**
```python
co.set_user_agent(user_agent='Mozilla/5.0 (Macintos.....')
```
---
# ✔️ 保存设置到文件
您可以把不同的配置保存到各自的 ini 文件,以便适应不同的场景。
## 📍 `save()`
此方法用于保存配置项到一个 ini 文件。
| 参数名称 | 类型 | 默认值 | 说明 |
|:------:|:---------------:|:------:| ------------------------------- |
| `path` | `str`<br>`Path` | `None` | ini 文件的路径, 传入`None`保存到当前读取的配置文件 |
| 返回类型 | 说明 |
| ----- | -------------- |
| `str` | 保存的 ini 文件绝对路径 |
**示例:**
```python
# 保存当前读取的ini文件
co.save()
# 把当前配置保存到指定的路径
co.save(path=r'D:\tmp\settings.ini')
```
---
## 📍 `save_to_default()`
此方法用于保存配置项到固定的默认 ini 文件。默认 ini 文件是指随 DrissionPage 内置的那个。
**参数:** 无
| 返回类型 | 说明 |
| ----- | -------------- |
| `str` | 保存的 ini 文件绝对路径 |
**示例:**
```python
co.save_to_default()
```
---
# ✔️ `ChromiumOptions`属性
## 📍 `debugger_address`
该属性为要控制的浏览器地址,格式为 ip:port默认为`'127.0.0.0:9222'`。可对其赋值进行设置。也可以用后文介绍的`set_paths()`方法设置。
**类型:**`str`
```python
co.debugger_address = 'localhost:9333'
```
---
## 📍 `browser_path`
该属性返回浏览器可执行文件的路径。
**类型:**`str`
---
## 📍 `user_data_path`
该属性返回用户数据文件夹路径。
**类型:**`str`
---
## 📍 `download_path`
该属性返回默认下载路径文件路径。
**类型:**`str`
---
## 📍 `user`
该属性返回用户配置文件夹名称。
**类型:**`str`
---
## 📍 `page_load_strategy`
该属性返回页面加载策略。有`'normal'``'eager'``'none'`三种
**类型:**`str`
---
## 📍 `timeouts`
该属性返回超时设置。包括三种:`'implicit'``'pageLoad'``'script'`
**类型:**`dict`
```python
print(co.timeouts)
```
**输出:**
```console
{
'implicit': 10,
    'pageLoad': 30,
    'script': 30
}
```
---
## 📍 `proxy`
该属性返回代理设置。
**类型:**`str`
---
## 📍 `arguments`
该属性以`list`形式返回浏览器启动参数。
**类型:**`list`
---
## 📍 `extensions`
该属性以`list`形式返回要加载的插件路径。
**类型:**`list`
---
## 📍 `preferences`
该属性返回用户首选项配置。
**类型:**`dict`

View File

@ -1,239 +0,0 @@
`ChromiumPage`对象和`WebPage`对象的 d 模式都能收发数据包,本节只介绍`ChromiumPage`的创建,在`WebPage`的章节再对其进行介绍。
# ✔️ `ChromiumPage`初始化参数
| 初始化参数 | 类型 | 默认值 | 说明 |
|:------------------:|:---------------------------------------------------:|:------:| ---------------------------------------------------------------------------------------------------------------------------- |
| `addr_driver_opts` | `str`<br>`ChrromiumDriver`<br>`ChromiumOptions`<br> | `None` | 浏览器启动配置或接管信息。<br>传入`ChromiumDriver`对象时接管浏览器;<br>传入 ' ip: port' 字符串或`ChromiumOptions`对象时按配置启动或接管浏览器;<br>`None`时使用配置文件配置启动浏览器 |
| `tab_id` | `str` | `None` | 要控制的标签页 id`None`则控制激活的标签页 |
| `timeout` | `float` | `None` | 整体超时时间,为`None`则从配置文件中读取,默认 10 |
---
# ✔️ 直接创建
这种方式代码最简洁,程序会从默认 ini 文件中读取配置,自动生成页面对象。
```python
from DrissionPage import ChromiumPage
page = ChromiumPage()
```
创建`ChromiumPage`对象时会在指定端口启动浏览器,或接管该端口已有浏览器。
默认情况下,程序使用 9222 端口,浏览器可执行文件路径为`'chrome'`。如路径中没找到浏览器可执行文件Windows 系统下程序会在注册表中查找路径。
如果都没找到,则要用下一种方式手动配置。
!>**注意:**<br>这种方式的程序不能直接打包,因为使用到 ini 文件。可参考“打包程序”一节的方法。
?>**Tips**<br>您可以修改配置文件中的配置,实现所有程序都按您的须要进行启动,详见”启动配置“章节。
---
# ✔️ 通过配置信息创建
如果须要已指定方式启动浏览器,可使用`ChromiumOptions`。它是专门用于设置浏览器初始状态的类,内置了常用的配置。详细使用方法见“浏览器启动配置”一节。
## 📍 使用方法
`ChromiumOptions`用于管理创建浏览器时的配置,内置了常用的配置,并能实现链式操作。详细使用方法见“启动配置”一节。
| 初始化参数 | 类型 | 默认值 | 说明 |
| ----------- | ------ | ------ | ------------------------------------ |
| `read_file` | `bool` | `True` | 是否从 ini 文件中读取配置信息,如果为`False`则用默认配置创建 |
| `ini_path` | `str` | `None` | 文件路径,为`None`则读取默认 ini 文件 |
!>**注意:**<br>- 配置对象只有在启动浏览器时生效。<br>- 浏览器创建后再修改这个配置是没有效果的。<br>- 接管已打开的浏览器配置也不会生效。
```python
# 导入 ChromiumOptions
from DrissionPage import ChromiumPage, ChromiumOptions
# 创建浏览器配置对象,指定浏览器路径
co = ChromiumOptions().set_paths(browser_path=r'D:\chrome.exe')
# 用该配置创建页面对象
page = ChromiumPage(addr_driver_opts=co)
```
---
## 📍 直接指定地址创建
`ChromiumPage`可以直接接收浏览器地址来创建,格式为 'ip:port'。
使用这种方式时,如果浏览器已存在,程序会直接接管;如不存在,程序会读取默认配置文件配置,在指定端口启动浏览器。
```python
page = ChromiumPage(addr_driver_opts='127.0.0.1:9333')
```
---
## 📍 使用指定 ini 文件创建
以上方法是使用默认 ini 文件中保存的配置信息创建对象,你可以保存一个 ini 文件到别的地方,并在创建对象时指定使用它。
```python
from DrissionPage import ChromiumPage, ChromiumOptinos
# 创建配置对象时指定要读取的ini文件路径
co = ChromiumOptinos(ini_path=r'./config1.ini')
# 使用该配置对象创建页面
page = ChromiumPage(addr_driver_opts=co)
```
---
# ✔️ 传递控制权
当须要使用多个页面对象共同操作一个页面时,可在对象间传递驱动器。
这可以实现多个页面对象共同控制一个浏览器。
```python
# 创建一个页面
page1 = ChormiumPage()
# 获取页面对象的浏览器控制器
driver = page1.driver
# 把控制器对象在第二个页面对象初始化时传递进去
page2 = ChormiumPage(driver_or_options=driver)
```
---
# ✔️ 接管已打开的浏览器
页面对象创建时只要指定的地址ip: port已有浏览器在运行就会直接接管。无论浏览器是下面哪种方式启动的。
## 📍 用程序启动的浏览器
默认情况下,创建浏览器页面对象时会自动启动一个浏览器。只要这个浏览器不关闭,下次运行程序时会接管同一个浏览器继续操作(配置的 ip: port 信息不变)。
这种方式极大地方便了程序的调试,使程序不必每次重新开始,可以单独调试某个功能。
```python
from DrissionPage import ChromiumPage
# 创建对象同时启动浏览器,如果浏览器已经存在,则接管它
page = ChromiumPage()
```
---
## 📍 手动打开的浏览器
如果须要手动打开浏览器再接管,可以这样做:
- 右键点击浏览器图标,选择属性
- 在“目标”路径后面加上` --remote-debugging-port=端口号`(注意最前面有个空格)
- 点击确定
- 在程序中的浏览器配置中指定接管该端口浏览器
文件快捷方式的目标路径设置:
```
D:\chrome.exe --remote-debugging-port=9222
```
程序代码:
```python
from DrissionPage import ChromiumPage, ChromiumOptions
co = ChromiumOptions().set_paths(local_port=9222)
page = ChromiumPage(addr_driver_opts=co)
```
!> **注意:**<br>接管浏览器时只有`local_port``debugger_address`参数是有效的。
---
## 📍 bat 文件启动的浏览器
可以把上一种方式的目标路径设置写进 bat 文件Windows系统运行 bat 文件来启动浏览器,再用程序接管。
新建一个文本文件,在里面输入以下内容(路径改为自己电脑的):
```console
"D:\chrome.exe" --remote-debugging-port=9222
```
保存后把后缀改成 bat然后双击运行就能在 9222 端口启动一个浏览器。程序代码则和上一个方法一致。
---
# ✔️ 多浏览器共存
如果想要同时操作多个浏览器,或者自己在使用其中一个上网,同时控制另外几个跑自动化,就须要给这些被程序控制的浏览器设置单独的**端口**和**用户文件夹**,否则会造成冲突。
## 📍 指定独立端口和数据文件夹
每个要启动的浏览器使用一个独立的`ChromiumOptions`对象进行设置:
```python
from DrissionPage import ChromiumPage, ChromiumOptions
# 创建多个配置对象,每个指定不同的端口号和用户文件夹路径
do1 = ChromiumOptions().set_paths(local_port=9111, user_data_path=r'D:\data1')
do2 = ChromiumOptions().set_paths(local_port=9222, user_data_path=r'D:\data2')
# 创建多个页面对象
page1 = ChromiumPage(addr_driver_opts=do1)
page2 = ChromiumPage(addr_driver_opts=do2)
# 每个页面对象控制一个浏览器
page1.get('https://www.baidu.com')
page2.get('http://www.163.com')
```
?>**Tips**<br>每个浏览器都要设置独立的端口号和用户文件夹,二者缺一不可。
---
## 📍 `auto_port()`方法
`ChromiumOptions`对象的`auto_port()`方法,可以指定程序每次使用空闲的端口和临时用户文件夹创建浏览器。也是每个浏览器要使用独立的`ChromiumOptions`对象。
但这种方法创建的浏览器不能重复使用。
```python
from DrissionPage import ChromiumPage, ChromiumOptions
co1 = ChromiumOptions().auto_port()
co2 = ChromiumOptions().auto_port()
page1 = ChromiumPage(addr_driver_opts=co1)
page2 = ChromiumPage(addr_driver_opts=co2)
page1.get('https://www.baidu.com')
page2.get('http://www.163.com')
```
---
## 📍 在 ini 文件设置使用自动分配
可以把自动分配的配置记录到 ini 文件,这样无须创建`ChromiumOptions`,每次启动的浏览器都是独立的,不会冲突。但和`auto_port()`一样,这些浏览器也不能复用。
```python
from DrissionPage.easy_set import use_auto_port
use_auto_port(True)
```
这段代码把记录该配置到 ini 文件,只需运行一次,要关闭的话把参数换成`False`再执行一次即可。
```python
from DrissionPage import ChromiumPage
page1 = ChromiumPage()
page2 = ChromiumPage()
page1.get('https://www.baidu.com')
page2.get('http://www.163.com')
```

View File

@ -1,62 +0,0 @@
`ChromiumPage`对象和`WebPage`对象的 d 模式都能控制浏览器访问网页。这里只对`ChromiumPage`进行说明,后面章节再单独介绍`WebPage`
# ✔️ `get()`
该方法用于跳转到一个网址。当连接失败时,程序会进行重试。
| 参数名称 | 类型 | 默认值 | 说明 |
|:-------------:|:----------------:|:-------:| --------------------------- |
| `url` | `str` | 必填 | 目标 url |
| `show_errmsg` | `bool` | `False` | 连接出错时是否显示和抛出异常 |
| `retry` | `int` | `None` | 重试次数,为`None`时使用页面参数,默认 3 |
| `interval` | `int`<br>`float` | `None` | 重试间隔(秒),为`None`时使用页面参数,默认 2 |
| `timeout` | `int`<br>`float` | `None` | 加载超时时间(秒) |
| 返回类型 | 说明 |
|:------:| ----- |
| `bool` | 否连接成功 |
**示例:**
```python
from DrissionPage import ChromiumPage
page = ChromiumPage()
page.get('https://www.baidu.com')
```
---
# ✔️ 设置超时和重试
网络不稳定时访问页面不一定成功,`get()`方法内置了超时和重试功能。通过`retry``interval``timeout`三个参数进行设置。
其中,如不指定`timeout`参数,该参数会使用`ChromiumPage``timeouts`属性的`page_load`参数中的值。
```python
from DrissionPage import ChromiumPage
page = ChromiumPage()
page.get('https://www.163.com', retry=1, interval=1, timeout=1.5)
```
---
# ✔️ 设置加载策略
通过设置`ChromiumPage`对象的`page_load_strategy`属性,可设置页面停止加载的时机。页面加载时,在到达超时时间,或达到设定的状态,就会停止,可有效节省采集时间。有以下三种模式:
- `'normal'`:常规模式,会等待页面加载完毕
- `'eager'`:加载完 DOM 即停止加载
- `'none'`:完成连接即停止加载
默认设置为`'normal'`
```python
from DrissionPage import ChromiumPage
page = ChromiumPage()
page.set_page_load_strategy('eager')
page.get('https://www.163.com')
```

View File

@ -1,320 +0,0 @@
成功访问网页后,可使用`ChromiumPage`自身属性和方法获取页面信息。
操控浏览器除了`ChromiumPage`,还有`ChromiumTab``ChromiumFrame`两种页面对象分别对应于标签页对象和`<iframe>`元素对象,后面会有单独章节介绍。
# ✔️ 运行状态信息
## 📍 `url`
此属性返回当前访问的 url。
**返回类型:**`str`
---
## 📍 `address`
此属性返回当前对象控制的页面地址和端口。
**返回类型:**`str`
```python
print(page.address)
```
**输出:**
```
127.0.0.1:9222
```
---
## 📍 `tab_id`
**返回类型:**`str`
此属性返回当前标签页的 id。
---
## 📍 `process_id`
此属性返回浏览器进程 id。
**返回类型:**`int``None`
---
## 📍 `is_loading`
此属性返回页面是否正在加载状态。
**返回类型:**`bool`
---
## 📍 `ready_state`
此属性返回页面当前加载状态,有 3 种:
- `'loading'`:表示文档还在加载中
- `'interactive'`DOM 已加载,但资源未加载完成
- `'complete'`:所有内容已完成加载
**返回类型:**`str`
---
## 📍 `url_available`
此属性以布尔值返回当前链接是否可用。
**返回类型:**`bool`
---
# ✔️ 窗口及页面信息
## 📍 `size`
此属性以`tuple`返回页面尺寸,格式:(宽, 高)。
**返回类型:**`Tuple[int, int]`
---
## 📍 `tabs_count`
此属性返回当前浏览器标签页数量。
**返回类型:**`int`
---
## 📍 `tabs`
此属性以列表形式返回当前浏览器所有标签页 id。
**返回类型:**`List[str]`
---
## 📍 `html`
此属性返回当前页面 html 文本。
**返回类型:**`str`
---
## 📍 `json`
此属性把请求内容解析成 json。
假如用浏览器访问会返回 json 数据的 url浏览器会把 json 数据显示出来,这个参数可以把这些数据转换为`dict`格式。
**返回类型:**`dict`
---
## 📍 `title`
此属性返回当前页面`title`文本。
**返回类型:**`str`
---
# ✔️ 配置参数信息
## 📍 `timeout`
此属性为整体默认超时时间,包括元素查找、点击、处理提示框、列表选择等须要用到超时设置的地方,都以这个数据为默认值。
默认为 10可对其赋值。
**返回类型:**`int``float`
```python
# 创建页面对象时指定
page = ChromiumPage(timeout=5)
# 修改 timeout
page.timeout = 20
```
---
## 📍 `timeouts`
此属性以字典方式返回三种超时时间。
- `'implicit'`:与`timeout`属性是同一个值
- `'page_load'`:用于等待页面加载
- `'script'`:用于等待脚本执行
**返回类型:**`dict`
```python
print(page.timeouts)
```
**输出:**
```
{'implicit': 10, 'pageLoad': 30.0, 'script': 30.0}
```
---
## 📍 `retry_times`
此属性为网络连接失败时的重试次数。默认为 3可对其赋值。
**返回类型:**`int`
```python
# 修改重试次数
page.retry_times = 5
```
---
## 📍 `retry_interval`
此属性为网络连接失败时的重试等待间隔秒数。默认为 2可对其赋值。
**返回类型:**`int``float`
```python
# 修改重试等待间隔时间
page.retry_interval = 1.5
```
---
## 📍 `page_load_strategy`
此属性返回页面加载策略,有 3 种:
- `'normal'`:等待页面所有资源完成加载
- `'eager'`DOM 加载完成即停止
- `'none'`:页面完成连接即停止
**返回类型:**`str`
---
# ✔️ cookies 和缓存信息
## 📍 `cookies`
此属性以`dict`方式返回当前页面所使用的 cookies。
**返回类型:**`dict`
---
## 📍 `get_cookies()`
此方法获取 cookies 并以 cookie 组成的`list`形式返回。
| 参数名称 | 类型 | 默认值 | 说明 |
|:---------:|:------:|:-------:| --------------------------------------- |
| `as_dict` | `bool` | `False` | 是否以字典方式返回结果,为`False`返回 cookie 组成的`list` |
| 返回类型 | 说明 |
|:------:| ------------- |
| `dict` | cookies 字典 |
| `list` | cookies 组成的列表 |
**示例:**
```python
from DrissionPage import ChromiumPage
p = ChromiumPage()
p.get('http://www.baidu.com')
for i in p.get_cookies(as_dict=False):
print(i)
```
**输出:**
```
{'domain': '.baidu.com', 'domain_specified': True, ......}
......
```
---
## 📍 `get_session_storage()`
此方法用于获取 sessionStorage 信息,可获取全部或单个项。
| 参数名称 | 类型 | 默认值 | 说明 |
|:------:|:-----:|:------:| -------------------------- |
| `item` | `str` | `None` | 要获取的项目,为`None`则返回全部项目组成的字典 |
| 返回类型 | 说明 |
|:------:| ---------------------- |
| `dict` | `item`参数为`None`时返回所有项目 |
| `str` | 指定`item`时返回该项目内容 |
---
## 📍 `get_local_storage()`
此方法用于获取 localStorage 信息,可获取全部或单个项。
| 参数名称 | 类型 | 默认值 | 说明 |
|:------:|:-----:|:------:| -------------------------- |
| `item` | `str` | `None` | 要获取的项目,为`None`则返回全部项目组成的字典 |
| 返回类型 | 说明 |
|:------:| ---------------------- |
| `dict` | `item`参数为`None`时返回所有项目 |
| `str` | 指定`item`时返回该项目内容 |
---
# ✔️ 内嵌对象
## 📍 `driver`
此属性返回当前页面对象使用的`ChromiumDriver`对象。
**返回类型:**`ChromiumDriver`
---
# ✔️ 页面截图
## 📍 `get_screenshot()`
此方法用于对页面进行截图,可对整个网页、可见网页、指定范围截图。对可视范围外截图需要 90 以上版本浏览器支持。
| 参数名称 | 类型 | 默认值 | 说明 |
|:--------------:|:-------------------------:|:-------:| --------------------------------------------------------------------------------------------------------- |
| `path` | `str`<br>`Path`<br>`None` | `None` | 保存图片的完整路径,文件后缀可选`'jpg'``'jpeg'``'png'``'webp'`<br>`None`时以 jpg 格式保存在当前文件夹 |
| `as_bytes` | `str`<br>`None`<br>`True` | `None` | 是否已字节形式返回图片,可选`'jpg'``'jpeg'``'png'``'webp'``None``True`<br>不为`None``path`参数无效<br>`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``None`时返回图片完整路径 |
```python
# 对整页截图并保存
page.get_screenshot(path='D:\\page.png', full_page=True)
```

View File

@ -1,506 +0,0 @@
本节介绍浏览器页面交互功能,元素的交互在下一节。
页面对象包括`ChromiumPage`、d 模式的`WebPage``ChromiumTab``ChromiumFrame`几种,这里只介绍`ChromiumPage`,其它几种后面专门章节介绍。
# ✔️ 页面跳转
## 📍 `get()`
该方法用于跳转到一个网址。当连接失败时,程序会进行重试。
| 参数名称 | 类型 | 默认值 | 说明 |
|:-------------:|:----------------:|:-------:| --------------------------- |
| `url` | `str` | 必填 | 目标 url |
| `show_errmsg` | `bool` | `False` | 连接出错时是否显示和抛出异常 |
| `retry` | `int` | `None` | 重试次数,为`None`时使用页面参数,默认 3 |
| `interval` | `int`<br>`float` | `None` | 重试间隔(秒),为`None`时使用页面参数,默认 2 |
| `timeout` | `int`<br>`float` | `None` | 加载超时时间(秒) |
| 返回类型 | 说明 |
|:------:| ------ |
| `bool` | 是否连接成功 |
**示例:**
```python
page.get('https://www.baidu.com')
```
---
## 📍 `back()`
此方法用于在浏览历史中后退若干步。
| 参数名称 | 类型 | 默认值 | 说明 |
|:-------:|:-----:|:---:| ---- |
| `steps` | `int` | `1` | 后退步数 |
**返回:**`None`
**示例:**
```python
page.back(2) # 后退两个网页
```
---
## 📍 `forward()`
此方法用于在浏览历史中前进若干步。
| 参数名称 | 类型 | 默认值 | 说明 |
|:-------:|:-----:|:---:| ---- |
| `steps` | `int` | `1` | 前进步数 |
**返回:**`None`
```python
page.forward(2) # 前进两步
```
---
## 📍 `refresh()`
此方法用于刷新当前页面。
| 参数名称 | 类型 | 默认值 | 说明 |
|:--------------:|:------:|:-------:| --------- |
| `ignore_cache` | `bool` | `False` | 刷新时是否忽略缓存 |
**返回:**`None`
```python
page.refresh() # 刷新页面
```
---
## 📍 `stop_loading()`
此方法用于强制停止当前页面加载。
**参数:** 无
**返回:**`None`
---
## 📍 `wait_loading()`
此方法用于等待页面进入加载状态。
我们经常会通过点击页面元素进入下一个网页,并立刻获取新页面的元素。但若跳转前的页面拥有和跳转后页面相同定位符的元素,会导致过早获取元素,跳转后失效的问题。使用此方法,会阻塞程序,等待页面开始加载后再继续,从而避免上述问题。
| 参数名称 | 类型 | 默认值 | 说明 |
|:---------:|:------------------------------------:|:------:| ------------------------------------------------- |
| `timeout` | `int`<br>`float`<br>`None`<br>`True` | `None` | 超时时间,为`None``Ture`时使用页面`timeout`设置<br>为数字时等待相应时间 |
| 返回类型 | 说明 |
|:------:| ------------- |
| `bool` | 等待结束时是否进入加载状态 |
**示例:**
```python
ele.click() # 点击某个元素
page.wait_loading() # 等待页面进入加载状态
# 执行在新页面的操作
print(page.title)
```
---
# ✔️ 执行脚本或命令
## 📍 `run_js()`
此方法用于执行 js 脚本。
| 参数名称 | 类型 | 默认值 | 说明 |
|:---------:|:------:|:-------:| ----------------------------------------------- |
| `script` | `str` | 必填 | js 脚本文本 |
| `as_expr` | `bool` | `False` | 是否作为表达式运行,为`True``args`参数无效 |
| `*args` | - | 无 | 传入的参数按顺序在js文本中对应`argument[0]``argument[1]`... |
| 返回类型 | 说明 |
|:-----:| ------ |
| `Any` | 脚本执行结果 |
**示例:**
```python
# 用传入参数的方式执行 js 脚本显示弹出框显示 Hello world!
page.run_js('alert(arguments[0]+arguments[1]);', 'Hello', ' world!')
```
---
## 📍 `run_async_script()`
此方法用于以异步方式执行 js 代码。
**参数:**
| 参数名称 | 类型 | 默认值 | 说明 |
|:---------:|:------:|:-------:| ----------------------------------------------- |
| `script` | `str` | 必填 | js 脚本文本 |
| `as_expr` | `bool` | `False` | 是否作为表达式运行,为`True``args`参数无效 |
| `*args` | - | 无 | 传入的参数按顺序在js文本中对应`argument[0]``argument[1]`... |
**返回:**`None`
---
## 📍 `run_cdp()`
此方法用于执行 Chrome DevTools Protocol 语句。
cdp 用法详见 [Chrome DevTools Protocol](https://chromedevtools.github.io/devtools-protocol/)。
| 参数名称 | 类型 | 默认值 | 说明 |
|:------------:|:-----:|:---:| ---- |
| `cmd` | `str` | 必填 | 协议项目 |
| `**cmd_args` | - | 无 | 项目参数 |
| 返回类型 | 说明 |
|:------:| ------- |
| `dict` | 执行返回的结果 |
**示例:**
```python
# 停止页面加载
page.run_cdp('Page.stopLoading')
```
---
# ✔️ cookies 及缓存
## 📍 `set_cookies()`
此方法用于设置 cookies。
可以接收`CookieJar``list``tuple``str``dict`格式的 cookies。
| 参数名称 | 类型 | 默认值 | 说明 |
|:---------:|:-----------------------------------------------------------:|:---:| ---------- |
| `cookies` | `RequestsCookieJar`<br>`list`<br>`tuple`<br>`str`<br>`dict` | 必填 | cookies 信息 |
**返回:**`None`
```python
cookies = {'name': 'abc'}
page.set_cookies(cookies, set_session=True, set_driver=True)
```
---
## 📍 `set_session_storage()`
此方法用于设置或删除某项 sessionStorage 信息。
| 参数名称 | 类型 | 默认值 | 说明 |
|:-------:|:----------------:|:---:| -------------- |
| `item` | `str` | 必填 | 要设置的项 |
| `value` | `str`<br>`False` | 必填 | 为`False`时,删除该项 |
**返回:**`None`
**示例:**
```python
page.set_session_storage(item='abc', value='123')
```
---
## 📍 `set_local_storage()`
此方法用于设置或删除某项 localStorage 信息。
| 参数名称 | 类型 | 默认值 | 说明 |
|:-------:|:----------------:|:---:| -------------- |
| `item` | `str` | 必填 | 要设置的项 |
| `value` | `str`<br>`False` | 必填 | 为`False`时,删除该项 |
**返回:**`None`
---
## 📍 `clear_cache()`
此方法用于清除缓存,可选择要清除的项。
| 参数名称 | 类型 | 默认值 | 说明 |
|:-----------------:|:------:|:------:| ------------------- |
| `session_storage` | `bool` | `True` | 是否清除 sessionstorage |
| `local_storage` | `bool` | `True` | 是否清除 localStorage |
| `cache` | `bool` | `True` | 是否清除 cache |
| `cookies` | `bool` | `True` | 是否清除 cookies |
**返回:**`None`
**示例:**
```python
page.clear_cache(cookies=False) # 除了 cookies其它都清除
```
---
# ✔️ 各种设置
## 📍 `set_timeouts()`
此方法用于设置三种超时时间,单位为秒。可单独设置,为`None`表示不改变原来设置。
| 参数名称 | 类型 | 默认值 | 说明 |
|:-----------:|:--------------------------:|:------:| -------- |
| `implicit` | `int`<br>`float`<br>`None` | `None` | 整体超时时间 |
| `page_load` | `int`<br>`float`<br>`None` | `None` | 页面加载超时时间 |
| `script` | `int`<br>`float`<br>`None` | `None` | 脚本运行超时时间 |
**返回:**`None`
**示例:**
```python
page.set_timeouts(implicit=10, page_load=30)
```
---
## 📍 `set_page_load_strategy`
此属性用于设置页面加载策略,调用其方法选择某种策略。
| 方法名称 | 参数 | 说明 |
|:----------:|:---:| ------------------- |
| `normal()` | 无 | 等待页面完全加载完成,为默认状态 |
| `eager()` | 无 | 等待文档加载完成就结束,不等待资源加载 |
| `none()` | 无 | 页面连接完成就结束 |
**示例:**
```python
page.set_page_load_strategy.normal()
page.set_page_load_strategy.eager()
page.set_page_load_strategy.none()
```
---
## 📍 `set_ua_to_tab()`
此方法用于为浏览器当前标签页设置 user agent只在当前 tab 有效。
| 参数名称 | 类型 | 默认值 | 说明 |
|:----:|:-----:|:---:| -------------- |
| `ua` | `str` | 必填 | user agent 字符串 |
**返回:**`None`
---
## 📍 `set_headers()`
此方法用于设置额外添加到当前页面请求 headers 的参数。
| 参数名称 | 类型 | 默认值 | 说明 |
|:---------:|:------:|:---:| ----------------- |
| `headers` | `dict` | 必填 | `dict`形式的 headers |
**返回:**`None`
**示例:**
```python
h = {'connection': 'keep-alive', 'accept-charset': 'GB2312,utf-8;q=0.7,*;q=0.7'}
page.set_headers(headers=h)
```
---
# ✔️ 窗口管理
## 📍 调整大小和位置
`set_window`属性返回一个`WindowSetter`对象,调用其方法执行改变窗口状态。
| 方法 | 参数 | 说明 |
|:---------------------:|:----------------:| ---- |
| `maximized()` | 无 | 最大化 |
| `minimized()` | 无 | 最小化 |
| `fullscreen()` | 无 | 全屏 |
| `normal()` | 无 | 常规 |
| `size(width, height)` | 宽,高 | 设置大小 |
| `location(x, y)` | 屏幕坐标,左上角为 (0, 0) | 设置位置 |
```python
# 窗口最大化
page.set_window.maximized()
# 窗口全屏,即 F11
page.set_window.fullscreen()
# 恢复普通窗口
page.set_window.normal()
# 设置窗口大小
page.set_window.size(500, 500)
# 设置窗口位置
page.set_window.location(200, 200)
```
---
## 📍 隐藏和显示窗口
`hide_browser()``show_browser()`方法用于随时隐藏和显示浏览器窗口。
与 headless 模式不一样,这两个方法是直接隐藏和显示浏览器进程。在任务栏上也会消失。
只支持 Windows 系统,并且必须已安装 pypiwin32 库才可使用。
🔸 `hide_browser()`
此方法用于隐藏当前浏览器窗口。
**参数:** 无
**返回:**`None`
---
🔸 `show_browser()`
此方法用于显示当前浏览器窗口。
**参数:** 无
**返回:**`None`
**示例:**
```python
page.hide_browser()
```
!>**注意:**<br>- 浏览器隐藏后并没有关闭,下次运行程序还会接管已隐藏的浏览器<br>- 浏览器隐藏后,如果有新建标签页,会自行显示出来
---
# ✔️ 滚动页面
## 📍 `scroll`
此属性用于以某种方式滚动页面。
调用时返回一个`Scroll`对象,调用其方法实现页面各种方式的滚动。
| 方法 | 参数 | 功能 |
| ------------------- | ------------------- | ---------------- |
| `to_top()` | 无 | 滚动到顶端,水平位置不变 |
| `to_bottom()` | 无 | 滚动到底端,水平位置不变 |
| `to_half()` | 无 | 滚动到垂直中间位置,水平位置不变 |
| `to_rightmost()` | 无 | 滚动到最右边,垂直位置不变 |
| `to_leftmost()` | 无 | 滚动到最左边,垂直位置不变 |
| `to_location(x, y)` | 滚动条坐标值,左上角为 (0 , 0) | 滚动到指定位置 |
| `up(pixel)` | 滚动的像素 | 向上滚动若干像素,水平位置不变 |
| `down(pixel)` | 滚动的像素 | 向下滚动若干像素,水平位置不变 |
| `right(pixel)` | 滚动的像素 | 向左滚动若干像素,垂直位置不变 |
| `left(pixel)` | 滚动的像素 | 向右滚动若干像素,垂直位置不变 |
```python
# 页面滚动到底部
page.scroll.to_bottom()
# 页面滚动到最右边
page.scroll.to_rightmost()
# 页面向下滚动 200 像素
page.scroll.down(200)
# 滚动到指定位置
page.scroll.to_location(100, 300)
```
---
## 📍 `scroll_to_see()`
此方法用于滚动页面直到元素可见。
| 参数名称 | 类型 | 默认值 | 说明 |
|:------------:|:-------------------------------------:|:---:| ----------------- |
| `loc_or_ele` | `str`<br>`tuple`<br>`ChromiumElement` | 必填 | 元素的定位信息,可以是元素、定位符 |
**返回:**`None`
**示例:**
```python
# 滚动到某个已获取到的元素
ele = page.ele('tag:div')
page.scroll_to_see(ele)
# 滚动到按定位符查找到的元素
page.scroll_to_see('tag:div')
```
---
# ✔️ 处理弹出消息
## 📍 `handle_alert()`
此方法 用于处理提示框。
它能够设置等待时间,等待提示框出现才进行处理,若超时没等到提示框,返回`None`
也可只获取提示框文本而不处理提示框。
| 参数名称 | 类型 | 默认值 | 说明 |
|:---------:|:----------------:|:------:| ------------------------------------------ |
| `accept` | `bool`<br>`None` | `True` | `True`表示确认,`False`表示取消,`None`不会按按钮但依然返回文本值 |
| `send` | `str` | `None` | 处理 prompt 提示框时可输入文本 |
| `timeout` | `int`<br>`float` | `None` | 等待提示框出现的超时时间,为`None`时使用页面整体超时时间 |
| 返回类型 | 说明 |
|:------:| --------------- |
| `str` | 提示框内容文本 |
| `None` | 未等到提示框则返回`None` |
**示例:**
```python
# 确认提示框并获取提示框文本
txt = page.handle_alert()
# 点击取消
page.handle_alert(accept=False)
# 给 prompt 提示框输入文本并点击确定
paeg.handle_alert(accept=True, send='some text')
# 不处理提示框,只获取提示框文本
txt = page.handle_alert(accept=None)
```
---
# ✔️ 关闭浏览器
## 📍 `quit()`
此方法用于关闭浏览器。
**参数:** 无
**返回:**`**None`
```python
page.quit()
```

View File

@ -1,421 +0,0 @@
浏览器页面对象获取元素对象的方式,与`SessionPage`获取元素对象的方法是一致的,但比后者有更多功能。本节重点介绍其独有功能,与`SessionPage`一致的部分,请查看“收发数据包》查找元素”章节。
# ✔️ 查找元素方法
## 📍 查找单个元素
🔸 `ele()`
页面对象和元素对象都拥有此方法,用于查找第一个匹配条件的元素。
页面对象和元素对象的`ele()`方法参数名称稍有不同,但用法一样。
查找元素内置了等待元素出现功能,默认跟随页面设置,也可以每次查找单独调整。
?>**Tips**<br>在元素对象的`ele()`方法中使用 xpath 可直接获取后代元素的属性。
| 参数名称 | 类型 | 默认值 | 说明 |
|:------------------:|:-----------------------------------------------:|:------:| --------------------------------------------- |
| `loc_or_str`(元素对象) | `str`<br>`Tuple[str, str]` | 必填 | 元素的定位信息,可以是查询字符串,或 loc 元组 |
| `loc_or_ele`(页面对象) | `str`<br>`ChromiumElement`<br>`Tuple[str, str]` | 必填 | 元素的定位信息可以是查询字符串、loc 元组或一个`ChromiumElement`对象 |
| `timeout` | `int`<br>`float` | `None` | 查找元素等待时间,为`None`则使用页面对象`timeout`属性值 |
| 返回类型 | 说明 |
|:-----------------:| --------------------------------- |
| `ChromiumElement` | 返回查找到的第一个符合条件的元素对象 |
| `None` | 限时内未找到符合条件的元素时返回`None` |
| `str` | 在元素的`ele()`中使用 xpath可直接获取后代元素的属性 |
**示例:**
```python
from DrissionPage import ChromiumPage
page = ChromiumPage()
# 在页面内查找元素
ele1 = page.ele('#one')
# 在元素内查找后代元素
ele2 = ele1.ele('第二行')
# 使用 xpath 获取后代中第一个 div 元素的 class 属性(元素内查找可用)
ele_class = ele1.ele('xpath://p/@class')
```
---
## 📍 查找多个元素
🔸 `eles()`
此方法与`ele()`相似,但返回的是匹配到的所有元素组成的列表。
元素对象用 xpath 获取元素属性时,返回属性文本组成的列表。
| 参数名称 | 类型 | 默认值 | 说明 |
|:------------:|:--------------------------:|:------:| ----------------------------------- |
| `loc_or_str` | `str`<br>`Tuple[str, str]` | 必填 | 元素的定位信息,可以是查询字符串,或 loc 元组 |
| `timeout` | `int`<br>`float` | `None` | 查找元素等待时间,为`None`则使用页面对象`timeout`属性值 |
| 返回类型 | 说明 |
|:-----------------------:| ---------------------------------- |
| `List[ChromiumElement]` | 返回找到的所有`ChromiumElement`组成的列表 |
| `List[str]` | 在元素的`eles()`中使用 xpath可直接获取后代元素的属性 |
**示例:**
```python
# 获取ele元素内的所有p元素
p_eles = ele.eles('tag:p')
# 打印第一个p元素的文本
print(p_eles[0].text)
```
---
## 📍 查找单个静态元素
静态元素即 s 模式的`SessionElement`
元素对象,是纯文本构造的,因此用它处理速度非常快。对于复杂的页面,要在成百上千个元素中采集数据时,转换为静态元素可把速度提升几个数量级。作者曾在采集的时候,用同一套逻辑,仅仅把元素转换为静态,就把一个要 30
秒才采集完成的页面,加速到零点几秒完成。
我们甚至可以把整个页面转换为静态元素,再在其中提取信息。
当然,这种元素不能进行点击等交互。
`s_ele()`可在把查找到的动态元素转换为静态元素输出,或者获取元素或页面本身的静态元素副本。
🔸 `s_ele()`
页面对象和元素对象都拥有此方法,用于查找第一个匹配条件的元素,获取其静态版本。
页面对象和元素对象的`s_ele()`方法参数名称稍有不同,但用法一样。
| 参数名称 | 类型 | 默认值 | 说明 |
|:------------------:|:-----------------------------------------------:|:---:| --------------------------------------------- |
| `loc_or_str`(元素对象) | `str`<br>`Tuple[str, str]` | 必填 | 元素的定位信息,可以是查询字符串,或 loc 元组 |
| `loc_or_ele`(页面对象) | `str`<br>`ChromiumElement`<br>`Tuple[str, str]` | 必填 | 元素的定位信息可以是查询字符串、loc 元组或一个`ChromiumElement`对象 |
| 返回类型 | 说明 |
|:----------------:| --------------------------------- |
| `SessionElement` | 返回查找到的第一个符合条件的元素对象的静态版本 |
| `None` | 限时内未找到符合条件的元素时返回`None` |
| `str` | 在元素的`ele()`中使用 xpath可直接获取后代元素的属性 |
!>**注意:**<br>页面对象和元素对象的`s_ele()`方法不能搜索到在`<iframe>`里的元素,页面对象的静态版本也不能搜索`<iframe>`里的元素。要使用`<iframe>`
里元素的静态版本,可先获取该元素,再转换。而使用`ChromiumFrame`对象,则可以直接用`s_ele()`查找元素,这在后面章节再讲述。
?>**Tips**<br>从一个`ChromiumElement`元素获取到的`SessionElement`版本,依然能够使用相对定位方法定位祖先或兄弟元素。
```python
from DrissionPage import ChromiumPage
p = ChromiumPage()
# 在页面中查找元素,获取其静态版本
ele1 = page.s_ele('search text')
# 在动态元素中查找元素,获取其静态版本
ele = page.ele('search text')
ele2 = ele.s_ele()
# 获取页面元素的静态副本(不传入参数)
s_page = page.s_ele()
# 获取动态元素的静态副本
s_ele = ele.s_ele()
# 在静态副本中查询下级元素因为已经是静态元素用ele()查找结果也是静态)
ele3 = s_page.ele('search text')
ele4 = s_ele.ele('search text')
```
---
## 📍 查找多个静态元素
查找多个静态元素使用`s_eles()`方法。
此方法与`s_ele()`相似,但返回的是匹配到的所有元素组成的列表,或属性值组成的列表。
| 参数名称 | 类型 | 默认值 | 说明 |
|:------------:|:--------------------------:|:---:| ------------------------- |
| `loc_or_str` | `str`<br>`Tuple[str, str]` | 必填 | 元素的定位信息,可以是查询字符串,或 loc 元组 |
| 返回类型 | 说明 |
|:----------------------:| ---------------------------------- |
| `List[SessionElement]` | 返回找到的所有元素的`SessionElement`版本组成的列表 |
| `List[str]` | 在元素的`eles()`中使用 xpath可直接获取后代元素的属性 |
**示例:**
```python
from DrissionPage import WebPage
p = WebPage()
for ele in p.s_eles('search text'):
print(ele.text)
```
---
## 📍 获取当前焦点元素
使用`active_ele`属性获取页面上焦点所在元素。
```python
ele = page.active_ele
```
---
## 📍 获取 shadow_root
d 模式元素如果包含 shadow_root可使用`shadow_root`属性获取。
该属性返回的是一个`ChromiumShadowRootElement`对象,用法与`ChromiumElement`相似。也能使用各种元素查找方式,返回内部元素或相对位置元素,返回 `ChromiumElement`
元素。返回的`ChromiumElement`和普通的没有区别。
```python
# 获取一个元素下是 shadow root
shadow_ele = ele.shadow_root
# 获取该 shadow root 下的一个元素
ele1 = shadow_ele.ele('search text')
# 点击获取到的元素
ele1.click()
```
---
# ✔️ 查找语法
查找语法与`SessionPage`一致,此处只放个汇总表,详情请查看“收发数据包》查找元素”章节。
| 匹配符 | 说明 | 示例 |
|:--------:| ------------------- | --------------------------- |
| `=` | 精确匹配符 | `'@name=row1'` |
| `:` | 模糊匹配符 | `'@name:row'` |
| `#` | id 匹配符 | `'#one'` |
| `.` | class 匹配符 | `'.p_cls'` |
| `@` | 单属性匹配符 | `'@name=row1'` |
| `@@` | 多属性匹配符 | `'@@name:row@@class:p_cls'` |
| `text` | 文本匹配符 | `'text=第一行'` |
| `text()` | 与`@``@@`配合使用的文本匹配符 | `'@text()=第二行'` |
| `tag` | 类型匹配符 | `'tag:div'` |
| `css` | css selector 匹配符 | `'css:.div'` |
| `xpath` | xpath 匹配符 | `'xpath://div'` |
| loc 元组 | selenium 的定位元组 | `(By.ID, 'abc')` |
---
# ✔️ 相对定位
相对语法与`SessionPage`一致,此处只放个汇总表,详情请查看“收发数据包》查找元素”章节。
注意有以下两点差异:
- `timeout`参数是有效的,会等待目标元素出现
- 返回的类型是`ChromiumElement`而非`SessionElement`
| 方法 | 说明 |
|:-----------:| ------------------------------------------------------------------- |
| `parent()` | 此方法获取当前元素某一级父元素,可指定筛选条件或层数 |
| `next()` | 此方法返回当前元素后面的某一个同级元素,可指定筛选条件和第几个 |
| `nexts()` | 此方法返回当前元素后面全部符合条件的同级元素或节点组成的列表,可用查询语法筛选 |
| `prev()` | 此方法返回当前元素前面的某一个同级元素,可指定筛选条件和第几个 |
| `prevs()` | 此方法返回当前元素前面全部符合条件的同级元素或节点组成的列表,可用查询语法筛选 |
| `after()` | 此方法返回当前元素后面的某一个元素,可指定筛选条件和第几个。这个方法查找范围不局限在兄弟元素间,而是整个 DOM 文档 |
| `afters()` | 此方法返回当前元素后面符合条件的全部元素或节点组成的列表,可用查询语法筛选。这个方法查找范围不局限在兄弟元素间,而是整个 DOM 文档 |
| `before()` | 此方法返回当前元素前面的某一个元素,可指定筛选条件和第几个。这个方法查找范围不局限在兄弟元素间,而是整个 DOM 文档 |
| `befores()` | 此方法返回当前元素前面全部符合条件的元素或节点组成的列表,可用查询语法筛选。这个方法查找范围不局限在兄弟元素间,而是整个 DOM 文档 |
---
# ✔️ 等待
## 📍 等待元素加载
由于网络、js 运行时间的不确定性等因素,经常须要等待元素加载到 DOM 中才能使用。
l浏览器所有查找元素操作都自带等待时间默认跟随元素所在页面`timeout`属性(默认 10 秒),也可以在每次查找时单独设置,单独设置的等待时间不会改变页面原来设置。
```python
# 页面初始化时设置查找元素超时时间为 15 秒
page = ChromiumPage(timeout=15)
# 设置查找元素超时时间为 5 秒
page.timeout = 5
# 使用页面超时时间来查找元素5 秒)
ele1 = page.ele('search text')
# 为这次查找页面独立设置等待时间1 秒)
ele1 = page.ele('search text', timeout=1)
# 查找后代元素使用页面超时时间5 秒)
ele2 = ele1.ele('search text')
# 查找后代元素使用单独设置的超时时间1 秒)
ele2 = ele1.ele('some text', timeout=1)
```
---
## 📍 等待元素状态改变
有时候我们须要等待元素到达某种状态,如显示、隐藏、删除。页面对象和元素对象都内置了`wait_ele()`方法,用于等待元素状态变化。
该方法可接收现成的`ChromiumElement`对象,或定位符,再调用内置方法实现等待。
默认等待时间为页面对象的`timeout`值,也可以单独设定。
🔸 `wait_ele()`方法
| 参数名称 | 类型 | 默认值 | 说明 |
|:------------:|:-----------------------------------------------:|:------:| ----------------- |
| `loc_or_ele` | `str`<br>`Tuple[str, str]`<br>`ChromiumElement` | 必填 | 要等待的元素,可以是元素或定位符 |
| `timeout` | `int`<br>`float` | `None` | 等待超时时间,默认使用页面超时时间 |
| 内置方法 | 参数 | 返回 | 功能 |
|:-----------:|:---:|:--------------:| ------------ |
| `display()` | 无 | `bool`表示是否等待成功 | 等待元素从 DOM 显示 |
| `hidden()` | 无 | `bool`表示是否等待成功 | 等待元素从 DOM 隐藏 |
| `delete()` | 无 | `bool`表示是否等待成功 | 等待元素从 DOM 删除 |
**示例:**
```python
# 等待 id 为 div1 的元素显示,超时使用页面设置
page.wait_ele('#div1').display()
# 等待 id 为 div1 的元素被删除(使用 loc 元组设置超时3秒
ele.wait_ele('#div1', timeout=3).delete()
# 等待已获取到的元素被隐藏
ele2 = ele1.ele('#div1')
ele1.wait_ele(ele2).hidden()
```
---
# ✔️ 查找 iframe 里的元素
## 📍 在页面下跨级查找
与 selenium 不同,本库可以直接查找`<iframe>`里面的元素,而且无视层级,可以直接获取到多层`<iframe>`里的元素。无需切入切出,大大简化了程序逻辑,使用更便捷。
假设在页面中有个两级`<iframe>`,其中有个元素`<div id='abc'></div>`,可以这样获取:
```python
page = ChromiumPage()
ele = page('#abc')
```
获取前后无须切入切出,也不影响获取页面上其它元素。
如果用 selenium要这样写
```python
driver = webdriver.Chrome()
driver.switch_to.frame(0)
driver.switch_to.frame(0)
ele = driver.find_element(By.ID, 'abc')
driver.switch_to.default_content()
```
显然比较繁琐,而且切入到`<iframe>`后无法对`<iframe>`外的元素进行操作。
!>**注意:**<br>- 跨级查找只是页面对象支持,元素对象不能直接查找内部 iframe 里的元素。<br>- 跨级查找只能用于与主框架同域名的`<iframe>`,不同域名的请用下面的方法。
---
## 📍 在 iframe 元素下查找
本库把`<iframe>`看作一个特殊元素/页面对象看待,可以实现同时操作多个`<iframe>`,而无须来回切换。
对于跨域名的`<iframe>`,我们无法通过页面直接查找里面的元素,可以先获取到`<iframe>`元素,再在其下查找。当然,非跨域`<iframe>`也可以这样操作。
假设一个`<iframe>`的 id 为 `'iframe1'`,要在其中查找一个 id 为`'abc'`的元素:
```python
page = ChromiumPage()
iframe = page('#iframe1')
ele = iframe('#abc')
```
这个`<iframe>`元素是一个页面对象,因此可以继续在其下进行跨`<iframe>`查找(相对这个`<iframe>`不跨域的)。
---
# ✔️ `ShadowRootElement`相关查找
本库把 shadow-root 也作为元素对象看待,是为`ChromiumShadowRootElement`对象。该对象可与普通元素一样查找下级元素和 DOM 内相对定位。
`ChromiumShadowRootElement`对象进行相对定位时,把它看作其父对象内部的第一个对象,其余定位逻辑与普通对象一致。
!> **注意:** <br>如果`ChromiumShadowRootElement`元素的下级元素中有其它`ChromiumShadowRootElement`元素,那这些下级`ChromiumShadowRootElement`
元素内部是无法直接通过定位语句查找到的,只能先定位到其父元素,再用`shadow-root`属性获取。
```python
# 获取一个 shadow-root 元素
sr_ele = page.ele('#app').shadow_root
# 在该元素下查找下级元素
ele1 = sr_ele.ele('tag:div')
# 用相对定位获取其它元素
ele1 = sr_ele.parent(2)
ele1 = sr_ele.next('tag:div', 1)
ele1 = sr_ele.after('tag:div', 1)
eles = sr_ele.nexts('tag:div')
# 定位下级元素中的 shadow+-root 元素
sr_ele2 = sr_ele.ele('tag:div').shadow_root
```
由于 shadow-root 不能跨级查找,链式操作非常常见,所以设计了一个简写:`sr`,功能和`shadow_root`一样,都是获取元素内部的`ChromiumShadowRootElement`
多级 shadow-root 链式操作示例:
以下这段代码,可以打印浏览器历史第一页,可见是通过多级 shadow-root 来获取的。
```python
from DrissionPage import ChromiumPage
page = ChromiumPage()
page.get('chrome://history/')
items = page('#history-app').sr('#history').sr.eles('t:history-item')
for i in items:
print(i.sr('#item-container').text.replace('\n', ''))
```
---
# ✔️ 简化写法
为进一步精简代码,对语法进行了精简
- 定位语法都有其简化形式。
- 页面和元素对象都实现了`__call__()`方法,可直接调用。
- 所有查找方法都支持链式操作
示例:
```python
# 定位到页面中 id 为 table_id 的元素,然后获取它的所有 tr 元素
eles = page('#table_id').eles('t:tr')
# 定位到 class 为 cls 的元素,然后在它里面查找文本为 text 的元素
ele2 = ele1('.cls1')('tx=text')
# 获取 ele1 的 shadow_root 元素,再在里面查找 class 属性为 cls 的元素
ele2 = ele1.sr('.cls')
# 按xpath 查找元素
ele2 = ele1('x://div[@class="ele_class"]')
```
简化写法对应列表
| 原写法 | 简化写法 |
|:-------------:|:------:|
| `text` | `tx` |
| `text()` | `tx()` |
| `tag` | `t` |
| `xpath` | `x` |
| `css` | `c` |
| `shadow_root` | `sr` |

View File

@ -1,347 +0,0 @@
浏览器元素对应的对象是`ChromiumElement``ChromiumShadowRootElement`,本节介绍获取到元素对象后,如何获取其信息。
`ChromiumElement`拥有`SessionElement`所有属性,并有更多浏览器专属的信息。本节重点介绍如何获取浏览器专有的元素信息。
# ✔️ 与`SessionElement`共有信息
此处仅列出列表,具体用法请查看收发数据包部分的“获取元素信息”。
| 属性或方法 | 说明 |
|:------------:| -------------------------------- |
| `html` | 此属性返回元素的`outerHTML`文本 |
| `inner_html` | 此属性返回元素的`innerHTML`文本 |
| `tag` | 此属性返回元素的标签名 |
| `text` | 此属性返回元素内所有文本组合成的字符串 |
| `raw_text` | 此属性返回元素内原始文本 |
| `texts()` | 此方法返回元素内所有**直接**子节点的文本,包括元素和文本节点 |
| `comments` | 此属性以列表形式返回元素内的注释 |
| `attrs` | 此属性以字典形式返回元素所有属性及值 |
| `attr()` | 此方法返回元素某个`attribute`属性值 |
| `link` | 此方法返回元素的 href 属性或 src 属性 |
| `page` | 此属性返回元素所在的页面对象 |
| `xpath` | 此属性返回当前元素在页面中 xpath 的绝对路径 |
| `css_path` | 此属性返回当前元素在页面中 css selector 的绝对路径 |
---
# ✔️ 获取大小和位置
## 📍 `size`
**类型:**`Tuple[int, int]`
此属性以元组形式返回元素的大小。
```python
size = ele.size
# 返回:(50, 50)
```
---
## 📍 `location`
**类型:**`Tuple[int, int]`
此属性以元组形式返回元素**左上角**在**整个页面**中的坐标。
```python
loc = ele.location
# 返回:(50, 50)
```
---
## 📍 `client_location`
**类型:**`Tuple[int, int]`
此属性以元组形式返回元素**左上角**在**当前视口**中的坐标。
```python
loc = ele.client_location
# 返回:(50, 50)
```
---
## 📍 `midpoint`
**类型:**`Tuple[int, int]`
此属性以元组形式返回元素**中点**在**整个页面**中的坐标。
```python
loc = ele.midpoint
# 返回:(55, 55)
```
---
## 📍 `client_midpoint`
**类型:**`Tuple[int, int]`
此属性以元组形式返回元素**中点**在**视口**中的坐标。
```python
loc = ele.client_midpoint
# 返回:(55, 55)
```
---
# ✔️ 获取属性和内容
## 📍 `pseudo_before`
**类型:**`str`
此属性以文本形式返回当前元素的`::before`伪元素内容。
```python
before_txt = ele.pseudo_before
```
---
## 📍 `pseudo_after`
**类型:**`str`
此属性以文本形式返回当前元素的`::after`伪元素内容。
```python
after_txt = ele.pseudo_after
```
---
## 📍 `style()`
该方法返回元素 css 样式属性值,可获取伪元素的属性。它有两个参数,`style`参数输入样式属性名称,`pseudo_ele`参数输入伪元素名称,省略则获取普通元素的 css 样式属性。
| 参数名称 | 类型 | 默认值 | 说明 |
|:------------:|:-----:|:----:| --------- |
| `style` | `str` | 必填 | 样式名称 |
| `pseudo_ele` | `str` | `''` | 伪元素名称(如有) |
| 返回类型 | 说明 |
|:-----:| ----- |
| `str` | 样式属性值 |
**示例:**
```python
# 获取 css 属性的 color 值
prop = ele.style('color')
# 获取 after 伪元素的内容
prop = ele.style('content', 'after')
```
---
## 📍 `prop()`
此方法返回`property`属性值。它接收一个字符串参数,返回该参数的属性值。
| 参数名称 | 类型 | 默认值 | 说明 |
|:------:|:-----:|:---:| ---- |
| `prop` | `str` | 必填 | 属性名称 |
| 返回类型 | 说明 |
|:-----:| --- |
| `str` | 属性值 |
---
## 📍 `shadow_root`
**类型:**`ChromiumShadowRootElement`
此属性返回元素内的 shadow-root 对象,没有的返回`None`
---
# ✔️ 获取状态信息
## 📍`is_in_viewport`
**类型:**`bool`
此属性以布尔值方式返回元素是否在视口中,以元素可以接受点击的点为判断。
---
## 📍`is_alive`
**类型:**`bool`
此属性以布尔值形式返回当前元素是否仍可用。用于判断 d 模式下是否因页面刷新而导致元素失效。
---
## 📍 `is_selected`
**类型:**`bool`
此属性以布尔值返回元素是否选中。
---
## 📍 `is_enabled`
**类型:**`bool`
此属性以布尔值返回元素是否可用。
---
## 📍 `is_displayed`
**类型:**`bool`
此属性以布尔值返回元素是否可见。
---
# ✔️ 保存和截图
保存功能是本库一个特色功能,可以直接读取浏览器缓存,无须依赖另外的 ui 库或重新下载就可以保存页面资源。
作为对比selenium 无法自身实现图片另存,往往须要通过使用 ui 工具进行辅助,不仅效率和可靠性低,还占用键鼠资源。
## 📍 `get_src()`
此方法用于返回元素`src`属性所使用的资源。base64 的会转为`bytes`返回,其它的以`str`返回。无资源的返回`None`
例如,可获取页面上图片字节数据,用于识别内容,或保存到文件。`<script>`标签也可获取 js 文本。
!> **注意:**<br>无法获取 Blob 内容。
**参数:** 无
| 返回类型 | 说明 |
|:------:| ------------ |
| `str` | 资源字符串 |
| `None` | 无资源的返回`None` |
**示例:**
```python
img = page('tag:img')
src = img.get_src()
```
---
## 📍 `save()`
此方法用于保存`get_src()`方法获取到的资源到文件。
| 参数名称 | 类型 | 默认值 | 说明 |
|:--------:|:-------------------------:|:------:| ----------------------------- |
| `path` | `str`<br>`Path`<br>`None` | `None` | 文件保存路径,为`None`时保存到当前文件夹 |
| `rename` | `str`<br>`None` | `None` | 文件名称,须包含后缀,为`None`时从资源 url 获取 |
**返回:**`None`
**示例:**
```python
img = page('tag:img')
img.save('D:\\img.png')
```
---
## 📍 `get_screenshot()`
此方法用于对元素进行截图。若截图时元素在视口外,须 90 以上版本内核支持。
**参数:**
- `path`:图片完整路径,后缀可选`'jpg'``'jpeg'``'png'``'webp'`
- `as_bytes`:是否已字节形式返回图片,可选`'jpg'``'jpeg'``'png'``'webp'`。为`True`时以`'png'`输出。生效时`path`参数无效。
**返回:** 图片完整路径或字节文本
| 参数名称 | 类型 | 默认值 | 说明 |
|:----------:|:-------------------------:|:------:| --------------------------------------------------------------------------------------------------------- |
| `path` | `str`<br>`Path`<br>`None` | `None` | 保存图片的完整路径,文件后缀可选`'jpg'``'jpeg'``'png'``'webp'`<br>`None`时以 jpg 格式保存在当前文件夹 |
| `as_bytes` | `str`<br>`None`<br>`True` | `None` | 是否已字节形式返回图片,可选`'jpg'``'jpeg'``'png'``'webp'``None``True`<br>不为`None``path`参数无效<br>`True`时选用 jpg 格式 |
| 返回类型 | 说明 |
|:-------:| -------------------------- |
| `bytes` | `as_bytes`生效时返回图片字节 |
| `str` | `as_bytes``None`时返回图片完整路径 |
**示例:**
```python
img = page('tag:img')
img.get_screenshot()
bytes_str = img.get_screenshot(as_bytes='png') # 返回截图二进制文本
```
---
# ✔️ `ChromiumShadowRootElement`属性
本库把 shadow dom 的`root`看作一个元素处理,可以获取属性,也可以执行其下级的查找,使用逻辑与`ChromiumElement`一致,但属性较之少,有如下这些:
## 📍 `tag`
**类型:**`str`
此属性返回元素标签名,即`'shadow-root'`
---
## 📍 `html`
**类型:**`str`
此属性返回`shadow_root`的 html 文本,由`<shadow_root></shadow_root>` 标签包裹。
---
## 📍 `inner_html`
**类型:**`str`
此属性返回`shadow_root`内部的 html 文本。
---
## 📍 `page`
**类型:**`ChromiumPage``ChromiumTab``ChromiumFrame``WebPage`
此属性返回元素所在页面对象。
---
## 📍 `parent_ele`
**类型:**`ChromiumElement`
此属性返回所依附的普通元素对象。
---
## 📍 `is_enabled`
**类型:**`bool`
`ChromiumElement`一致。
---
## 📍 `is_alive`
**类型:**`bool`
`ChromiumElement`一致。

View File

@ -1,551 +0,0 @@
本节介绍与浏览器元素的交互。浏览器元素对象为`ChromiumElement`
# ✔️ 点击元素
## 📍 `click()`
此方法用于点击元素。可选择模拟点击或 js 点击。并支持点击重试功能。
| 参数名称 | 类型 | 默认值 | 说明 |
|:--------------:|:--------------------------:|:-------:| ----------------------------------------------------------------------------------------------------------------------- |
| `by_js` | `bool` | `None` | 是否用 js 方式点击<br>`True`时直接用 js 点击,不重试;<br>`False`时只用模拟点击,且根据`retry`设置重试;<br>`None`时先用模拟点击并根据`retyr`设置重试,如全部失败,改用 js 点击 |
| `retry` | `bool` | `False` | 遇到其它元素遮挡时,是否重试 |
| `timeout` | `int`<br>`float` | `0.2` | 点击失败重试超时时间,为`None`时使用父页面`timeout`设置,`retry``True`时才生效 |
| `wait_loading` | `int`<br>`float`<br>`True` | `0` | 指定等待页面进入加载状态的超时时间,为`True`时等待 2 秒,默认 0 秒 |
| 返回类型 | 说明 |
|:------:| ------ |
| `bool` | 是否点击成功 |
点击重试的设计,可以用于检测页面上的遮罩层是否消失。遮罩层经常出现在 js 方式翻页的时候,它的覆盖会阻碍模拟点击,所以可以通过对其下面的元素不断重试点击,来判断遮罩层是否存在。
而如果直接使用 js 进行点击,则可无视任何遮挡,只要元素在 DOM 内,就能点击得到,这样可以根据须要灵活地对元素进行操作。
通常,点击链接后立刻获取新页面的元素,程序可自动等待元素加载,但若跳转前的页面拥有和跳转后页面相同定位符的元素,会导致过早获取元素,跳转后失效的问题。可以对`wait_loading`参数设置一个超时时间,点击后程序会等待页面进入
loading 状态,才会继续往下执行,从而避免上述问题。
**示例:**
```python
# 对ele元素进行点击
ele.click()
# 用js方式点击ele元素
ele.click(by_js=True)
# 假设遮罩层出现ele是遮罩层下方的元素
ele.click(by_js=False, retry=True, timeout = 10) # 不断重试点击,直到遮罩层消失,或到达 10 秒
ele.click(by_js=True) # 无视遮罩层,直接用 js 点击下方元素
```
---
## 📍 `click_at()`
此方法用于带偏移量点击元素,偏移量相对于元素左上角坐标。不传入`offset_x``offset_y`时点击元素中上部位置。
点击的目标不一定在元素上,可以传入负值,或大于元素大小的值,点击元素附近的区域。向右和向下为正值,向左和向上为负值。
| 参数名称 | 类型 | 默认值 | 说明 |
|:----------:|:-----:|:--------:| ------------------------------------- |
| `offset_x` | `int` | `None` | 相对元素左上角坐标的 x 轴偏移量,向下向右为正 |
| `offset_y` | `int` | `None` | 相对元素左上角坐标的 y 轴偏移量,向下向右为正 |
| `button` | `str` | `'left'` | 要点击的键,传入`'left'``'right'``'middle'` |
**返回:**`None`
**示例:**
```python
# 点击元素右上方 50*50 的位置
ele.click_at(50, -50)
# 点击元素上中部x 相对左上角向右偏移50y 保持在元素中点
ele.click_at(offset_x=50)
# 和 click() 相似,但没有重试功能
ele.click_at()
```
---
## 📍 `r_click()`
此方法实现右键单击元素。
**参数:** 无
**返回:**`None`
**示例:**
```python
ele.r_click()
```
---
## 📍 `r_click_at()`
此方法用于带偏移量右键点击元素,用法和`click_at()`相似。
| 参数名称 | 类型 | 默认值 | 说明 |
|:----------:|:-----:|:------:| ------------------------ |
| `offset_x` | `int` | `None` | 相对元素左上角坐标的 x 轴偏移量,向下向右为正 |
| `offset_y` | `int` | `None` | 相对元素左上角坐标的 y 轴偏移量,向下向右为正 |
**返回:**`None`
**示例:**
```python
# 点击距离元素左上角 50*50 的位置(位于元素内部)
ele.r_click_at(50, 50)
```
---
## 📍 `m_click()`
此方法实现中键单击元素。
**参数:** 无
**返回:**`None`
**示例:**
```python
ele.m_click()
```
---
# ✔️ 输入内容
## 📍 `clear()`
此方法用于清空元素文本,可选择模拟按键或 js 方式。
模拟按键方式会自动输入`ctrl-a-del`组合键来清除文本框js 方式则直接把元素`value`属性设置为`''`
| 参数名称 | 类型 | 默认值 | 说明 |
|:-------:|:------:|:-------:| ----------- |
| `by_js` | `bool` | `False` | 是否用 js 方式清空 |
**返回:**`None`
**示例:**
```python
ele.clear()
```
---
## 📍 `input()`
此方法用于向元素输入文本或组合键,也可用于输入文件路径到上传控件。可选择输入前是否清空元素。
| 参数名称 | 类型 | 默认值 | 说明 |
|:-------:|:------:|:-------:| ---------------------------------- |
| `vals` | `Any` | `False` | 文本值或按键组合<br>对文件上传控件时输入路径字符串或其组成的列表 |
| `clear` | `bool` | `True` | 输入前是否清空文本框 |
**返回:**`None`
?>**Tips:** <br>- 有些文本框可以接收回车代替点击按钮,可以直接在文本末尾加上`'\n'`<br>- 会自动把非`str`数据转换为`str`
**示例:**
```python
# 输入文本
ele.input('Hello world!')
# 输入文本并回车
ele.input('Hello world!\n')
```
---
## 📍 输入组合键
使用组合键或要传入特殊按键前,先要导入按键类`Keys`
```python
from DrissionPage import Keys
```
然后将组合键放在一个`tuple`中传入`input()`即可。
```python
ele.input((Keys.CTRL, 'a', Keys.DEL)) # ctrl+a+del
```
---
## 📍 上传文件
上传文件是用`input()``type`属性为`'file'``<input>`元素进行输入,把文件路径输入到元素即可,用法与输入文本一致。
稍有不同的是,无论`clear`参数是什么,都会清空原控件内容。
多文件上传控件,多个路径以`list``tuple`或以`\n`分隔的字符串传入。
```python
# 传入一个路径
ele.input('D:\\test1.txt')
# 传入多个路径,方式 1
paths = 'D:\\test1.txt\nD:\\test2.txt'
ele.input(paths)
# 传入多个路径,方式 2
paths = ['D:\\test1.txt', 'D:\\test2.txt']
ele.input(paths)
```
---
# ✔️ 拖拽和悬停
?>**Tips:** <br>除了以下方法,本库还提供更灵活的动作链`ActionChains`功能,详见后面章节。
## 📍 `drag()`
此方法用于拖拽元素到相对于当前的一个新位置,可以设置速度,可以选择是否随机抖动。
| 参数名称 | 类型 | 默认值 | 说明 |
|:----------:|:------:|:------:| ---------------- |
| `offset_x` | `int` | `0` | x 轴偏移量,向下向右为正 |
| `offset_y` | `int` | `0` | y 轴偏移量,向下向右为正 |
| `speed` | `int` | `40` | 拖动的速度,传入 0 即瞬间到达 |
| `shake` | `bool` | `True` | 是否随机抖动 |
**返回:**`None`
**示例:**
```python
# 拖动当前元素到距离 50*50 的位置,速度为 100不随机抖动
ele.drag(50, 50, 100, False)
```
---
## 📍 `drag_to()`
此方法用于拖拽元素到另一个元素上或一个坐标上。
| 参数名称 | 类型 | 默认值 | 说明 |
|:------------:|:--------------------------------------:|:------:| ---------------- |
| `ele_or_loc` | `ChromiumElement`<br>`Tuple[int, int]` | 必填 | 另一个元素对象或坐标元组 |
| `speed` | `int` | `40` | 拖动的速度,传入 0 即瞬间到达 |
| `shake` | `bool` | `True` | 是否随机抖动 |
**返回:**`None`
**示例:**
```python
# 把 ele1 拖拽到 ele2 上
ele1 = page.ele('#div1')
ele2 = page.ele('#div2')
ele1.drag_to(ele2)
# 把 ele1 拖拽到网页 50, 50 的位置
ele1.drag_to((50, 50))
```
---
## 📍 `hover()`
此方法用于模拟鼠标悬停在元素上,可接受偏移量,偏移量相对于元素左上角坐标。不传入`offset_x``offset_y`值时悬停在元素中点。
| 参数名称 | 类型 | 默认值 | 说明 |
|:----------:|:-----:|:------:| ------------------------ |
| `offset_x` | `int` | `None` | 相对元素左上角坐标的 x 轴偏移量,向下向右为正 |
| `offset_y` | `int` | `None` | 相对元素左上角坐标的 y 轴偏移量,向下向右为正 |
**返回:**`None`
**示例:**
```python
# 悬停在元素右上方 50*50 的位置
ele.hover(50, -50)
# 悬停在元素上中部x 相对左上角向右偏移50y 保持在元素中点
ele.hover(offset_x=50)
# 悬停在元素中点
ele.hover()
```
---
# ✔️ 修改元素
## 📍 `set_innerHTML()`
此方法用于设置元素的 innerHTML 内容。
| 参数名称 | 类型 | 默认值 | 说明 |
|:------:|:-----:|:---:| ------ |
| `html` | `str` | 必填 | html文本 |
**返回:**`None`
---
## 📍 `set_prop()`
此方法用于设置元素`property`属性。
| 参数名称 | 类型 | 默认值 | 说明 |
|:-------:|:-----:|:---:| --- |
| `prop` | `str` | 必填 | 属性名 |
| `value` | `str` | 必填 | 属性值 |
**返回:**`None`
**示例:**
```python
ele.set_prop('value', 'Hello world!')
```
---
## 📍 `set_attr()`
此方法用于设置元素`attribute`属性。
| 参数名称 | 类型 | 默认值 | 说明 |
|:-------:|:-----:|:---:| --- |
| `attr` | `str` | 必填 | 属性名 |
| `value` | `str` | 必填 | 属性值 |
**返回:**`None`
**示例:**
```python
ele.set_attr('href', 'http://www.gitee.com')
```
---
## 📍 `remove_attr()`
此方法用于删除元素`attribute`属性。
| 参数名称 | 类型 | 默认值 | 说明 |
|:------:|:-----:|:---:| --- |
| `attr` | `str` | 必填 | 属性名 |
**返回:**`None`
**示例:**
```python
ele.remove_attr('href')
```
---
# ✔️ 执行 js 脚本
## 📍 `run_js()`
此方法用于对元素执行 js 代码,代码中用`this`表示元素自己。
| 参数名称 | 类型 | 默认值 | 说明 |
|:---------:|:------:|:-------:| ----------------------------------------------- |
| `script` | `str` | 必填 | js 脚本文本 |
| `as_expr` | `bool` | `False` | 是否作为表达式运行,为`True``args`参数无效 |
| `*args` | - | 无 | 传入的参数按顺序在js文本中对应`argument[0]``argument[1]`... |
| 返回类型 | 说明 |
|:-----:| ------ |
| `Any` | 脚本执行结果 |
!>**注意:**<br>要获取 js 结果记得写上`return`
**示例:**
```python
# 用执行 js 的方式点击元素
ele.run_js('this.click();')
# 用 js 获取元素高度
height = ele.run_js('return this.offsetHeight;')
```
---
## 📍 `run_async_script()`
此方法用于以异步方式执行 js 代码,代码中用`this`表示元素自己。
| 参数名称 | 类型 | 默认值 | 说明 |
|:---------:|:------:|:-------:| ----------------------------------------------- |
| `script` | `str` | 必填 | js 脚本文本 |
| `as_expr` | `bool` | `False` | 是否作为表达式运行,为`True``args`参数无效 |
| `*args` | - | 无 | 传入的参数按顺序在js文本中对应`argument[0]``argument[1]`... |
**返回:**`None`
---
# ✔️ 元素滚动
## 📍 `scroll`
此属性用于以某种方式滚动元素中的滚动条。
调用此属性返回一个`ChromiumScroll`对象,调用该对象的方法实现各种方式的滚动。
| 方法 | 参数说明 | 功能 |
|:-------------------:|:------:|:----------------:|
| `to_top()` | 无 | 滚动到顶端,水平位置不变 |
| `to_bottom()` | 无 | 滚动到底端,水平位置不变 |
| `to_half()` | 无 | 滚动到垂直中间位置,水平位置不变 |
| `to_rightmost()` | 无 | 滚动到最右边,垂直位置不变 |
| `to_leftmost()` | 无 | 滚动到最左边,垂直位置不变 |
| `to_location(x, y)` | 滚动条坐标值 | 滚动到指定位置 |
| `up(pixel)` | 滚动的像素 | 向上滚动若干像素,水平位置不变 |
| `down(pixel)` | 滚动的像素 | 向下滚动若干像素,水平位置不变 |
| `right(pixel)` | 滚动的像素 | 向左滚动若干像素,垂直位置不变 |
| `left(pixel)` | 滚动的像素 | 向右滚动若干像素,垂直位置不变 |
```python
# 滚动到底部
ele.scroll.to_bottom()
# 滚动到最右边
ele.scroll.to_rightmost()
# 向下滚动 200 像素
ele.scroll.down(200)
# 滚动到指定位置
ele.scroll.to_location(100, 300)
```
---
# ✔️ 列表选择
## 📍 `select`
此属性用于对`<select>`元素的操作。非`<select>`元素此属性为`None`
调用此属性时返回一个`ChromiumSelect`对象,调用该对象的方法实现列表项的选中与取消。
假设有以下`<select>`元素,下面示例以此为基础:
```html
<select id='s' multiple>
<option value='value1'>text1</option>
<option value='value2'>text2</option>
<option value='value3'>text3</option>
</select>
```
该对象实现了`__call__()`方法,可直接调用进行按文本选择项目。
```python
ele = page.ele('#s')
ele.select('text1') # 选中文本为 'text1' 的项目
```
---
## 📍 对象方法
| 方法 | 参数说明 | 功能 |
|:---------------------------------:|:-------:|:--------------:|
| `by_text(text, timeout)` | 文本,超时时间 | 根据文本选择项 |
| `by_value(value, timeout)` | 项值,超时时间 | 根据值选择项 |
| `by_index(index, timeout)` | 序号,超时时间 | 根据序号选择项0开始 |
| `cancel_by_text(text, timeout)` | 文本,超时时间 | 根据文本取消选择(多选列表) |
| `cancel_by_value(value, timeout)` | 项值,超时时间 | 根据项值取消选择(多选列表) |
| `cancel_by_index(index, timeout)` | 序号,超时时间 | 根据序号取消选择(多选列表) |
| `invert()` | 无 | 反选(多选列表) |
| `clear()` | 无 | 清空列表(多选列表) |
**示例:**
```python
ele.select.by_text('text1') # 和 ele.select('text1') 一样
ele.select.by_value('value2') # 选中 value 属性为 'value2' 的项
ele.select.by_index(2) # 选中第 3 项
ele.select.cancel_by_text('text1') # 取消选中文本为 'text1' 的项
ele.select.cancel_by_value('value2') # 取消选中 value 属性为 'value2' 的项
ele.select.cancel_by_index(2) # 取消选中第 3 项
ele.invert() # 反选
ele.clear() # 清空
```
---
## 📍 `ChromiumSelect`对象属性
| 属性 | 类型 | 说明 |
|:------------------:|:-----------------------:| ------------------------- |
| `is_multi` | `bool` | 返回是否多选表单 |
| `options` | `List[ChromiumElement]` | 返回所有选项元素组成的列表 |
| `selected_option` | `ChromiumElement` | 返回第一个被选中的`<option>`元素 |
| `selected_options` | `List[ChromiumElement]` | 返回所有被选中的`<option>`元素组成的列表 |
**示例:**
```python
print(ele.select.is_multi)
```
**输出:**
```console
True
```
---
## 📍 多选
上述各种选择/取消选择的方法均支持多选下拉列表。
要选择/取消选择多项,只要传入相应内容组成的`tuple``list`即可。
```python
# 选择多个文本项
ele.select(('text1', 'text2'))
# 选择多个值
ele.select.by_value(('value1', 'value2'))
# 取消选择多个序号
ele.select.cancel_by_index((0, 2))
```
---
## 📍 等待
很多网站下拉列表采用 js 加载,如果加载不及时会导致异常。
因此本库在此集成了一个贴心小功能,上面各种方法均设置了`timeout`参数,如果选择目标未找到,会在限时内等待该项出现,超时就返回`False`
```python
# 目标选择 'abc',设置超时时间为 3 秒
print(ele.select('abc', 3))
```
**输出:**
```console
False
```

View File

@ -1,314 +0,0 @@
本节介绍对浏览器标签页的管理及使用技巧。
与 selenium 不同DrissionPage 能够用多个标签页对象同时操作多个标签页,而无须切入切出。并且,标签页无须在激活状态也可以控制。因此能够实现一些非常灵活的使用方式。
比如,多线程分别独立控制标签页,或一个总标签页,控制多个从标签页,或者配合插件实现浏览器随时更换代理等。
`ChromiumPage``WebPage`对象为浏览器标签页总管,可以控制标签页的增删。`ChromiumTab`对象为独立的标签页对象,可控制每个标签页内的操作。
# ✔️ 标签页总览
## 📍 `tabs_count`
**类型:**`int`
此属性返回标签页数量。
```python
print(page.tabs_count)
```
**输出:**
```console
2
```
---
## 📍 `tabs`
**类型:**`List[str]`
此属性以`list`方式返回所有标签页 id。
```python
print(page.tabs)
```
**输出:**
```console
['0B300BEA6F1F1F4D5DE406872B79B1AD', 'B838E91F38121B32940B47E8AC59D015']
```
---
# ✔️ 新建标签页
## 📍 `new_tab()`
该方法用于新建一个标签页,该标签页在最后面。
| 参数名称 | 类型 | 默认值 | 说明 |
|:-----------:|:---------------:|:------:| -------------------- |
| `url` | `str`<br>`None` | `None` | 新标签页访问的网址,不传入则新建空标签页 |
| `switch_to` | `bool` | `True` | 新建标签页后是否把焦点移过去 |
**返回:**`None`
**示例:**
```python
page.new_tab(url='https://www.baidu.com')
```
---
# ✔️ 关闭标签页
## 📍 `close_tabs()`
此方法用于关闭指定的标签页,可关闭多个。默认关闭当前的。
如果被关闭的标签页包含当前页,会切换到剩下的第一个页面,但未必是视觉上第一个。
| 参数名称 | 类型 | 默认值 | 说明 |
|:---------:|:----------------------------------------------:|:------:| ---------------------------------------- |
| `tab_ids` | `str`<br>`None`<br>`List[str]`<br>`Tuple[str]` | `None` | 要处理的标签页 id可传入 id 组成的列表或元组,为`None`时处理当前页 |
| `others` | `bool` | `True` | 是否关闭指定标签页之外的 |
**返回:**`None`
**示例:**
```python
# 关闭当前标签页
page.close_tabs()
# 关闭第1、3个标签页
tabs = page.tabs
page.close_tabs(tab_ids=(tabs[0], tabs[2]))
```
---
## 📍 `close_other_tabs()`
此方法用于关闭传入的标签页以外标签页,默认保留当前页。可传入多个。
如果被关闭的标签页包含当前页,会切换到剩下的第一个页面,但未必是视觉上第一个。
| 参数名称 | 类型 | 默认值 | 说明 |
|:---------:|:----------------------------------------------:|:------:| ---------------------------------------- |
| `tab_ids` | `str`<br>`None`<br>`List[str]`<br>`Tuple[str]` | `None` | 要处理的标签页 id可传入 id 组成的列表或元组,为`None`时处理当前页 |
**返回:**`None`
**示例:**
```python
# 关闭除当前标签页外的所有标签页
page.close_other_tabs()
# 关闭除第一个以外的标签页
page.close_other_tabs(page.tab[0])
# 关闭除指定id以外的标签页
reserve_list = ('0B300BEA6F...', 'B838E91...')
page.close_other_tabs(reserve_list)
```
---
# ✔️ 切换标签页
## 📍 `main_tab`
**类型:**`str`
日常使用时,经常会用一个标签页作为主标签页,产生众多临时标签页去进行操作。因此我们可以为每个`ChromiumPage``WebPage`对象设置一个标签页为主标签页,方便随时切换。
默认接管浏览器时活动标签页则为主标签页。
**示例:**
```python
print(page.main_tab)
```
**输出:**
```console
'0B300BEA6F1F1F4D5DE406872B79B1AD'
```
---
## 📍 `lastest_tab`
**类型:**`str`
此属性返回最新的标签页。最新的标签页是指最新出现或最新被激活的标签页。
**示例:**
```python
# 打开了一个标签页
ele.click()
# 切换到最新打开的标签页
page.to_tab(page.lastest_tab)
```
---
## 📍 `set_main_tab()`
此方法用于设置某个标签页为主标签页。
| 参数名称 | 类型 | 默认值 | 说明 |
|:--------:|:---------------:|:------:| ---------------------- |
| `tab_id` | `str`<br>`None` | `None` | 要设置的标签页 id默认设置当前标签页为主 |
**返回:**`None`
**示例:**
```python
# 指定一个标签页为主标签页
page.set_main_tab(tab_id='0B300BEA6F1F1F4D5DE406872B79B1AD')
# 设置当前控制的标签页为主标签页
page.set_main_tab()
```
---
## 📍`to_main_tab()`
此方法用于把焦点定位到主标签页,使当前对象控制目标改为主标签页。
**参数:** 无
**返回:**`None`
**示例:**
```python
page.to_main_tab()
```
---
## 📍 `to_tab()`
此方法把焦点定位到某个标签页,使当前对象控制目标改为该标签页。
| 参数名称 | 类型 | 默认值 | 说明 |
|:----------:|:---------------:|:------:| ----------------------- |
| `tab_id` | `str`<br>`None` | `None` | 标签页 id默认为`None`切换到主标签页 |
| `activate` | `bool` | `True` | 切换后是否变为活动状态 |
**返回:**`None`
**示例:**
```python
# 切换到主标签页
page.to_tab()
# 切换到第一个标签页
page.to_tab(page.tabs[0])
# 切换到id为该字符串的标签页
page.to_tab('0B300BEA6F1F1F4D5DE406872B79B1AD')
```
---
## 📍 `to_front()`
此方法用于激活当前标签页使其处于最前面。标签页无须在活动状态程序也能操控。
**参数:** 无
**返回:**`None`
---
# ✔️ 多标签页协同
## 📍 获取标签页对象
可以用`WebPage``ChromiumPage``get_tab()`方法获取标签页对象,然后可以使用这个对象对标签页进行操作。
🔸 `get_tab()`
| 参数名称 | 类型 | 默认值 | 说明 |
|:--------:|:---------------:|:------:| --------------------------- |
| `tab_id` | `str`<br>`None` | `None` | 要获取的标签页 id默认为`None`获取当前标签页 |
| 返回类型 | 说明 |
|:-------------:| ----- |
| `ChromiumTab` | 标签页对象 |
**示例:**
```python
tab = page.get_tab() # 获取当前标签页对象
```
---
## 📍 使用标签页对象
每个`ChromiumTab`对象控制一个浏览器标签页,方法和直接使用`ChromiumPage`一致,只比`ChromiumPage`少了控制标签页功能。
```python
tab.get('https://www.baidu.com') # 使用标签页对象
```
---
## 📍 控制多标签页协作示例
做自动化的时候,我们经常会遇到这样一种场景:我们有一个列表页,须要逐个点开里面的链接,获取新页面的内容,每个链接会打开一个新页面。
如果用 selenium 来做,点击一个链接后必须把焦点切换到新标签页,采集信息后再回到原来的页面,点击下一个链接,但由于焦点的切换,原来的元素信息已丢失,我们只能重新获取所有链接,以计数方式点击下一个,非常不优雅。
而用`ChromiumPage`,点开标签页后焦点无须移动,可直接生成一个新标签页的页面对象,对新页面进行采集,而原来列表页的对象可以继续操作下一个链接。甚至可以用多线程控制多个标签页,实现各种黑科技。
我们用 gitee 的推荐项目页面做个演示:[最新推荐项目 - Gitee.com](https://gitee.com/explore/all)
```python
from DrissionPage import ChromiumPage
page = ChromiumPage()
page.get('https://gitee.com/explore/all')
links = page.eles('t:h3')
for link in links[:-1]:
# 点击链接
link.click()
# 获取新标签页对象
new_tab = page.get_tab(page.latest_tab)
# 等待新标签页加载
new_tab.wait_loading()
# 打印标签页标题
print(new_tab.title)
# 关闭除列表页外所有标签页
page.close_other_tabs()
```
**输出:**
```console
wx-calendar: 原生小程序日历组件(可滑动,可标记,可禁用)
thingspanel-go: 开源插件化物联网平台Go语言开发。支持MQTT、Modbus多协议、多类型设备接入与可视化、自动化、告警、规则引擎等功能。 QQ群371794256。
APITable: vika.cn维格表社区版地表至强的开源低代码、多维表格工具Airtable的开源免费替代。
ideaseg: 基于 NLP 技术实现的中文分词插件,准确度比常用的分词器高太多,同时提供 ElasticSearch 和 OpenSearch 插件。
vue-plugin-hiprint: hiprint for Vue2/Vue3 ⚡打印、打印设计、可视化设计器、报表设计、元素编辑、可视化打印编辑
ExDUIR.NET: Windows平台轻量DirectUI框架
后面省略。。。
```

View File

@ -1,383 +0,0 @@
`<iframe>`元素是一种特殊的元素,它既是元素,也是页面,因此独立一个章节对其进行介绍。
与 selenium 不同DrissionPage 无需切入切出即可处理`<iframe>`元素。因此可实现跨级元素查找、元素内部单独跳转、同时操作`<iframe>`内外元素、多线程控制多个`<iframe>`等操作,功能更灵活,逻辑更清晰。
我们使用菜鸟教程在线编辑器来演示:
[菜鸟教程在线编辑器 (runoob.com)](https://www.runoob.com/try/try.php?filename=tryhtml_iframe)
源代码框内容要作一点调整,然后按“点击运行”:
```html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>菜鸟教程(runoob.com)</title>
</head>
<body>
<iframe id="sss" src="https://www.runoob.com">
<p>您的浏览器不支持 iframe 标签。</p>
</iframe>
</body>
</html>
```
`F12`,可以看到网页右侧是一个两层`<iframe>`,一个 id 是`'iframeResult'``<iframe>`里面有一个 id 是`'sss'``<iframe>`。最里层的`<iframe>`
页面指向 https://www.runoob.com。
---
# ✔ 获取`<iframe>`对象
现在我们获取最里层`<iframe>`对象。方法和直接获取元素一样:
```python
iframe = page('#sss')
print(iframe.html)
```
**输出:**
```console
<iframe id="sss" src="https://www.runoob.com"><html><head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>菜鸟教程 - 学的不仅是技术,更是梦想!</title>
<meta name="robots" content="max-image-preview:large">
下面省略。。。
```
这个`ChromiumFrame`对象既是页面也是元素,下面演示其功能。
---
# ✔ 查找`<iframe>`内元素
从刚才获取元素对象看出,我们并不须要先切入 id 为`'iframeResult'``<iframe>`,就可以获取到里面的元素。所以我们获取元素也并不一定要先获取到`ChromiumFrame`对象。
## 📍 在`<iframe>`内查找
使用我们刚才获取到的元素,可以在里面查找元素:
```python
ele = iframe('首页')
print(ele)
```
**输出:**
```console
<ChromiumElement a href='https://www.runoob.com/' data-id='index' title='菜鸟教程' class='current'>
```
---
## 📍 页面跨`<iframe>`查找
如果`<iframe>`元素的网址和主页面是同域的,我们可以直接用页面对象查找`<iframe>`内部元素,而无需先获取`ChromiumFrame`对象:
```python
ele = page('首页')
print(ele)
```
**输出:**
```console
<ChromiumElement a href='https://www.runoob.com/' data-id='index' title='菜鸟教程' class='current'>
```
只要是同域名的,无论跨多少曾`<iframe>`都能用页面对象直接获取。
---
## 📍 与 selenium 对比
`WebPage`
```python
from DrissionPage import WebPage
page = WebPage()
ele = page('首页')
```
`MixPage`(基于 selenium
```python
from DrissionPage import MixPage
page = MixPage()
page.to_frame('#iframeResult')
page.to_frame('#sss')
ele = page('首页')
page.to_frame.main()
```
可见,原来的逻辑要切入切出,比较繁琐。
---
## 📍 重要事项
如果`<iframe>`跟当前标签页是不同域名的,不能使用页面对象直接查找其中元素,只能先获取其`ChromiumFrame`元素对象,再在这个对象中查找。
---
# ✔ `ChromiumFrame`的元素特征
正如上面所说,`ChromiumFrame`既是元素也是页面,这里说一下其元素方面的用法。
## 📍 `tab_id`
**类型:**`str`
此属性返回`<iframe>`元素所在标签页的 id。
---
## 📍 `tag`
**类型:**`str`
此属性返回元素名称。
---
## 📍 `html`
**类型:**`str`
此属性返回整个`<iframe>`元素的 outerHTML 文本。
---
## 📍 `inner_html`
**类型:**`str`
此属性返回 innerHTML 文本。
---
## 📍 `attrs`
**类型:**`dict`
此属性以`dict`形式返回元素所有 attribute 属性。
---
## 📍 `size`
**类型:**`Tuple[int, int]`
此属性以`tuple`形式返回元素大小。
---
## 📍 `location`
**类型:**`Tuple[int, int]`
此属性以`tuple`形式返回元素在主页面中的位置,左上角为 (0, 0)。
---
## 📍 `is_displayed`
**类型:**`bool`
此属性返回元素是否可见。
---
## 📍 `xpath`
**类型:**`str`
此属性返回元素在其页面上的 xpath 路径。
---
## 📍 `css_path`
**类型:**`str`
此属性返回元素在其页面上的 css selector 路径。
---
## 📍 `attr()`
此方法用于一个获取元素 attribute 属性。
| 参数名称 | 类型 | 默认值 | 说明 |
|:------:|:-----:|:---:| --- |
| `attr` | `str` | 必填 | 属性名 |
| 返回类型 | 说明 |
|:------:| ------------- |
| `str` | 属性值文本 |
| `None` | 没有该属性返回`None` |
---
## 📍 `set_attr()`
此方法用于设置元素的 attribute 属性。
| 参数名称 | 类型 | 默认值 | 说明 |
|:-------:|:-----:|:---:| --- |
| `attr` | `str` | 必填 | 属性名 |
| `value` | `str` | 必填 | 属性值 |
**返回:**`None`
---
## 📍 `remove_attr()`
此方法用于删除元素的 attribute 属性。
| 参数名称 | 类型 | 默认值 | 说明 |
|:------:|:-----:|:---:| --- |
| `attr` | `str` | 必填 | 属性名 |
**返回:**`None`
---
## 📍 相对定位
相对定位方法与普通元素一致,详见获取元素章节。
- `parent()`:返回上面某一级父元素。
- `prev()`:返回前面的一个兄弟元素。
- `next()`:返回后面的一个兄弟元素。
- `before()`:返回当前元素前面的一个元素。
- `after()`:返回当前元素后面的一个元素。
- `prevs()`:返回前面全部兄弟元素或节点组成的列表。
- `nexts()`:返回后面全部兄弟元素或节点组成的列表。
- `befores()`:返回当前元素后面符合条件的全部兄弟元素或节点组成的列表。
---
# ✔ `ChromiumFrame`的页面特征
## 📍 `url`
**类型:**`str`
此属性返回页面当前 url。
---
## 📍 `title`
**类型:**`str`
此属性返回页面当前 title 文本。
---
## 📍 `cookies`
**类型:**`dict`
此属性返回页面当前 cookies 内容。
---
## 📍 `get()`
此方法用于实现`<iframe>`页面跳转,使用方法与`ChromiumPage`一致。
```python
iframe.get('https://www.runoob.com/css3/css3-tutorial.html')
```
---
## 📍 `refresh()`
此方法用于刷新页面。
**参数**
**返回:**`None`
```python
iframe.refresh()
```
---
## 📍 `ready_state`
**类型:**`str`
此属性为页面加载状态,包括`'loading'``'interactive'``'complete'`3 种。
---
## 📍 `is_loading`
**类型:**`bool`
此属性返回页面是否正在加载。
---
## 📍 `active_ele`
**类型:**`ChromiumElement`
此属性返回页面中焦点所在元素。
---
## 📍 `frame_size`
**类型:**`Tuple[int, int]`
此属性以`tuple`形式返回页面大小。
---
## 📍 `run_js()`
此方法用于在`<iframe>`内执行 js 脚本。
| 参数名称 | 类型 | 默认值 | 说明 |
|:---------:|:------:|:-------:| ----------------------------------------------- |
| `script` | `str` | 必填 | js 脚本文本 |
| `as_expr` | `bool` | `False` | 是否作为表达式运行,为`True``args`参数无效 |
| `*args` | - | 无 | 传入的参数按顺序在js文本中对应`argument[0]``argument[1]`... |
| 返回类型 | 说明 |
|:-----:| ------ |
| `Any` | 脚本执行结果 |
---
## 📍 `scroll`
`ChromiumFrame`的滚动方法与页面或元素是一致的。
**示例:** 使`<iframe>`元素向下滚动 300 像素
```python
iframe.scroll.down(300)
```

View File

@ -1,15 +0,0 @@
`WebPage`对象整合了`SessionPage``ChromiumPage`,实现了两者之间的互通。
它既可以操控浏览器,也可以收发数据包,并且会在两者之间同步登录信息。
它有 d 和 s 两种模式,分别对应操控浏览器和收发数据包。
`WebPage`可灵活的在两种模式间切换,从而实现一些有趣的用法。
比如,网站登录代码非常复杂,用数据包实现过于烧脑,我们可以用浏览器处理登录,再通过切换模式用收发数据包的方式去采集数据。
两种模式的使用逻辑是一致的,跟`ChromiumPage`没有区别,易于上手。
`WebPage`结构图:
![](https://gitee.com/g1879/DrissionPage/raw/master/docs/imgs/webpage.jpg)

View File

@ -1,103 +0,0 @@
本节介绍`WebPage`对象的创建。
`WebPage`对象拥有两种模式d 模式用于操控浏览器s 模式用于收发数据包。
# ✔️ `WebPage`初始化参数
| 初始化参数 | 类型 | 默认值 | 说明 |
|:--------------------:|:------------------------------------------------:|:------:| ----------------------------------------------------------------------------------------------------------------------------------------- |
| `mode` | `str` | `'d'` | 只能接收`'d'``'s'`,即初始选择操控浏览器还是收发数据包 |
| `timeout` | `float` | `None` | 整体超时时间,为`None`则从配置文件中读取,默认 10 |
| `driver_or_options` | `ChromiumDriver`<br>`ChromiumOptions`<br>`False` | `None` | 默认为`None`表示从 ini 文件读取配置进行初始化<br>接收`ChromiumDriver`时直接接管一个已打开的页面<br>接收`ChromiumOptions`时用该配置启动或接管浏览器<br>如不使用 d 模式功能,接收`False`,避免打包出错 |
| `session_or_options` | `Session`<br>`SessionOptions`<br>`False` | `None` | 默认为`None`表示从 ini 文件读取配置进行初始化<br>接收`Session`时直接使用一个已创建的`Session`对象<br>接收`SessionOptions`时用该配置创建`Session`对象<br>如不使用 s 模式功能,接收`False`,避免打包出错 |
---
# ✔️ 直接创建
这种方式代码最简洁,程序会从默认 ini 文件中读取配置,自动生成页面对象。
创建时,可指定初始模式。
```python
from DrissionPage import WebPage
# 默认d模式创建对象
page = WebPage()
# 指定s模式创建对象
page = WebPage('s')
```
d 模式创建`WebPage`对象时会在指定端口启动浏览器,或接管该端口已有浏览器。
默认情况下,程序使用 9222 端口,浏览器可执行文件路径为`'chrome'`。如路径中没找到浏览器可执行文件Windows 系统下程序会在注册表中查找路径。如果都没找到,则要用下一种方式手动配置。
s 模式创建`WebPage`不会打开或接管浏览器。
!>**注意:**<br>这种方式的程序不能直接打包,因为使用到 ini 文件。可参考“打包程序”一节的方法。
?>**Tips**<br>您可以修改配置文件中的配置,实现所有程序都按您的须要进行启动,详见”启动配置“章节。
---
# ✔️ 通过配置信息创建
如果须要已指定方式启动浏览器,可使用`ChromiumOptions``SessionOptions`。它们的使用在各自的章节已经介绍过,这里只演示如何在`WebPage`创建时使用。
## 📍 使用方法
创建两个配置对象后,传递给`WebPage`的初始化方法。
```python
from DrissionPage import WebPage, ChromiumOptions, SessionOptions
co = ChromiumOptions()
so = SessionOptions()
page = WebPage(driver_or_options=co, session_or_options=so)
```
如果只须要对一个模式的配置进行修改,另一个模式使用 ini 的配置,可以只传入一种配置对象。
```python
from DrissionPage import WebPage, ChromiumOptions
co = ChromiumOptions()
page = WebPage(driver_or_options=co)
```
---
## 📍 使用指定 ini 文件创建
以上方法是使用默认 ini 文件中保存的配置信息创建对象,你可以保存一个 ini 文件到别的地方,并在创建对象时指定使用它。
```python
from DrissionPage import WebPage, ChromiumOptinos, SessionOptions
co = ChromiumOptinos(ini_path=r'./config1.ini')
so = SessionOptions(ini_path=r'./config1.ini')
page = ChromiumPage(addr_driver_opts=co, session_or_options=so)
```
---
# ✔️ 传递控制权
当须要使用多个页面对象共同操作一个页面时,可在对象间传递驱动器。
!>**注意:**<br>传递控制权的时候,记得 driver 和 session 都要传递。
```python
page1 = WebPage()
# 获取页面对象的浏览器控制器
driver = page1.driver
# 获取页面对象的内置Session对象
session = page1.session
# 把内置对象在第二个页面对象初始化时传递进去
page2 = ChormiumPage(driver_or_options=driver, session_or_options=session)
```

View File

@ -1,97 +0,0 @@
本节介绍`WebPage`的模式切换功能。
`WebPage`的 d 模式,行为与`ChromiumPage`一致s 模式行为与`SessionPage`一致。
使用`change_mode()`方法进行切换。模式切换的时候会同步登录信息。
# ✔️ `mode`
**类型:**`str`
此属性返回`WebPage`当前模式。
**示例:**
```python
from DrissionPage import WebPage
page = WebPage()
print(page.mode)
```
**输出:**
```console
d
```
---
# ✔️ `change_mode()`
此方法用于切换`WebPage`运行模式。
| 参数名称 | 类型 | 默认值 | 说明 |
|:--------------:|:---------------:|:------:| ------------------------------------------------ |
| `mode` | `str`<br>`None` | `None` | 接收 's' 或 'd',以切换到指定模式<br>接收`None`则切换到与当前相对的另一个模式 |
| `go` | `bool` | `True` | 目标模式是否跳转到原模式的 url |
| `copy_cookies` | `bool` | `True` | 切换时是否复制 cookies 到目标模式 |
**返回:**`None`
---
# ✔️ 示例
## 📍 切换模式
```python
from DrissionPage import WebPage
page = WebPage()
page.get('https://www.baidu.com')
print(page.mode)
page.change_mode()
print(page.mode)
print(page.title)
```
**输出:**
```console
d
s
百度一下,你就知道
```
本示例中,执行操作如下:
- 初始d 模式访问百度
- 切换到 s 模式,此时会同步登录信息到 s 模式,且在 s 模式访问百度
- 打印 s 模式访问到的页面标题
---
## 📍 自动切换
在某个模式中使用另一个模式独有的方法,会自动切换模式。
!>**注意:**<br>这个功能还在调整中,后续版本表现可能和现在不一致,慎用。
```python
page = WebPage('s')
page.get('https://www.baidu.com')
print(page.mode)
print(page.size)
print(page.mode)
```
**输出:**
```python
s
(1250, 684)
d
```

View File

@ -1,57 +0,0 @@
本节介绍`Webpage`独有的功能。
`WebPage``ChromiumPage``SessionPage`的集成,因此拥有这两者全部功能。这些功能具体查看相关章节,这里只介绍`WebPage`独有的功能。
# ✔️ cookies 处理
## 📍 `cookies_to_session()`
此方法把浏览器的 cookies 复制到`session`对象。
| 参数名称 | 类型 | 默认值 | 说明 |
|:-----------------:|:------:|:------:| ------------------ |
| `copy_user_agent` | `bool` | `True` | 是否复制 user agent 信息 |
**返回:**`None`
---
## 📍 `cookies_to_session()`
此方法把`session`对象的 cookies 复制到浏览器。
**参数:** 无
**返回:**`None`
---
# ✔️ 关闭对象
## 📍 `close_driver()`
此方法关闭内置`ChromiumDriver`对象及浏览器,并切换到 s 模式。
**参数:** 无
**返回:**`None`
---
## 📍 `close_driver()`
此方法关闭内置`Session`对象及浏览器,并切换到 d 模式。
**参数:** 无
**返回:**`None`
---
## 📍 `quit()`
此方法彻底关闭内置的`Session`对象和`ChromiumDriver`对象,并关闭浏览器(如已打开)。
**参数:** 无
**返回:**`None`

View File

@ -1,108 +0,0 @@
本库使用 ini 文件记录浏览器或`Session`对象的启动配置。便于配置复用,免于在代码中加入繁琐的配置信息。
默认情况下,页面对象启动时自动加载文件中的配置信息。
也可以在默认配置基础上用简单的方法再次修改,再保存到 ini 文件。
也可以保存多个 ini 文件,按不同项目须要调用。
!> **注意:** <br>- ini 文件仅用于管理启动配置,页面对象创建后再修改 ini 文件是没用的。 <br>- 如果是接管已打开的浏览器,这些设置也没有用。 <br>- 每次升级本库ini
文件都会被重置,可另存到其它路径以免重置。
# ✔️ ini 文件内容
ini 文件初始内容如下。
```
[paths]
chromedriver_path =
download_path =
[chrome_options]
debugger_address = 127.0.0.1:9222
binary_location = chrome
arguments = ['--no-first-run', '--no-sandbox', '--disable-gpu', '--ignore-certificate-errors', '--disable-infobars', '--disable-popup-blocking']
extensions = []
experimental_options = {'prefs': {'profile.default_content_settings.popups': 0, 'profile.default_content_setting_values': {'notifications': 2}, 'plugins.plugins_list': [{'enabled': False, 'name': 'Chrome PDF Viewer'}]}, 'useAutomationExtension': False, 'excludeSwitches': ['enable-automation']}
page_load_strategy = normal
user = Default
auto_port = 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]
implicit = 10
page_load = 30
script = 30
[proxies]
http =
https =
```
---
# ✔️ 文件位置
默认配置文件存放在库文件夹下,文件名为 configs.ini。
用户可另存其它配置文件,或从另存的文件读取配置,但默认文件的位置和名称不会改变。
---
# ✔️ 使用默认配置文件启动
## 📍 使用页面对象自动加载
这是默认启动方式。
```python
from DrissionPage import WebPage
page = WebPage()
```
---
## 📍 使用配置对象加载
这种方式一般用于加载配置后须要进一步修改。
```python
from DrissionPage import ChromiumOptions, SessionOptions, WebPage
do = ChromiumOptions(ini_path=r'D:\setting.ini')
so = SessionOptions(ini_path=r'D:\setting.ini')
page = WebPage(driver_or_options=do, session_or_options=so)
```
---
## 📍 使用 Drission 对象加载
这种方式一般用于加载非默认配置文件,或须在多个页面对象间传递`Drission`对象的情况。这是`MixPage`独有的加载方式。
```python
from DrissionPage import MixPage, Drission
ds = Drission(ini_path=r'D:\config1.ini')
page = MixPage(drission=ds)
```
---
# ✔️ 保存/另存 ini 文件
```python
from DrissionPage import ChromiumOptions
co = ChromiumOptions()
# 修改一些设置
co.set_no_imgs()
# 保存到当前打开的ini文件
co.save()
# 保存到指定位置的配置文件
co.save(r'D:\config1.ini')
# 保存到默认配置文件
co.save_to_default()
```

View File

@ -1,173 +0,0 @@
Chrome 浏览器的配置繁琐且难以记忆,本库提供一些常用功能的快速设置方法,调用即可修改 ini 文件中该部分内容。对于简单的设置,使用可以使代码更整洁。这些方法统称 easy_set 方法。
!>**注意:**<br>- easy_set 方法仅用于设置 ini 文件,浏览器或 Session 创建后再调用没有效果的。 <br>- 如果是接管已打开的浏览器,这些设置也没有用。<br>- 虽然能简化代码,但会修改 ini
文件,只适合在单页面对象时使用。
# ✔️ 导入
easy_set 方法存放在`DrissionPage.easy_set`路径中,可用以下方法按需导入:
```python
from DrissionPage.easy_set import set_headless, set_paths
```
---
# ✔️ 简单示例
由于 easy_set 方法会直接修改 ini 文件内容,而`ChromiumPage`创建时会默认读取 ini 文件,所以代码看上去比较简洁。
但如果要创建多个页面对象,就要避免用这种方法,否则会造成冲突。
```python
from DrissionPage.easy_set import set_headless
from DrissionPage import ChromiumPage
# 设置无头模式
set_headless(True)
# 启动浏览器
page = ChromiumPage()
```
---
# ✔️ 方法介绍
## 📍 `set_paths()`
此方法用于设置各类路径信息。
| 参数名称 | 类型 | 默认值 | 说明 |
|:------------------:|:---------------:|:-------:| -------------------------------------------------------------- |
| `driver_path` | `str`<br>`Path` | `None` | chromdriver 的路径,只`MixPage``DriverPage`需要使用 |
| `chrome_path` | `str`<br>`Path` | `None` | 浏览器可执行文件路径,为向后兼容保留,建议用`browser_path`代替 |
| `browser_path` | `str`<br>`Path` | `None` | 浏览器可执行文件路径,与`chrome_path`同时设置时会覆盖`chrome_path` |
| `local_port` | `str`<br>`int` | `None` | 浏览器使用的本地端口号 |
| `debugger_address` | `str` | `None` | 调试浏览器地址,格式 ip: port`'127.0.0.1:9222'` |
| `download_path` | `str`<br>`Path` | `None` | 下载文件默认保存路径 |
| `user_data_path` | `str`<br>`Path` | `None` | 用户数据文件夹路径 |
| `cache_path` | `str`<br>`Path` | `None` | 缓存路径 |
| `ini_path` | `str`<br>`Path` | `None` | 要修改的ini文件路径`None`表示默认配置文件 |
| `check_version` | `bool` | `False` | 是否检查设置的 chromedriver 和 Chrome 是否匹配,只`MixPage``DriverPage`需要使用 |
**返回:**`None`
---
## 📍 `use_auto_port()`
此方法用于设置是否使用自动分配的端口和临时文件夹。
设置为`True`时,浏览器每次会用可用的端口和临时文件夹保存用户数据,而无视已经保存的端口和路径。
| 参数名称 | 类型 | 默认值 | 说明 |
|:----------:|:---------------:|:------:| --------------------------- |
| `on_off` | `bool` | `True` | `bool`表示开或关 |
| `ini_path` | `str`<br>`Path` | `None` | 要修改的ini文件路径`None`表示默认配置文件 |
**返回:**`None`
---
## 📍 `set_argument()`
此方法用于设置浏览器启动参数。
| 参数名称 | 类型 | 默认值 | 说明 |
|:----------:|:---------------:|:------:| --------------------------- |
| `arg` | `str` | `True` | 属性名 |
| `value` | `str` | `True` | 属性值,有值的属性传入值,没有的传入`None` |
| `ini_path` | `str`<br>`Path` | `None` | 要修改的ini文件路径`None`表示默认配置文件 |
**返回:**`None`
---
## 📍 `set_user_agent()`
此方法用于设置 user agent。
| 参数名称 | 类型 | 默认值 | 说明 |
|:------------:|:---------------:|:------:| --------------------------- |
| `user_agent` | `str` | 必填 | user agent文本 |
| `ini_path` | `str`<br>`Path` | `None` | 要修改的ini文件路径`None`表示默认配置文件 |
**返回:**`None`
---
## 📍 `set_proxy()`
此方法用于设置浏览器代理。注意此方法只能设置浏览器代理,不能设置`Session`对象代理。
| 参数名称 | 类型 | 默认值 | 说明 |
|:----------:|:---------------:|:------:| ----------------------------------- |
| `proxy` | `str` | 必填 | 代理网址和端口,例:`'http://localhost:1080'` |
| `ini_path` | `str`<br>`Path` | `None` | 要修改的ini文件路径`None`表示默认配置文件 |
**返回:**`None`
---
## 📍 `set_headless()`
此方法用于设置浏览器是否使用无界面模式。
| 参数名称 | 类型 | 默认值 | 说明 |
|:----------:|:---------------:|:------:| --------------------------- |
| `on_off` | `bool` | `True` | `bool`表示开或关 |
| `ini_path` | `str`<br>`Path` | `None` | 要修改的ini文件路径`None`表示默认配置文件 |
**返回:**`None`
---
## 📍 `set_no_imgs()`
此方法用于设置浏览器是否禁止加载图像。
| 参数名称 | 类型 | 默认值 | 说明 |
|:----------:|:---------------:|:------:| --------------------------- |
| `on_off` | `bool` | `True` | `bool`表示开或关 |
| `ini_path` | `str`<br>`Path` | `None` | 要修改的ini文件路径`None`表示默认配置文件 |
**返回:**`None`
---
## 📍 `set_no_js()`
此方法用于设置浏览器是否禁用 JavaScript。
| 参数名称 | 类型 | 默认值 | 说明 |
|:----------:|:---------------:|:------:| --------------------------- |
| `on_off` | `bool` | `True` | `bool`表示开或关 |
| `ini_path` | `str`<br>`Path` | `None` | 要修改的ini文件路径`None`表示默认配置文件 |
**返回:**`None`
---
## 📍 `set_mute()`
此方法用于设置浏览器是否静音。
| 参数名称 | 类型 | 默认值 | 说明 |
|:----------:|:---------------:|:------:| --------------------------- |
| `on_off` | `bool` | `True` | `bool`表示开或关 |
| `ini_path` | `str`<br>`Path` | `None` | 要修改的ini文件路径`None`表示默认配置文件 |
**返回:**`None`
---
## 📍 `show_settings()`
此方法用于打印 ini 文件内容。
| 参数名称 | 类型 | 默认值 | 说明 |
|:----------:|:---------------:|:------:| --------------------------- |
| `ini_path` | `str`<br>`Path` | `None` | 要修改的ini文件路径`None`表示默认配置文件 |
**返回:**`None`

View File

@ -1,463 +0,0 @@
浏览器没有为下载功能提供方便的程序接口,难以实现有效的下载管理,如检测下载状态、重命名、失败管理等。使用 requests 下载文件能较好实现以上功能,但代码较为繁琐。
因此 DrissionPage 内置了高效可靠的下载工具,提供任务管理、多线程、大文件分块、自动重连、文件名冲突处理等功能。
无论是控制浏览器,还是收发数据包的场景,都可以使用它。甚至可以拦截浏览器的下载动作,转用内置下载器进行下载。使用方式简洁高效。
?> 该工具名为 DownloadKit现已独立打包成一个库详细用法见[DownloadKit](https://gitee.com/g1879/DownloadKit)。这里只介绍和页面对象有关的用法。
# ✔️ 功能简介
`SessionPage``ChromiumPage``WebPage`都可以使用`download()`方法进行下载,还可以使用`download_set`属性对下载参数进行配置。内置下载器功能如下:
- 支持多线程同时下载多个文件
- 大文件自动分块使用多线程下载
- 可拦截浏览器下载动作,自动调用下载器下载
- 自动创建目标路径
- 自动去除路径中的非法字符
- 下载时支持文件重命名
- 自动处理文件名冲突
- 自动去除文件名非法字符
- 支持 post 方式
- 支持自定义连接参数
- 任务失败自动重试
---
# ✔️ 简单示例
下载一个文件(单线程):
```python
page.download('https://xxxxxx/file.pdf')
```
添加下载任务,以多线程方式下载:
```python
page.download.add('https://xxxxxx/file.pdf')
```
拦截浏览器下载动作,改用 DwonloadKit 下载:
```python
page.download_set.by_DownloadKit()
page('#download_btn').click()
```
下载设置:
```python
# 设置默认保存路径
page.download_set.save_path('...')
# 设置存在同名文件时处理方式
page.download_set.if_file_exists.skip()
# 设置使用浏览器或DownloadKit下载
page.download_set.by_DownloadKit()
page.download_set.by_browser()
# 设置使用DownloadKit下载时是否允许多线程同时下载一个文件
page.download_set.split(True)
```
---
# ✔️ 单线程下载
直接调用页面对象的`download()`方法,可下载一个文件,该方法是阻塞式的,会等待下载完成再往下操作。下载时默认打印下载进度。
### 🔸 `download()`
| 参数名称 | 类型 | 默认值 | 说明 |
|:-------------:|:---------------:|:------:| ------------------------------------------------ |
| `file_url` | `str` | 必填 | 文件网址 |
| `goal_path` | `str`<br>`Path` | `None` | 保存文件夹路径,为`None`则保存在当前目录 |
| `rename` | `str` | `None` | 重命名文件名,可不包含后缀 |
| `file_exists` | `str` | `None` | 遇到同名文件时的处理方式,可选`'skip'``'overwrite'``'rename'` |
| `data` | `str`<br>`dict` | `None` | post 方式使用的数据,如不为`None`,使用 post 方式发送请求 |
| `show_msg` | `bool` | `True` | 是否显示下载进度 |
| `**kwargs` | - | 必填 | requests的`get()`方法参数 |
| 返回类型 | 返回格式 | 说明 |
|:-------:|:------------:| ------------------- |
| `tuple` | (任务结果, 任务信息) | 返回任务结果和信息组成的`tuple` |
任务结果可能出现以下值:
- `'success'`:表示下载成功
- `'skip'`:表示存在同名文件,跳过任务
- `'cancel'`:表示任务下载途中被取消
- `False`:表示下载失败
任务信息成功时返回文件绝对路径,其它情况返回相应说明。
**示例:**
```python
from DrissionPage import WebPage
page = WebPage('s')
# 文件 url
url = 'https://www.baidu.com/img/flexible/logo/pc/result.png'
# 存放路径
save_path = r'C:\download'
# 重命名为img.png存在重名时自动在文件名末尾加上序号显示下载进度
res = page.download(url, save_path, 'img', 'rename', show_msg=True)
# 打印结果
print(res)
```
显示:
```console
urlhttps://www.baidu.com/img/flexible/logo/pc/result.png
文件名img.png
目标路径C:\download
100% 下载完成 C:\download\img.png
('success', 'C:\\download\\img.png')
```
---
# ✔️ 多线程并发下载
您可以添加数量不限的下载任务,程序会自动调配线程去完成这些任务。
默认设置下,页面对象最多使用 10 个下载线程,如进程已满,新任务会进入等待队列排队,待有空线程时自动开始。
添加多线程任务的方法是调用页面对象`donwload`属性的`add()`方法。
### 🔸 `download.add()`
| 参数名称 | 类型 | 默认值 | 说明 |
|:-------------:|:---------------:|:------:| ----------------------------------------------------------------- |
| `file_url` | `str` | 必填 | 文件网址 |
| `goal_path` | `str`<br>`Path` | `None` | 保存文件夹路径,为`None`则保存在当前目录 |
| `rename` | `str` | `None` | 重命名文件名,可不包含后缀 |
| `file_exists` | `str` | `None` | 遇到同名文件时的处理方式,可选`'skip'``'overwrite'``'rename'` |
| `data` | `str`<br>`dict` | `None` | post 方式使用的数据,如不为`None`,使用 post 方式发送请求 |
| `split` | `bool` | `None` | 是否允许多线程分块下载,为`None`则使用下载器内置设置,默认为`True`<br>默认大于 50MB 的文件使用多线程分块下载 |
| `**kwargs` | - | 必填 | requests的`get()`方法参数 |
| 返回类型 | 说明 |
|:---------:| ---------------------------------------------------------------------------- |
| `Mission` | 返回任务对象,任务对象具体属性及方法详见 [DownloadKit](https://gitee.com/g1879/DownloadKit) 相关说明 |
**示例:**
```python
from DrissionPage import WebPage
page = WebPage('s')
# 文件 url
url = 'https://www.baidu.com/img/flexible/logo/pc/result.png'
# 存放路径
save_path = r'C:\download'
# 返回一个任务对象
mission = page.download.add(url, save_path)
# 通过任务对象查看状态
print(mission.rate, mission.info)
```
**输出:**
```console
90% '下载中'
```
---
# ✔️ 接管浏览器下载任务
## 📍 切换下载方式
很多时候,网站会用某些非显式的方式触发浏览器下载,因此不能很好地获取文件 url下载依赖浏览器功能。页面对象可以设置用`DownloadKit`拦截浏览器的下载任务,使下载更快,下载过程更可控。
方法是使用`download_set.by_DownloadKit()`方法,指定使用`DownloadKit`接管浏览器的下载动作。
```python
page.download_set.by_DownloadKit()
```
事实上,页面对象创建时就默认开启了此功能,如果想用浏览器本身的下载功能,可以这样写:
```python
page.download_set.by_browser()
```
!>**注意:**<br>接管浏览器下载任务后会使用 get 方式下载,如果是需要 post 的请求,可能不能正确下载。
---
## 📍 等待下载开始
在浏览器中点击下载按钮后,有时下载不会立即触发,这时如果过快进行其它操作,可能导致一些意想不到的问题。因此设计了`wait_download_begin()`
方法,用于等待下载动作的开始,以便正确接管。可控制浏览器的页面对象`ChromiumPage``WebPage`拥有此方法。
!>**注意:**<br>如果网站须要很长时间准备下载的文件,请设置一个足够长的超时时间。
### 🔸 `wait_download_begin()`
此方法会阻塞程序,等待下载动作触发。如到达超时时间仍未触发,则返回`False`
| 参数名称 | 类型 | 默认值 | 说明 |
|:---------:|:----------------:|:------:| ------------------------------------- |
| `timeout` | `int`<br>`float` | `None` | 等待下载开始的超时时间,为`None`则使用页面对象`timeout`属性 |
| 返回类型 | 说明 |
|:------:| -------- |
| `bool` | 是否等到下载开始 |
**示例:**
```python
page('#download_btn').click()
page.wait_download_begin()
```
---
# ✔️ 查看任务信息
用单线程方式下载,会阻塞程序直到下载完成,因此无须查看任务信息。
用多线程或接管浏览器下载任务的方式下载,可用以下方式查看任务信息。
## 📍 获取单个任务对象
使用`download.add()`
添加任务时,会返回一个任务对象,后续程序可以使用该对象查询该任务下载进度、结果,也可以取消任务。这里不对该对象作详细说明,详情请移步:[DownloadKit](https://gitee.com/g1879/DownloadKit)。
**示例:**
```python
mission = page.download.add('http://xxxx.pdf')
print(mission.id) # 获取任务id
print(mission.rate) # 打印下载进度(百分比)
print(mission.state) # 打印任务状态,'waiting'、'running'、'done'
print(mission.info) # 打印任务信息
print(mission.result) # 打印任务结果,'success'表示成功False表示失败'skip'表示跳过,'cancel'表示取消
```
除添加任务时获取对象,也可以使用`download.get_mission()`获取。在上一个示例中可以看到,任务对象有`id`属性,把任务的`id`传入此方法,会返回该任务对象。
**示例:**
```python
mission_id = mission.id
mission = page.download.get_mission(mission_id)
```
---
## 📍 获取全部任务对象
使用页面对象的`download.missions`属性,可以获取所有下载任务。该属性返回一个`dict`,保存了所有下载任务。以任务对象的`id`为 key。
```python
page.download_set.save_path(r'D:\download')
page.download('http://xxxxx/xxx1.pdf')
page.download('http://xxxxx/xxx1.pdf')
print(page.download.missions)
```
**输出:**
```
{
1: <Mission 1 D:\download\xxx1.pdf xxx1.pdf>
2: <Mission 2 D:\download\xxx1_1.pdf xxx1_1.pdf>
...
}
```
---
## 📍 获取下载失败的任务
使用`download.get_failed_missions()`方法,可以获取下载失败的任务列表。
```python
page.download_set.save_path(r'D:\download')
page.download('http://xxxxx/xxx1.pdf')
page.download('http://xxxxx/xxx1.pdf')
print(page.download.get_failed_missions()
```
**输出:**
```
[
<Mission 1 状态码404 None>,
<Mission 2 状态码404 None>
...
]
```
?>**Tips**<br>获取失败任务对象后,可从其`data`属性读取任务内容,以便记录日志或择机重试。
---
# ✔️ 下载设置
主要的下载设置使用`download_set`内置方法进行,更多运行参数使用`download`属性的子属性进行。
## 📍 设置文件保存路径
### 🔸 `download_set.save_path()`
此方法用于设置文件下载默认保存路径。
| 参数名称 | 类型 | 默认值 | 说明 |
|:------:|:---------------:|:---:| ------------------ |
| `path` | `str`<br>`Path` | 必填 | 文件保存路径,绝对路径和相对路径均可 |
**返回:**`None`
**示例:**
```python
page.download_set.save_path(r'D:\tmp')
```
?>**Tips**<br>- 保存路径可指定不存在的文件夹,程序会自动创建。<br>- 设置默认保存路径后,每个任务仍可在创建时指定自己的保存路径,以覆盖默认设置。
---
## 📍 设置下载方式
### 🔸 `download_set.by_DownloadKit()`
此方法用于设置当前页面对象使用`DownloadKit`下载文件。
### 🔸 `download_set.by_browser()`
此方法用于设置当前页面对象使用浏览器下载工具下载文件。
---
## 📍 设置重名文件处理方法
下载过程中可能遇到保存路径已存在同名文件,`DownloadKit`提供 3 种处理方法。
!>**注意:**<br>这个设置只有在使用`DownloadKit`作为下载工具时有效。
### 🔸 `download_set.if_file_exists.rename()`
此方式会对新文件进行重命名,在文件名后加上序号。
假如,保存路径已存在`abc.txt`文件,新的`abc.txt`文件会自动重命名为`abc_1.txt`,再下载一个`abc.txt`时,会命名为`abc_2.txt`,如此类推。
**示例:** 3 次下载同一个文件
```python
page.download_set.if_file_exists.rename()
page.download('http://xxxxx/xxx.pdf')
page.download('http://xxxxx/xxx.pdf')
page.download('http://xxxxx/xxx.pdf')
```
在文件夹会生成如下 3 个文件:
```
xxx.pdf
xxx_1.pdf
xxx_2.pdf
```
---
### 🔸 `download_set.if_file_exists.skip()`
遇到同名文件时,跳过。同时任务对象的`result`属性设置为`'skip'`
**示例:** 3 次下载同一个文件
```python
page.download_set.if_file_exists.skip()
page.download('http://xxxxx/xxx.pdf')
page.download('http://xxxxx/xxx.pdf')
page.download('http://xxxxx/xxx.pdf')
```
在文件夹只会生成如下 1 个文件:
```
xxx.pdf
```
---
### 🔸 `download_set.if_file_exists.overwrite()`
遇到同名文件时,覆盖。
!>**注意:**<br>这个方式在多线程下载时要慎用,万一多个任务下载多个同名文件,会导致互相覆盖的现象。
?>**Tips**<br>- 除了整体设置,还可以在创建任务时单独设置该任务的处理方式。<br>- 文件名如遇到`'?'``'\'`等非法字符,会自动替换为空格。
---
## 📍 设置大文件是否分块
`DownloadKit`具备多线程下载大文件功能,在文件超过指定大小时(默认 50MB可对文件进行多线程分块下载每个线程负责 50MB 的下载,以提高下载速度。这个功能默认是关闭的,您可以设置是否开启。
!>**注意:**
这个设置只有在使用`DownloadKit`作为下载工具时有效。
### 🔸 `download_set.split()`
```python
# 使用分块下载
page.download_set.split(on_off=True)
# 禁用分块下载
page.download_set.split(on_off=False)
```
?>**Tips**
除了整体设置,还可以在创建任务时单独设置该任务是否使用分块下载。
---
## 📍运行参数设置
运行参数主要包括可使用线程上限、连接失败重试次数、重试间隔。
### 🔸 `download.roads`
此参数设置整个`DownloadKit`对象允许使用的线程数上限,默认为 10。
默认设置下,页面对象最多使用 10 个下载线程,如进程已满,新任务会进入等待队列排队,待有空线程时自动开始。
这个属性只能在没有任务在运行的时候设置。
```python
page.download.roads = 20 # 允许最多使用20个线程进行下载
```
---
### 🔸 `download.retry`
此属性用于设置连接失败时重试次数,默认为 3。
```python
page.download.roads = 5 # 设置连接失败时重试5次
```
---
### 🔸 `download.interval`
此属性用于设置连接失败时重试间隔,默认为 5 秒。
```python
page.download.interval = 10 # 设置连接失败时等待10秒再重试
```
?> **Tips**<br> 重试次数和间隔在初始化时继承页面对象的`retry_times``retry_interval`属性,可用上面例子的方法对下载的重试次数和间隔进行设置,设置后不会影响页面对象的设置。

View File

@ -1,109 +0,0 @@
本节演示一个能够大幅加快浏览器页面数据采集的黑科技。
# ✔️ 示例
我们找一个比较大的页面来演示,比如网页首页:[https://www.163.com]([https://www.163.com](https://www.163.com))
我们数一下这个网页内的`<a>`元素数量:
```python
from DrissionPage import ChromiumPage
page = ChromiumPage()
page.get('https://www.163.com')
print(len(page('t:body').eles('t:a')
```
**输出:**
```console
1613
```
嗯,数量不少,可以看出效果。
加入现在我们的任务是打印所有链接的文本,常规做法是遍历所有元素,然后打印。
这里引入一个计时工具,可以标记一段代码运行时间。您也可以用其它方法计时。
```python
from DrissionPage import ChromiumPage
from TimePinner import Pinner # 导入计时工具
pinner = Pinner() # 创建计时器对象
page = ChromiumPage()
page.get('https://www.163.com')
pinner.pin() # 标记开始记录
# 获取所有链接对象并遍历
links = page('t:body').eles('t:a')
for lnk in links:
print(lnk.text)
pinner.pin('用时') # 记录并打印时间
```
**输出:**
```console
0.0
网络大过年_网易政务_网易网
网易首页
...中间省略...
不良信息举报 Complaint Center
廉正举报
用时4.057772700001806
```
用时 4 秒。
现在,我们稍微修改一个小小的地方。
`page('t:body').eles('t:a')`改成`page('t:body').s_eles('t:a')`,然后再执行一次。
```python
from DrissionPage import ChromiumPage
from TimePinner import Pinner # 导入计时工具
pinner = Pinner() # 创建计时器对象
page = ChromiumPage()
page.get('https://www.163.com')
pinner.pin() # 标记开始记录
# 获取所有链接对象并遍历
links = page('t:body').s_eles('t:a')
for lnk in links:
print(lnk.text)
pinner.pin('用时') # 记录并打印时间
```
**输出:**
```console
0.0
网络大过年_网易政务_网易网
网易首页
...中间省略...
不良信息举报 Complaint Center
廉正举报
用时0.2797656000002462
```
神奇不?原来 4 秒的采集时间现在只需 0.28 秒。
---
# ✔️ 解读
`s_eles()``eles()`的区别在于前者会把整个页面转变成一个静态页面,再在其中获取静态元素,因为静态元素是纯文本的,没有各种属性、交互等等消耗资源的部分,所有运行速度非常快。
作者曾经采集过一个非常复杂的页面,动态元素用时 30 秒,转静态元素就只要 0.X 秒,加速效果非常明显。
我们可以获取页面中内容容器(示例中的`<body>`),把它转换成静态元素,再在其中获取信息。
当然,静态元素没有交互功能,但它只是副本,不会影响原来的动态元素。

View File

@ -1,91 +0,0 @@
本节介绍打包程序须要注意的事项。
因为程序用到 ini 文件,而打包时不会自动带上,因此直接打包是会导致运行出错。
解决办法:
- 手动带上 ini 文件,并在程序中指定路径
- 把配置信息写在程序中,不带 ini 文件
# ✔️ 手动带上 ini
在程序中用相对路径方式指定 ini 文件,并且打包后手动把 ini 文件复制到程序文件夹。
`WebPage`
```python
from DrissionPage import WebPage, ChromiumOptions, SessionOptions
do = ChromiumOptions(ini_path=r'.\configs.ini')
so = SessionOptions(ini_path=r'.\configs.ini')
page = WebPage(driver_or_options=do, session_or_options=so)
```
`MixPage`
```python
from DrissionPage import Drission, MixPage
drission = Drission(ini_path=r'.\configs.ini') # ini文件放在程序相同路径下
page = MixPage(drission=drission)
```
---
# ✔️ 不使用 ini
这种方法须把所有配置信息写到代码里。
`WebPage`
```python
from DrissionPage import WebPage, ChromiumOptions, SessionOptions
do = ChromiumOptions(read_file=False) # 不读取文件方式新建配置对象
so = SessionOptions(read_file=False)
do.set_paths(chrome_path=r'.\chrome.exe') # 输入配置信息
page = WebPage(driver_or_options=do, session_or_options=so)
```
`MixPage`
```python
from DrissionPage import MixPage, DriverOptions, SessionOptions
do = DriverOptions(read_file=False)
so = SessionOptions(read_file=False)
page = MixPage(driver_options=do, session_options=so)
```
!> **注意** <br>这个时候 driver 和 session 两个参数都要输入内容,如果其中一个不需要设置可以输入`False`
如:
```python
page = WebPage(driver_or_options=do, session_or_options=False)
```
---
# ✔️ 实用示例
通常,我会把一个绿色浏览器和打包后的 exe 文件放在一起,程序中用相对路径指向该浏览器,这样拿到别的电脑也可以正常实用。
```python
from DrissionPage import WebPage, ChromiumOptions
do = ChromiumOptions(read_file=False).set_paths(local_port='9888',
browser_path=r'.\Chrome\chrome.exe',
user_data_path=r'.\Chrome\userData')
page = WebPage(driver_or_options=do, session_or_options=False)
# 注意session_or_options=False
page.get('https://www.baidu.com')
```
注意以下两点,程序就会跳过读取 ini 文件:
- `ChromiumOptions()`里要设置`read_file=False`
- 如果不传入某个模式的配置(示例中为 s 模式),要在页面对象初始化时设置对应参数为`False`

View File

@ -1,27 +0,0 @@
许多网页的数据来自接口,在网站使用过程中动态加载,如使用 JS 加载内容的翻页列表。
这些数据通常以 json 形式发送,浏览器接收后,对其进行解析,再加载到 DOM 相应位置。
做数据采集的时候,我们往往从 DOM 中去获取解析后数据的,可能存在 数据不全、加载响应不及时、难以判断加载完成等问题。
因此开发了一个监听器,专门用于抓取 Chromium 内核浏览器数据包。
由于该工具不依赖 DrissionPage现已独立发布为一个库但仍然可以在 DrissionPage 中导入。
!> 为了便于维护,该工具用法请移步 [FlowViewer](https://gitee.com/g1879/FlowViewer) 查看。
# ✔️ 简单示例
```python
from DrissionPage.tools import Listener
listener =Listener(9222) # 创建监听器监听9222端口的浏览器
listener.set_targets('JobSearchResult.aspx') # 设置需要监听的url
listener.listen(count=10) # 开始监听接收到10条目标url的请求后停止
for i in listener.steps():
print(i.body) # 打印实时打印监听到的内容
listener.stop() #停止监听
```

View File

@ -1,15 +0,0 @@
本库 3.0 以前的版本是对 selenium 进行重新封装实现的。
其页面对象为`MixPage``DriverPage`,对应于新版的`WebPage``ChromiumPage`
经过几年的使用,旧版已相当稳定。但由于依赖 selenium功能开发受到较大制约且 selenium 有其特征,容易被网站识别,故开发了新版`WebPage`取代之。
新版于旧版使用方法基本一致,但新版功能更多更强,且部分方法或属性名称有修改。
目前旧版开发已冻结,为兼容以前的项目,除了修复 bug旧版不会再有功能上的修改。
有兴趣的读者可以了解一下。
其结构图如下:
![](https://gitee.com/g1879/DrissionPage/raw/master/docs/imgs/mixpage.jpg)

View File

@ -1,59 +0,0 @@
DrissionPage 代码可无缝拼接 selenium 及 requests 代码。既可直接使用 selenium 的`WebDriver`对象,也可导出自身的`WebDriver`给 selenium 代码使用。requests 的
`Session`对象也可直接传递。便于已有项目的迁移。
# ✔️ selenium 转 DrissionPage
```python
from selenium import webdriver
driver = webdriver.Chrome()
driver.get('https://www.baidu.com')
# 把 driver 传递给 Drission创建 MixPage 对象
drission = Drission(driver_or_options=driver)
page = MixPage(drission=drission)
# 打印结果:百度一下,你就知道
print(page.title)
```
# ✔️ DrissionPage 转 selenium
```python
page = MixPage()
page.get('https://www.baidu.com')
# 从 MixPage 对象中获取 WebDriver 对象
driver = page.driver
# 打印结果:百度一下,你就知道
print(driver.title)
# 使用 selenium 原生功能
element = driver.find_element(By.XPATH, '//div')
```
# ✔️ requests 转 DrissionPage
```python
from requests import Session
session = requets.Session()
# 把 session 传递给 Drission创建 MixPage 对象
drission = Drission(session_or_options=session)
page = MixPage('s', drission=drission)
page.get('https://www.baidu.com')
```
# ✔️ DrissionPage 转 requests
```python
from DrissionPage import MixPage
page = MixPage('s')
# 提取 MixPage 中的 Session 对象
session = page.session
response = session.get('https://www.baidu.com')
```

View File

@ -1,17 +0,0 @@
目前只在 Windows 系统 Chrome 浏览器上进行过完整功能测试,如要使用其它系统或浏览器,在启动和设置方面可能遇到问题。
这个时候可使用 selenium 原生方法创建 driver然后用 Drission 对象接收即可。
!> **注意:** <br>本库所有功能暂时只在 Chrome 上做了完整测试。
```python
from selenium import webdriver
from DrissionPage import Drission, MixPage
# 用 selenium 原生代码创建 WebDriver 对象
driver = webdriver.Firefox()
# 把 WebDriver 对象传入 Drission 对象
dr = Drission(driver_or_options=driver)
page = MixPage(drission=dr)
page.get('https://www.baidu.com')
```

View File

@ -1,46 +0,0 @@
如果无须切换模式,可根据需要只使用 DriverPage 或 SessionPage。
分别对应 d 和 s 模式,用法和 MixPage 相似。
# ✔️ SessionPage
```python
from DrissionPage.session_page import SessionPage
from DrissionPage import Drission
# 用配置文件信息创建 Drission获取其中 Session 对象
session = Drission().session
# 传入 Session 对象创建页面对象
page = SessionPage(session)
# 使用页面对象
page.get('http://www.baidu.com')
print(page.ele('#su').text)
```
**输出:**
```console
百度一下
```
# ✔️ DriverPage
```python
from DrissionPage.driver_page import DriverPage
from DrissionPage import Drission
# 用配置文件信息创建 Drission获取其中 WebDriver 对象
driver = Drission().driver
# 传入 WebDriver 对象创建页面对象
page = DriverPage(driver)
# 使用页面对象
page.get('http://www.baidu.com')
print(page.ele('#su').text)
```
**输出:**
```console
百度一下
```

View File

@ -1,262 +0,0 @@
在入门指南的快速上手一节,我们已经初步了解如何创建页面对象,本节进一步介绍更多功能。
页面对象类型为`MixPage`,通过`MixPage()`创建。
可以通过指定配置信息创建须要的页面对象,如无界面的浏览器、是否加载插件、是否接管已打开的浏览器、设置`headers`、设置代理等等。
这些配置信息,可以通过几种方式设置。配置的详细用法后文再讲。本节先了解创建页面对象的几种方式。
# ✔️ `MixPage`
`MixPage`页面对象封装了常用的网页操作,并实现在两种模式之间的切换。
`MixPage`须控制一个`Drission`对象并使用其中的`WebDriver``Session`对象来实现。对浏览器或网络连接的操作。如没有传入,`MixPage`会自己创建一个(使用传入的配置信息或从默认 ini 文件读取)。
**初始化参数:**
- `mode`:初始化时模式,`'d'``'s'`,默认为`'d'`
- `drission`Drission 对象,不传入时会自动创建
- `timeout`超时时间s 模式时为连接时间d 模式时为查找元素、处理弹出框、输入文本等超时时间
- `driver_options`:浏览器设置,没传入`drission`参数时会用这个设置新建`Drission`对象中的`WebDriver`对象,传入`False`则不创建
- `session_options`requests 设置,没传入`drission`参数时会用这个设置新建`Drission`对象中的`Session`对象,传入`False`则不创建
!> **注意:** <br>有传入`drission`参数时,`driver_options``session_options`参数无效
# ✔️ 直接创建
这种方式代码最简洁,程序会从配置文件中读取配置,自动生成页面对象。可以保持代码简洁。
在基本概念一节我们提到过,本库使用配置文件记录常用配置信息,也可以直接把配置写在代码里。
```python
# 默认以 d 模式创建页面对象
page = MixPage('d')
# 指定以 s 模式创建页面对象
page = MixPage('s')
```
# ✔️ 通过配置信息创建
本库有两种管理配置信息的对象,分别是`DriverOptions``SessionOptions`,对应 d 模式和 s 模式的配置。
须要时,可以创建相应的配置对象进行设置。
## 📍 `DriverOptions`
`DriverOptions`用于管理创建浏览器时的配置,浏览器创建后再修改这个配置是没有效果的。
`DriverOptions`对象能实现链式操作。
**初始化参数:**
- `read_file`:是否从 ini 文件中读取配置信息
- `ini_path`ini 文件路径,为`None`则读取默认 ini 文件
```python
# 导入 DriverOptions
from DrissionPage import MixPage, DriverOptions
# 创建浏览器配置对象,从文件读取配置,并增加设置浏览器静音和不加载图片
do = DriverOptions().set_mute().set_no_imgs()
# 用该配置创建页面对象
page = MixPage(driver_options=do)
```
## 📍 `SessionOptions`
`SessionOptions`用于管理创建`Session`对象时的配置,内置了常用的配置,并能实现链式操作。详细使用方法见“启动配置”一节。
**初始化参数:**
- `read_file`:是否从 ini 文件中读取配置信息
- `ini_path`ini 文件路径,为`None`则读取默认 ini 文件
!>**注意:**<br>`Session`对象创建后再修改这个配置是没有效果的。
```python
# 导入 DriverOptions
from DrissionPage import MixPage,SessionOptions
proxies = {'http': 'http://127.0.0.1:1080',
'https': 'https://127.0.0.1:1080'}
# 创建配置对象,不从 ini 文件读取,并设置代理信息
so = SessionOptions(read_file=False).set_proxies(proxies)
# 用该配置创建页面对象s 模式)
page = MixPage(mode='s', session_options=so)
```
d 模式的配置和 s 模式的配置是可以同时使用的,不会互相影响。
```python
page = MixPage(mode='s', session_options=so, driver_options=do)
```
# ✔️ 传入`Drission`对象创建
在入门指南的基本概念一节里,我们讲过`Drission`对象相当于驱动器的角色。事实上,上述两种方式,`MixPage`都会自动创建一个`Drission`对象用于管理与网站或浏览器的连接,我们当然也可以手动创建并传入`MixPage`
`Drission`一般是不用手动创建的要手动创建的时候一般是用于i以下几种情况
- 指定使用某个配置文件
- 在不同`MixPage`间传递驱动器
- 与 selenium 或 requests 原生代码拼接,用于兼容这两者的代码
## 📍 `Drission`
**初始化参数:**
- `driver_or_options``WebDriver`对象、`DriverOptions`对象或`Options`对象。传入`False`时自动创建一个空配置对象。
- `session_or_options``Session`对象、`SessionOptions`对象、`Options`对象或设置字典。传入`False`时自动创建一个空配置对象。
- `ini_path`:要使用的 ini 文件的路径
- `proxy`:初始化时设置代理
## 📍 使用其它 ini 文件创建
```python
from DrissionPage import MixPage, Drission
d = Drission(ini_path=r'./config1.ini')
page = MixPage(drission=d)
```
## 📍 传递驱动器
多页面对象间共用驱动器,如多个`MixPage`控制一个浏览器:
```python
from DrissionPage import MixPage
page1 = MixPage()
d = page1.drission
page2 = MixPage(drission=d)
```
## 📍 从 selenium 和 requests 代码传入
DrissionPage 的代码能和 selenium 及 requests 代码兼容,便于不同程序间的对接。
只需把`WebDriver`对象或`Session`传入`Drission`对象即可。
```python
from selenium import webdriver
from requests import Session
from DrissionPage import Drission, MixPage
driver = webdriver.Chrome()
session = Session()
d = Drission(driver_or_options=driver, session_or_options=session)
page = MixPage(drission=d)
page.get('https://www.baidu.com')
```
## 📍 用配置信息创建
因为`MixPage`创建时能直接接收配置信息,所以这个方法基本不需要用到,写出来只是表示有这个功能。
```python
from DrissionPage.config import DriverOptions, SessionOptions
from DrissionPage import Drission, MixPage
do = DriverOptions()
so = SessionOptions()
d = Drission(driver_or_options=do, session_or_options=so)
page = MixPage(drission=d)
```
# ✔️ 接管手动打开的浏览器
如果须要手动打开浏览器,手动操作后再接管,可以这样做:
- 右键点击浏览器图标,选择属性
- 在“目标”路径后面加上` --remote-debugging-port=端口号`(注意最前面有个空格)
- 点击确定
- 在程序中的浏览器配置中指定接管该端口浏览器,如下:
```
# 文件快捷方式的目标路径设置
D:\chrome.exe --remote-debugging-port=9222
```
```python
from DrissionPage.config import DriverOptions
from DrissionPage import MixPage
do = DriverOptions().set_paths(local_port=9222)
page = MixPage(driver_options=do)
```
?>**Tips**<br>接管使用 bat 文件打开的浏览器也是一样做法做法。<br>接管浏览器时只有`local_port``debugger_address``driver_path`参数是有效的。
# ✔️ 多 Chrome 浏览器共存
如果想要同时操作多个 Chrome 浏览器,或者自己在使用 Chrome 上网,同时控制另外几个跑自动化,就须要给这些被程序控制的浏览器设置单独的端口和用户文件夹,否则会造成冲突。具体用`DriverOptions`对象进行设置,示例如下:
```python
from DrissionPage.config import DriverOptions
do1 = DriverOptions().set_paths(local_port=9111, user_data_path=r'D:\data1')
do2 = DriverOptions().set_paths(local_port=9222, user_data_path=r'D:\data2')
page1 = MixPage(driver_options=do1)
page2 = MixPage(driver_options=do2)
page1.get('https://www.baidu.com')
page2.get('http://www.163.com')
```
如果要接管多个手动打开的浏览器,每个浏览器后面的参数都要添加`--remote-debugging-port``--user-data-dir`参数。示例如下:
```
# 浏览器1目标设置
D:\chrome.exe --remote-debugging-port=9111 --user-data-dir=D:\data1
# 浏览器2目标设置
D:\chrome.exe --remote-debugging-port=9222 --user-data-dir=D:\data2
```
```python
from DrissionPage.config import DriverOptions
do1 = DriverOptions().set_paths(local_port=9111)
do2 = DriverOptions().set_paths(local_port=9222)
page1 = MixPage(driver_options=do1)
page2 = MixPage(driver_options=do2)
```
?> **Tips**<br>使用 bat 文件打开浏览器再接管操作是一样的。
# ✔️ 一些技巧
事实上,本库默认启动浏览器的方式是先通过程序在 9222或用户指定的端口运行一个浏览器进程然后通过程序接管。这种做法有很多好处
## 📍 可重复使用的浏览器对象
当程序运行完毕,浏览器不会主动关闭,下次再运行的时候可以直接在当前状态下继续运行。于是无须每次打开新的浏览器对象,无须从最开始步骤重新运行整个程序,一些前置条件(如登录)也无须每次运行,大大提高开发效率。
## 📍 简便的调试方式
通过重复使用浏览器对象,用户可以把浏览器页面调整到某个状态再用程序接管,对调试某个特定问题效率极高。比如有些通过很多步骤才能到达的页面,如果每次重新运行会花费大量时间,而将页面固定再由程序接管,测试各种细节非常方便快捷。
## 📍 一行代码传递登录状态到 requests
本库的一个特点是打通了浏览器和`requests`之间的登录状态,我们可以手动登录浏览器,再用程序接管,然后切换到 s 模式,这时 s 模式的`Session`对象即已活动浏览器的登录信息,无须用`requests`
处理登录过程,极大地简化了开发复杂程度。示例:
```python
# 假设已在 9222 端口打开了一个浏览器,并已登录某网站
from DrissionPage import MixPage
page = MixPage() # 用 d 模式创建页面对象,默认接管 9222 端口
page.change_mode() # 切换到 s 模式,即使用 requests 进行操作
page.get('某url...') # 即可已登录状态访问
```
使用其它端口号:
```python
from DrissionPage.config import DriverOptions
from DrissionPage import MixPage
do = DriverOptions().set_paths(local_port=9333)
page = MixPage(driver_option=do)
page.change_mode()
page.get('某url...')
```

View File

@ -1,113 +0,0 @@
操作页面前通常要先跳转到目标 url接管现有浏览器除外本库访问网页主要支持`get()``post()`两种方式,还支持自动重试。
# ✔️ `get()`
该方法在 d 模式和 s 模式下都可用,用于跳转到一个网址。
当连接失败时,程序默认重试 3 次,每次间隔 2 秒,也可以通过参数设置重试次数和间隔。
在 s 模式下,可传入连接参数,语法与 requests 的`get()`方法一致。
方法返回是否连接成功的布尔值s 模式下根据`Response`对象的`status_code`参数决定d 模式下根据浏览器的状态,还可以通过重写`check_page()`方法实现自定义检查方式。
**参数:**
- `url`:目标 url
- `show_errmsg`:是否显示和抛出异常,默认不抛出,连接错误会返回`None`
- `retry`:重试次数,与页面对象的设置一致,默认 3 次
- `interval`:重试间隔(秒),与页面对象的设置一致,默认 2 秒
- `**kwargs`连接参数s 模式专用,与 requests 的使用方法一致
**返回:**`bool`类型表示是否连接成功d 模式下如返回`None`表示不确定
## 📍 d 模式
```python
from DrissionPage import MixPage
page = MixPage('d')
page.get('https://www.baidu.com')
```
## 📍 s 模式
s 模式的`**kwargs`参数与 requests 中该参数使用方法一致,但有一个特点,如果该参数中设置了某一项(如`headers`),该项中的每个项会覆盖从配置中读取的同名项,而不会整个覆盖。
就是说,如果想继续使用配置中的`headers`信息,而只想修改其中一项,只需要传入该项的值即可。这样可以简化代码逻辑。
实用功能:
- 程序会根据要访问的网址自动在`headers`中加入`Host``Referer`项。
- 程序会自动从返回内容中确定编码,一般情况无须手动设置。
- s 模式下页面对象的`timeout`属性是指连接超时时间,不是查找元素超时时间。
```python
from DrissionPage import MixPage
page = MixPage('s')
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()`
此方法是用 post 方式请求页面。大致用法与`get()`一致,但增加一个`data`参数。
此方法只有 s 模式拥有,调用时,页面对象会自动切换到 s 模式。
**参数:**
- `url`:目标 url
- `data`:提交的数据,可以是`dict``str`类型
- `show_errmsg`:是否显示和抛出异常,默认不抛出,连接错误会返回 None
- `retry`:重试次数,与页面对象的设置一致,默认 3 次
- `interval`:重试间隔(秒),与页面对象的设置一致,默认 2 秒
- `**kwargs`连接参数s 模式专用
?> **Tips:** <br>虽然参数里没有`json`参数,但也和 requests 一样可以对`json`参数传值。
```python
from DrissionPage import MixPage
page = MixPage('s')
data = {'username': 'xxxxx', 'pwd': 'xxxxx'}
page.post('http://example.com', data=data)
# 或
page.post('http://example.com', json=data)
```
`data`参数和`json`参数都可接收`str``dict`格式数据,即有以下 4 种传递数据的方式:
```python
# 向 data 参数传入字符串
page.post(url, data='xxx')
# 向 data 参数传入字典
page.post(url, data={'xxx': 'xxx'})
# 向 json 参数传入字符串
page.post(url, json='xxx')
# 向 json 参数传入字典
page.post(url, json={'xxx': 'xxx'})
```
# ✔️ 其它请求方式
本库只针对常用的 get 和 post 方式作了优化,但也可以通过提取页面对象内的`Session`对象以原生 requests 代码方式执行其它请求方式。当然,它们工作在 s 模式。
```python
from DrissionPage import MixPage
page = MixPage('s')
# 获取内置的 Session 对象
session = page.session
# 以 head 方式发送请求
response = session.head('https://www.baidu.com')
print(response.headers)
```
**输出:**
```console
{'Accept-Ranges': 'bytes', 'Cache-Control': 'private, no-cache, no-store, proxy-revalidate, no-transform', 'Connection': 'keep-alive', 'Content-Length': '277', 'Content-Type': 'text/html', 'Date': 'Tue, 04 Jan 2022 06:49:18 GMT', 'Etag': '"575e1f72-115"', 'Last-Modified': 'Mon, 13 Jun 2016 02:50:26 GMT', 'Pragma': 'no-cache', 'Server': 'bfe/1.0.8.18'}
```

View File

@ -1,894 +0,0 @@
本节介绍如何从页面对象或元素对象中获取需要的元素对象。
无论是数据采集还是页面自动化,定位元素都是重中之重的的技能,浏览器开发者工具虽然可以直接复制绝对 xpath 或 css 路径,但这样做一来代码繁琐,可读性低,二来难以应付动态变化的页面。
本库提供一套简洁易用的语法,用于快速定位元素,并且内置等待功能、支持链式查找,减少了代码的复杂性。
定位元素大致分为三种方法:
- 在页面或元素内查找子元素
- 根据 DOM 结构相对定位
- 根据页面布局位置相对定位
d 模式的元素还有专门用于处理 shadow dom 的`shadow_root`属性。获取到的元素可继续用这些方法获取后代元素,使用方法和普通元素一致。
# ✔️ 示例
## 📍 简单示例
```html
<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>
```
假设 page 为以上页面的`MixPage`对象,我们可以用这些方法获取其中的元素:
```python
# 获取 id 为 one 的元素
div1 = page.ele('#one')
# 获取 div1 元素内所有 p 元素组成的列表
p_eles = div1.eles('tag:p')
# 获取 name 属性为 row1 的元素
p1 = page.ele('@name=row1')
# 获取 name 属性为 row2 且包含“第二”文本的 p 元素
p_ele = page.ele('tag:p@@text():第二@@name=row2')
# 获取包含“第二个div”文本的元素
div2 = page.ele('第二个div')
# 用 xpath 查找
div2 = page.ele('xpath://div[@id="tow"]')
# 从第一行元素用相对定位获取第三行元素
p3 = p1.next(2)
# 获取 p1 元素的父元素
parent = p1.parent()
# 获取 p1 后面的第一个 div 元素
div2 = p1.after(1, 'tag:div')
```
## 📍 实际示例
复制此代码可直接运行查看结果。
```python
from DrissionPage import MixPage
page = MixPage('s')
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)
"""输出:
全部推荐项目
前沿技术
智能硬件
IOT/物联网/边缘计算
车载应用
以下省略……
"""
```
# 查找元素方法
## 📍 `ele()`
此方法用于查找并返回第一个匹配的元素d 模式下返回`DriverElement`s 模式下返回`SessionElement`,用 xpath 获取元素属性时,直接返回属性文本。查找不到结果则返回`None`
**参数:**
- `loc_or_str`(元素对象拥有):元素的定位信息,可以是 loc 元组,或查询字符串
- `loc_or_ele`页面对象拥有元素的定位信息可以是元素对象loc 元组,或查询字符串
- `timeout`查找元素超时时间默认与元素所在页面等待时间一致s 模式下无效
**返回:** s 模式下返回`SessionElement`d 模式下返回`DriverElement`,或用 xpath 获取到的属性值
```python
# 在页面内查找元素
ele1 = page.ele('search text')
# 在元素内查找后代元素
ele2 = ele1.ele('search text')
# 使用 xpath 获取后代中第一个 div 元素的 class 属性
class = ele1.ele('xpath://div/@class')
```
## 📍 `eles()`
此方法与`ele()`相似,但返回的是匹配到的所有元素组成的列表,用 xpath 获取元素属性时,返回属性文本组成的列表。
**参数:**
- `loc_or_str`:元素的定位信息,可以是 loc 元组,或查询字符串
- `timeout`查找元素超时时间默认与元素所在页面等待时间一致s 模式下无效
**返回:** s 模式下返回`SessionElement`组成的列表d 模式下返回`DriverElement`组成的列表,或用 xpath 获取到的属性值组成的列表
```python
# 获取 ele 元素内的所有 p 元素
p_eles = ele.eles('tag:p')
# 打印第一个 p 元素
print(p_eles[0])
```
## 📍 `s_ele()`
此方法用于在一个元素下查找后代元素,以`SessionElement`形式返回结果xpath 获取属性值时依然是返回`str`),也可以直接将一个元素或页面转换为`SessionElement`版本。
这是为了 d 模式处理速度的提升,当爬取复杂页面信息而且不须要和元素进行交互时,生成整个页面或者主要容器元素的`SessionElement`,再在其中获取信息,可以将速度提升几个数量级。
s 模式下这个方法和`ele()`是一样的。
**参数:**
- `loc_or_str`(元素对象拥有):元素的定位信息,可以是 loc 元组,或查询字符串。为`None`时直接返回当前元素的`SessionElemnet`版本
- `loc_or_ele`(页面对象拥有):元素的定位信息,可以是 loc 元组,或查询字符串。为`None`时直接返回当前页面的 `SessionElemnet`版本
**返回:**`SessionElement`,或用 xpath 获取到的属性值
```python
# 获取元素或页面的的 SessionElement 版本
ele2 = ele1.s_ele()
ele2 = page.s_ele()
# 在 ele1 元素下查找元素,并以 SessionElemnet 返回
ele2 = ele1.s_ele('search text')
# 在页面下查找元素,并以 SessionElemnet 返回
ele = page.s_ele('search text')
```
## 📍 `s_eles()`
此方法与`s_ele()`相似,但返回的是匹配到的所有元素组成的列表,或属性值组成的列表。
**参数:**
- `loc_or_str`:元素的定位信息,可以是 loc 元组,或查询字符串(必填)
**返回:**`SessionElement`组成的列表,或用 xpath 获取到的属性值组成的列表
## 📍 `active_ele`
该属性返回当前页面焦点所在元素。d 模式独有。
```python
ele = page.active_ele
```
## 📍 `shadow_root`
`DriverElement`元素除了以上方法和属性外,还有`shadow_root`属性,用于获取其内部的 shadow_root 元素。
该属性返回的是一个`ShadowRootElement`,类似于`DriverElement`,功能比`DriverElement`少。但也有`ele()``eles()`方法,可直接搜索其下的元素,返回 `DriverElement`
元素。返回的`DriverElement`和普通的没有区别。
```python
# 获取一个元素下是 shadow root
shadow_ele = ele.shadow_root
# 获取该 shadow root 下的一个元素
ele1 = shadow_ele.ele('search text')
# 点击获取到的元素
ele1.click()
```
# 查找语法
我们使用一套简洁高效的语法去定位元素,大大简化了定位元素的代码量,增强了功能,也兼容 css selector、xpath、selenium 原生的 loc 元组s 模式也能用。d 模式和 s
模式定位元素的语法是完全一样的,便于模式切换时平滑过渡。
**匹配模式** 指字符串是否完全匹配,有以下两种:
## 📍 `=`
表示精确匹配,匹配完全符合的文本或属性。
## 📍 `:`
表示模糊匹配,匹配含有某个字符串的文本或属性。
**关键字** 是出现在定位语句最左边,用于指明该语句以哪种方式去查找元素,有以下这些:
## 📍 `#`
表示`id`属性,只在语句最前面且单独使用时生效,可配合`=``:`
```python
# 在页面中查找 id 属性为 ele_id 的元素
ele1 = page.ele('#ele_id')
# 在 ele1 元素内查找 id 属性包含 ele_id 文本的元素
ele2 = ele1.ele('#:ele_id')
```
## 📍 `.`
表示`class`属性,只在语句最前面且单独使用时生效,可配合`=``:`
```python
# 查找 class 属性为 ele_class 的元素
ele2 = ele1.ele('.ele_class')
# 查找 class 属性包含 ele_class 文本的元素
ele2 = ele1.ele('.:ele_class')
```
## 📍 `@`
表示某个属性,只匹配一个属性。
`@`关键字只有一个简单功能,就是匹配`@`后面的内容,不再对后面的字符串进行解析。因此即使后面的字符串也存在`@``@@`,也作为要匹配的内容对待。
!> **注意:**
如果属性中包含特殊字符,如包含`@`,用这个方式不能正确匹配到,须使用 css selector 方式查找。且特殊字符要用`\`转义。
```python
# 查找 name 属性为 ele_name 的元素
ele2 = ele1.ele('@name=ele_name')
# 查找 name 属性包含 ele_name 文本的元素
ele2 = ele1.ele('@name:ele_name')
# 查找有 name 属性的元素
ele2 = ele1.ele('@name')
# 查找没有任何属性的元素
ele2 = ele1.ele('@')
# 查找 emaile 属性为 abc@def.com 的元素,有多个 @ 也不会重复处理
ele2 = ele1.ele('@email=abc@def.com')
# 属性中有特殊字符的情形匹配abc@def属性等于v的元素
ele2 = ele1.ele('css:div[abc\@def="v"]')
```
## 📍 `@@`
表示某个属性,多属性匹配时使用,个数不限。还能匹配要忽略的元素,匹配文本时也和`@`不一样。
`@@`后跟 - 时,表示 not。如
- `@@-name`表示匹配没有`name`属性的元素
- `@@-name=ele_name`表示匹配`name`属性不为`ele_name`的元素
如有以下情况,不能使用此方式,须改用 xpath 的方式:
- 匹配文本或属性中出现`@@`
- 属性名本身以`-`开头
!> **注意:**
如果属性中包含特殊字符,如包含`@`,用这个方式不能正确匹配到,须使用 css selector 方式查找。且特殊字符要用`\`转义。
```python
# 查找 name 属性为 name 且 class 属性包含 cls 文本的元素
ele2 = ele1.ele('@@name=name@@class:cls')
# 查找没有 class 属性的元素
ele2 = ele1.ele('@@-class')
# 查找 name 属性不包含 ele_name 的元素
ele2 = ele1.ele('@@-name:ele_name')
```
## 📍 `text`
要匹配的文本,查询字符串如开头没有任何关键字,也表示根据传入的文本作模糊查找。
如果元素内有多个直接的文本节点,精确查找时可匹配所有文本节点拼成的字符串,模糊查找时可匹配每个文本节点。
```python
# 查找文本为 some text 的元素
ele2 = ele1.ele('text=some text')
# 查找文本包含 some text 的元素
ele2 = ele1.ele('text:some text')
# 与上一行一致
ele2 = ele1.ele('some text')
```
?> **Tips** <br>若要查找的文本包含`text:` ,可下面这样写,即第一个`text:` 为关键字,第二个是要查找的内容:
```python
ele2 = page.ele('text:text:')
```
## 📍 `text()`
作为查找属性时使用的文本关键字,必须与`@``@@`配合使用。
`@`配合和与`@@`配合使用时,`text()`的意义是有差别的。
`@text()`表示在元素的直接子文本节点中匹配,且多个节点不能合并匹配。 `@@text()`表示在元素内部所有文本中匹配,且会把元素内部所有文本拼成一个总字符串再进行匹配,因此可以模糊匹配元素里面任意文本。
```python
# 查找文本为 some text 的元素
ele2 = ele1.ele('@text()=some text')
# 查找文本包含 some text 的元素
ele2 = ele1.ele('@text():some text')
# 查找文本为 some text 且 class 属性为 cls 的元素
ele2 = ele1.ele('@@text()=some text@@class=cls')
# 查找文本为 some text 且没有任何属性的元素(因第一个 @@ 后为空)
ele2 = ele1.ele('@@@@text():some text')
# 查找直接子文本包含 some text 字符串的元素,和 text 关键字没有区别
ele = page.ele('@text():some text')
ele = page.ele('text:some text')
# 查找元素内部包含 some text 字符串的元素
ele = page.ele('@@text():some text')
```
## 📍 `tag`
表示元素的标签,只在语句最前面且单独使用时生效,可与`@``@@`配合使用。`tag:``tag=`效果一致。
```python
# 定位 div 元素
ele2 = ele1.ele('tag:div')
# 定位 class 属性为 cls 的 div 元素
ele2 = ele1.ele('tag:div@class=cls')
# 定位文本为 text 的 div 元素
ele2 = ele1.ele('tag:div@text()=text')
# 定位 class 属性为 cls 且文本为 text 的 div 元素
ele2 = ele1.ele('tag:div@@class=cls@@text()=text')
# 查找直接文本节点包含 text 字符串的 div 元素
ele2 = ele1.ele('tag:div@text():text')
# 查找内部文本节点包含 text 字符串的 div 元素
ele2 = ele1.ele('tag:div@@text():text')
```
?> **Tips:** <br>注意, `tag:div@text():text``tag:div@@text():text` 是有区别的,前者只在`div`的直接文本节点搜索,后者搜索`div`的整个内部。
## 📍 `css`
表示用 css selector 方式查找元素。`css:``css=`效果一致。
```python
# 查找 div 元素
ele2 = ele1.ele('css:.div')
# 查找 div 子元素元素,这个写法是本库特有,原生不支持
ele2 = ele1.ele('css:>div')
```
## 📍 `xpath`
表示用 xpath 方式查找元素。`xpath:``xpath=`效果一致。
该方法支持完整的 xpath 语法,能使用 xpath 直接获取元素属性selenium 不支持这种用法。
```python
# 查找 div 元素
ele2 = ele1.ele('xpath:.//div')
# 和上面一行一样,查找元素的后代时,// 前面的 . 可以省略
ele2 = ele1.ele('xpath://div')
# 获取 div 元素的 class 属性,返回字符串
txt = ele1.ele('xpath://div/@class')
```
?> **Tips:** <br>查找元素的后代时selenium 原生代码要求 xpath 前面必须加`.`,否则会变成在全个页面中查找。笔者觉得这个设计是画蛇添足,既然已经通过元素查找了,自然应该只查找这个元素内部的元素。所以,用 xpath
在元素下查找时,最前面`//``/`前面的`.`可以省略。
## 📍 selenium 的 loc 元组
查找方法能直接接收 selenium 原生定位元组进行查找s 模式下也支持这种写法。
```python
from selenium.webdriver.common.by import By
# 查找 id 为 ele_id 的元素
loc1 = (By.ID, 'ele_id')
ele = page.ele(loc1)
# 按 xpath 查找
loc2 = (By.XPATH, '//div[@class="ele_class"]'
ele = page.ele(loc2)
```
# ✔️ 等待
d 模式下所有查找元素操作都自带等待,默认为跟随元素所在页面`timeout`属性(默认 10 秒),也可以在每次查找时单独设置,单独设置的等待时间不会改变页面原来设置。
```python
# 页面初始化时设置查找元素超时时间为 15 秒
page = MixPage(timeout=15)
# 设置查找元素超时时间为 5 秒
page.timeout = 5
# 使用页面超时时间来查找元素5 秒)
ele1 = page.ele('some text')
# 为这次查找页面独立设置等待时间1 秒)
ele1 = page.ele('some text', timeout=1)
# 查找后代元素使用页面超时时间5 秒)
ele2 = ele1.ele('some text')
# 查找后代元素使用单独设置的超时时间1 秒)
ele2 = ele1.ele('some text', timeout=1)
```
# ✔️ DOM 内相对定位
以下方法可以以某元素为基准,在 DOM 中按照条件获取其兄弟元素、祖先元素、文档前后元素。
除获取元素外,还能通过 xpath 获取任意节点内容,如文本节点、注释节点。这在处理元素和文本节点混排的时候非常有用。
## 📍 `parent()`
此方法获取当前元素某一级父元素,可指定筛选条件或层数。
**参数:**
- `level_or_loc`:第几级父元素,或定位符
**返回:** 某层父元素
```python
# 获取 ele1 的第二层父元素
ele2 = ele1.parent(2)
# 获取 ele1 父元素中 id 为 id1 的元素
ele2 = ele1.parent('#id1')
```
## 📍 `next()`
此方法返回当前元素后面的某一个兄弟元素,可指定筛选条件和第几个。
**参数:**
- `index`:查询结果中的第几个
- `filter_loc`:用于筛选元素的查询语法
- `timeout`:查找元素的超时时间
**返回:** 本元素后面某个兄弟元素或节点文本
```python
# 获取 ele1 后面第一个兄弟元素
ele2 = ele1.next()
# 获取 ele1 后面第 3 个兄弟元素
ele2 = ele1.next(3)
# 获取 ele1 后面第 3 个 div 兄弟元素
ele2 = ele1.next(3, 'tag:div')
# 获取 ele1 后面第一个文本节点的文本
txt = ele1.next(1, 'xpath:text()')
```
## 📍 `nexts()`
此方法返回后面全部符合条件的兄弟元素或节点组成的列表,可用查询语法筛选。
**参数:**
- `filter_loc`:用于筛选元素的查询语法
- `timeout`:查找元素的超时时间
**返回:** 本元素前面符合条件的兄弟元素或节点文本组成的列表
```python
# 获取 ele1 后面所有兄弟元素
eles = ele1.nexts()
# 获取 ele1 后面所有 div 兄弟元素
divs = ele1.nexts('tag:div')
# 获取 ele1 后面的所有文本节点
txts = ele1.nexts('xpath:text()')
```
## 📍 `prev()`
此方法返回当前元素前面的某一个兄弟元素,可指定筛选条件和第几个。
**参数:**
- `index`:查询结果中的第几个
- `filter_loc`:用于筛选元素的查询语法
- `timeout`:查找元素的超时时间
**返回:** 本元素前面某个兄弟元素或节点文本
```python
# 获取 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()')
```
## 📍 `prevs()`
此方法返回前面全部符合条件的兄弟元素或节点组成的列表,可用查询语法筛选。
**参数:**
- `filter_loc`:用于筛选元素的查询语法
- `timeout`:查找元素的超时时间
**返回:** 本元素前面符合条件的兄弟元素或节点文本组成的列表
```python
# 获取 ele1 前面所有兄弟元素
eles = ele1.prevs()
# 获取 ele1 前面所有 div 兄弟元素
divs = ele1.prevs('tag:div')
```
## 📍 `after()`
此方法返回当前元素后面的某一个元素,可指定筛选条件和第几个。这个方法查找范围不局限在兄弟元素间,而是整个 DOM 文档。
**参数:**
- `index`:查询结果中的第几个
- `filter_loc`:用于筛选元素的查询语法
- `timeout`:查找元素的超时时间
**返回:** 本元素后面某个元素或节点
```python
# 获取 ele1 后面第 3 个元素
ele2 = ele1.after(3)
# 获取 ele1 后面第 3 个 div 元素
ele2 = ele1.after(3, 'tag:div')
# 获取 ele1 后面第一个文本节点的文本
txt = ele1.after(1, 'xpath:text()')
```
## 📍 `afters()`
此方法返回后面符合条件的全部元素或节点组成的列表,可用查询语法筛选。这个方法查找范围不局限在兄弟元素间,而是整个 DOM 文档。
**参数:**
- `filter_loc`:用于筛选元素的查询语法
- `timeout`:查找元素的超时时间
**返回:** 本元素后面符合条件的元素或节点组成的列表
```python
# 获取 ele1 后所有元素
eles = ele1.afters()
# 获取 ele1 前面所有 div 元素
divs = ele1.afters('tag:div')
```
## 📍 `before()`
此方法返回当前元素前面的某一个元素,可指定筛选条件和第几个。这个方法查找范围不局限在兄弟元素间,而是整个 DOM 文档。
**参数:**
- `index`:查询结果中的第几个
- `filter_loc`:用于筛选元素的查询语法
- `timeout`:查找元素的超时时间
**返回:** 本元素前面某个元素或节点
```python
# 获取 ele1 前面第 3 个元素
ele2 = ele1.before(3)
# 获取 ele1 前面第 3 个 div 元素
ele2 = ele1.before(3, 'tag:div')
# 获取 ele1 前面第一个文本节点的文本
txt = ele1.before(1, 'xpath:text()')
```
## 📍 `befores()`
此方法返回前面全部符合条件的元素或节点组成的列表,可用查询语法筛选。这个方法查找范围不局限在兄弟元素间,而是整个 DOM 文档。
**参数:**
- `filter_loc`:用于筛选元素的查询语法
- `timeout`:查找元素的超时时间
**返回:** 本元素前面符合条件的元素或节点组成的列表
```python
# 获取 ele1 前面所有元素
eles = ele1.befores()
# 获取 ele1 前面所有 div 元素
divs = ele1.befores('tag:div')
```
# ✔️ 页面布局相对定位
以下方法用于在页面上根据显示位置定位元素。只能有在 d 模式selenium4 版本,且 driver 版本较新的情况下才能使用。
与上面通过 DOM 定位的方法不同,这些方法只能获取元素,不能获取节点。
获取多个元素时,元素由近到远排列。
## 📍 `left()`
此方法返回当前元素左边的某一个元素,可指定筛选条件和第几个。
**参数:**
- `index`:查询结果中的第几个
- `filter_loc`:用于筛选元素的查询语法
**返回:** 本元素左边的某个元素
```python
# 获取 ele1 左边第 3 个元素
ele2 = ele1.left(3)
# 获取 ele1 左边第 3 个 div 元素
ele2 = ele1.left(3, 'tag:div')
```
## 📍 `lefts()`
此方法返回左边全部符合条件的元素组成的列表,可用查询语法筛选。
**参数:**
- `filter_loc`:用于筛选元素的查询语法
**返回:** 本元素左边符合条件的元素组成的列表
```python
# 获取 ele1 左边所有元素
eles = ele1.lefts()
# 获取 ele1 左边所有 div 元素
divs = ele1.lefts('tag:div')
```
## 📍 `right()`
此方法返回当前元素左边的某一个元素,可指定筛选条件和第几个。
**参数:**
- `index`:查询结果中的第几个
- `filter_loc`:用于筛选元素的查询语法
**返回:** 本元素左边的某个元素
```python
# 获取 ele1 左边第 3 个元素
ele2 = ele1.right(3)
# 获取 ele1 左边第 3 个 div 元素
ele2 = ele1.right(3, 'tag:div')
```
## 📍 `rights()`
此方法返回右边全部符合条件的元素组成的列表,可用查询语法筛选。
**参数:**
- `filter_loc`:用于筛选元素的查询语法
**返回:** 本元素右边符合条件的元素组成的列表
```python
# 获取 ele1 右边所有元素
eles = ele1.rights()
# 获取 ele1 右边所有 div 元素
divs = ele1.rights('tag:div')
```
## 📍 `below()`
此方法返回当前元素下边的某一个元素,可指定筛选条件和第几个。
**参数:**
- `index`:查询结果中的第几个
- `filter_loc`:用于筛选元素的查询语法
**返回:** 本元素下边的某个元素
```python
# 获取 ele1 下边第 3 个元素
ele2 = ele1.below(3)
# 获取 ele1 下边第 3 个 div 元素
ele2 = ele1.below(3, 'tag:div')
```
## 📍 `belows()`
此方法返回下边全部符合条件的元素组成的列表,可用查询语法筛选。
**参数:**
- `filter_loc`:用于筛选元素的查询语法
**返回:** 本元素下边符合条件的元素组成的列表
```python
# 获取 ele1 下边所有元素
eles = ele1.belows()
# 获取 ele1 下边所有 div 元素
divs = ele1.belows('tag:div')
```
## 📍 `above()`
此方法返回当前元素上边的某一个元素,可指定筛选条件和第几个。
**参数:**
- `index`:查询结果中的第几个
- `filter_loc`:用于筛选元素的查询语法
**返回:** 本元素上边的某个元素
```python
# 获取 ele1 上边第 3 个元素
ele2 = ele1.above(3)
# 获取 ele1 上边第 3 个 div 元素
ele2 = ele1.above(3, 'tag:div')
```
## 📍 `aboves()`
此方法返回上边全部符合条件的元素组成的列表,可用查询语法筛选。
**参数:**
- `filter_loc`:用于筛选元素的查询语法
**返回:** 本元素上边符合条件的元素组成的列表
```python
# 获取 ele1 上边所有元素
eles = ele1.aboves()
# 获取 ele1 上边所有 div 元素
divs = ele1.aboves('tag:div')
```
## 📍 `near()`
此方法返回最接近当前元素的某一个元素,可指定筛选条件和第几个。
**参数:**
- `index`:查询结果中的第几个
- `filter_loc`:用于筛选元素的查询语法
**返回:** 最接近本元素的某个元素
```python
# 获取最接近 ele1 的第 3 个元素
ele2 = ele1.near(3)
# 获取最接近 ele1 的第 3 个 div 元素
ele2 = ele1.near(3, 'tag:div')
```
## 📍 `nears()`
此方法返回该元素附近全部符合条件的元素组成的列表,可用查询语法筛选。
**参数:**
- `filter_loc`:用于筛选元素的查询语法
**返回:** 本元素附近符合条件的元素组成的列表
```python
# 获取 ele1 附近所有元素
eles = ele1.nears()
# 获取 ele1 附近所有 div 元素
divs = ele1.nears('tag:div')
```
# ✔️ `ShadowRootElement`相关查找
本库把 shadow-root 也作为元素对象看待,是为`ShadowRootElement`对象。对`ShadowRootElement`对象可与普通元素一样查找下级元素和 DOM 内相对定位,但不能用页面布局相对定位。
`ShadowRootElement`对象进行相对定位时,把它看作其父对象内部的第一个对象,其余定位逻辑与普通对象一致。
!> **注意:** <br>如果`ShadowRootElement`元素的下级元素中有其它`ShadowRootElement`元素,那这些下级`ShadowRootElement`
元素内部是无法直接通过定位语句查找到的,只能先定位到其父元素,再用`shadow-root`属性获取。
```python
# 获取一个 shadow-root 元素
sr_ele = page.ele('#app').shadow_root
# 在该元素下查找下级元素
ele1 = sr_ele.ele('tag:div')
# 用相对定位获取其它元素
ele1 = sr_ele.parent(2)
ele1 = sr_ele.next(1, 'tag:div')
ele1 = sr_ele.after(1, 'tag:div')
eles = sr_ele.nexts('tag:div')
# 定位下级元素中的 shadow+-root 元素
sr_ele2 = sr_ele.ele('tag:div').shadow_root
```
# ✔️ 简化写法
为进一步精简代码,对语法进行了精简
- 定位语法都有其简化形式。
- 页面和元素对象都实现了`__call__()`方法,可直接调用。
- 所有查找方法都支持链式操作
示例:
```python
# 定位到页面中 id 为 table_id 的元素,然后获取它的所有 tr 元素
eles = page('#table_id').eles('t:tr')
# 定位到 class 为 cls 的元素,然后在它里面查找文本为 text 的元素
ele2 = ele1('.cls1')('tx=text')
# 获取 ele1 的 shadow_root 元素,再在里面查找 class 属性为 cls 的元素
ele2 = ele1.sr('.cls')
# 按xpath 查找元素
ele2 = ele1('x://div[@class="ele_class"]')
```
简化写法对应列表
| 原写法 | 简化写法 |
|:-------------:|:------:|
| `text` | `tx` |
| `text()` | `tx()` |
| `tag` | `t` |
| `xpath` | `x` |
| `css` | `c` |
| `shadow_root` | `sr` |
# ✔️ Tips
- 从一个`DriverElement`元素获取到的`SessionElement`版本,依然能够使用相对定位方法定位祖先或兄弟元素。
- `SessionElement``SessionPage``ele()``eles()`方法也有`timeout`参数,但它是不生效的,仅用于保持与 d 模式元素书写一致,便于无差别的调用。
- 定位语句内容与关键字重复时,请使用 xpath 或 css selector 代替。

View File

@ -1,373 +0,0 @@
获取到须要的页面元素后,可以使用元素对象获取元素的信息。
本库有三种元素对象,分别是`DriverElement``ShadowRootElement``SessionElement`,前两者是 d 模式下通过浏览器页面元素生成,后者由静态文本生成。`DriverElement`
`SessionElement`的基本属性一致。
`ShadowRootElement`由于是 shadow dom 元素,属性比较其余两种少,下文单独介绍。
# ✔️ 示例
```python
from DrissionPage import MixPage
page = MixPage('s')
page.get('https://gitee.com/explore')
# 获取推荐目录下所有 a 元素
li_eles = page('tag:ul@@text():全部推荐项目').eles('t:a')
for i in li_eles: # 遍历列表
print(i.tag, i.text, i.attr('href')) # 获取并打印标签名、文本、href 属性
"""输出:
a 全部推荐项目 https://gitee.com/explore/all
a 前沿技术 https://gitee.com/explore/new-tech
a 智能硬件 https://gitee.com/explore/hardware
a IOT/物联网/边缘计算 https://gitee.com/explore/iot
a 车载应用 https://gitee.com/explore/vehicle
以下省略……
"""
```
# ✔️ 两种模式共有属性
假设`ele`为以下`div`元素的对象:
```html
<div id="div1">Hello World!
<p>行元素</p>
<!--这是注释-->
</div>
```
## 📍 `html`
此属性返回元素的`outerHTML`文本。
```python
html = ele.html
"""返回:
<div id="div1">Hello World!
<p>&#34892;&#20803;&#32032;</p>
<!--&#36825;&#26159;&#27880;&#37322;-->
</div>
"""
```
## 📍 `inner_html`
此属性返回元素的`innerHTML`文本。
```python
inner_html = ele.inner_html
"""返回:
Hello World!
<p>&#34892;&#20803;&#32032;</p>
<!--&#36825;&#26159;&#27880;&#37322;-->
"""
```
## 📍 `tag`
此属性返回元素的标签名。
```python
tag = ele.tag
# 返回div
```
## 📍 `text`
此属性返回元素内所有文本组合成的字符串。
该字符串已格式化,即已转码,已去除多余换行符,符合人读取习惯,便于直接使用。无须重复写处理代码。
```python
text = ele.text
"""返回:
Hello World!
行元素
"""
```
## 📍 `raw_text`
此属性返回元素内原始文本。
```python
text = ele.raw_text
"""返回(注意保留了元素间的空格和换行):
Hello World!
行元素
"""
```
## 📍 `texts()`
此方法返回元素内所有直接子节点的文本,包括元素和文本节点。 它有一个参数`text_node_only`,为`True`时则只获取只返回文本节点。这个方法适用于获取文本节点和元素节点混排的情况。
**参数:**
- `text_node_only`:是否只返回文本节点
**返回:** 文本列表
```python
texts = ele.texts()
print(e.texts(text_node_only=True))
print(e.texts())
```
**输出:**
```console
['Hello World!', '行元素']
['Hello World!']
```
## 📍 `comments`
```python
comments = ele.comments
# 返回:[<!--这是注释-->]
```
## 📍 `attrs`
此属性以字典形式返回元素所有属性及值。
```python
attrs = ele.attrs
# 返回:{'id': 'div1'}
```
## 📍 `attr()`
此方法返回元素某个`attribute`属性值。它接收一个字符串参数`attr`,返回该属性值文本,无该属性时返回`None`
此属性返回的`src``href`属性为已补充完整的路径。`text`属性为已格式化文本,`innerText`属性为未格式化文本。
**参数:**
- `attr`:属性名称
**返回:** 属性值文本
```python
ele_id = ele.attr('id')
# 返回div1
```
## 📍 `link`
此方法返回元素的 href 属性或 src 属性,没有这两个属性则返回`None`
```html
<a href='http://www.baidu.com'>百度</a>
```
假设`a_ele`为以上元素的对象:
```python
link = a_ele.link
# 返回http://www.baidu.com
```
## 📍 `page`
此属性返回元素所在的页面对象。
- 由页面对象生成的元素返回该页面对象,如`MixPage``DriverPage``SessionPage`
- 由 html 文本直接生成的`SessionElement``page`属性为`None`
```python
page = ele.page
```
## 📍 `inner_ele`
此属性返回被当前元素包装的对应模式原本的元素对象。
d 模式包装的是一个 selenium 的`WebElement`对象s 模式包装的是一个 lxml 的`HtmlElement`对象。
获取该对象,可直接使用该对象以该库的原生代码来执行须要的操作,便于不同项目的对接,及使用本库没有封装的功能。
```python
# 获取元素的 WebElement 对象
web_ele = ele.inner_ele
# 使用 selenium 原生的元素点击方法
web_ele.find_element(By.ID, 'ele_id').click()
```
## 📍 `xpath`
此属性返回当前元素在页面中 xpath 的绝对路径。
```python
xpath = ele.xpath
# 返回:/html/body/div
```
## 📍 `css_path`
此属性返回当前元素在页面中 css selector 的绝对路径。
```python
css = ele.css_path
# 返回::nth-child(1)>:nth-child(1)>:nth-child(1)
```
## 📍 `is_valid()`
此方法返回当前元素是否可用。`SessionElement`始终返回`True``DriverElement`视乎元素是否还在 DOM 内返回布尔值。
**参数:** 无
**返回:**`bool`类型
```python
is_valid = ele.is_valid()
```
# ✔️ DriverElement 独有属性
## 📍 `size`
此属性以字典形式返回元素的大小。
```python
size = ele.size
# 返回:{'height': 50, 'width': 50}
```
## 📍 `location`
此属性以字典形式返回元素坐标。
```python
loc = ele.location
# 返回:{'x': 50, 'y': 50}
```
## 📍 `pseudo_before`
此属性以文本形式返回当前元素的`::before`伪元素内容。
```python
before_txt = ele.pseudo_before
```
## 📍 `pseudo_after`
此属性以文本形式返回当前元素的`::after`伪元素内容。
```python
after_txt = ele.pseudo_after
```
## 📍 `style()`
该方法返回元素 css 样式属性值,可获取伪元素的属性。它有两个参数,`style`参数输入样式属性名称,`pseudo_ele`参数输入伪元素名称,省略则获取普通元素的 css 样式属性。
**参数:**
- `style`:样式名称
- `pseudo_ele`:伪元素名称(如有)
**返回:** 样式属性值
```python
# 获取 css 属性的 color 值
prop = ele.style('color')
# 获取 after 伪元素的内容
prop = ele.style('content', 'after')
```
## 📍 `prop()`
此方法返回`property`属性值。它接收一个字符串参数,返回该参数的属性值。
**参数:**
- `prop`:属性名称
**返回:** 属性值
```python
prop = ele.prop('value')
```
## 📍 `select`
此属性返回 `select` 元素用于处理下拉列表的 Select 类(“[元素操作](元素操作.md)”章节说明),非下拉列表元素返回`False`
## 📍 `is_selected()`
此方法以布尔值返回元素是否选中。
**参数:** 无
**返回:** `bool`
```python
selected = ele.is_selected()
```
## 📍 `is_enabled()`
此方法以布尔值返回元素是否可用。
**参数:** 无
**返回:**`bool`
```python
enable = ele.is_enabled()
```
## 📍 `is_displayed()`
此方法以布尔值返回元素是否可见。
**参数:** 无
**返回:**`bool`
```python
displayed = ele.is_displayed()
```
# ✔️ `ShadowRootElement`属性与方法
本库把 shadow dom 的`root`看作一个元素处理,可以获取属性,也可以执行其下级的查找,但其属性比较少。有如下这些:
## 📍 `tag`
此属性返回元素标签名,即`'shadow-root'`
## 📍 `html`
此属性返回`shadow_root`的 html 文本,由`<shadow_root></shadow_root>` 标签包裹。
## 📍 `inner_html`
此属性返回`shadow_root`内部的 html 文本。
## 📍 `page`
此属性返回元素所在页面对象。
## 📍 `inner_ele`
此属性返回对象中保存的`shadow-root`元素。
## 📍 `parent_ele`
这是`ShadowRootElement`独有的属性,返回它所依附的普通元素对象。
## 📍 `is_enabled()`
`DriverElement`一致。
## 📍 `is_valid()`
`DriverElement`一致。

View File

@ -1,519 +0,0 @@
d 模式下的`DriverElement`对象可以对浏览器相应元素进行控制。
# ✔️ 元素操作方法
## 📍 `click()`
此方法用于点击元素。可以选择是否用 js 方式点击,可以在点击失败时自动重试。默认情况下,使用 selenium 原生的点击方法,如果重试超过限定时间,自动改用 js 方式点击。可通过`by_js`参数设置点击方式。
此设计除了可保证点击成功,还可以用于检测页面上的遮罩层是否消失。遮罩层经常出现在 js 方式翻页的时候,它出现的时候会阻碍 selenium
的原生点击,所以可以通过对其下面的元素不断重试点击,来判断遮罩层是否存在。当然,这个方法是否可行要看具体网页设计。
而如果直接使用 js 进行点击,则可无视任何遮挡,只要元素在 DOM 内,就能点击得到,这样可以根据须要灵活地对元素进行操作。
!> 注意:<br>使用 js 方式点击时,是不会进行重试的。
**参数:**
- `by_js`:是否用 js 方式点击,为`None`时先用 selenium 原生方法点击,重试失败超时后改为用 js 点击;为`True`时直接用 js 点击;为`False`时即使重试超时也不会改用 js。
- `timeout`:点击失败重试超时时间,为`None`时使用父页面`timeout`设置。
**返回:**`bool`,表示是否点击成功。
```python
# 点击一个元素,重试超时根据所在页面元素而定,都失败就改用 js 点击
ele.click()
# 点击一个元素,重试 5 秒,都失败也不改用 js 点击
ele.click(by_js=False, timeout=5)
# 假设遮罩层出现ele 是遮罩层下方的元素
ele.click(timeout = 10) # 不断重试点击,直到遮罩层消失,或到达 10 秒
ele.click(by_js=True) # 无视遮罩层,直接用 js 点击下方元素
```
## 📍 `click_at()`
此方法用于带偏移量点击元素,偏移量相对于元素左上角坐标。不传入`x``y`值时点击元素中点。可选择是否用 js 方式点击,但不会进行重试。
可用于点击一些奇怪的东西,比如用伪元素表示的控件。
点击的目标不一定在元素上,可以传入负值,或大于元素大小的值,点击元素附近的区域。向右和向下为正值,向左和向上为负值。
**参数:**
- `x`:相对元素左上角坐标的 x 轴偏移量
- `y`:相对元素左上角坐标的 y 轴偏移量
- `by_js`:是否用 js 点击
**返回:**`None`
```python
# 点击元素右上方 50*50 的位置
ele.click_at(50, -50)
# 点击元素上中部x 相对左上角向右偏移50y 保持在元素中点
ele.click_at(x=50)
# 点击元素中点,和 click() 相似,但没有重试功能
ele.click_at()
```
## 📍 `r_click()`
此方法实现右键单击元素。
**参数:** 无
**返回:**`None`
```python
ele.r_click()
```
## 📍 `r_click_at()`
此方法用于带偏移量右键点击元素,用法和`click_at()`相似,但没有`by_js`参数。
**参数:**
- `x`相对元素左上角坐标的x轴偏移量
- `y`相对元素左上角坐标的y轴偏移量
**返回:** `None`
```python
# 点击距离元素左上角 50*50 的位置(位于元素内部)
ele.r_click_at(50, 50)
```
## 📍 `input()`
此方法用于向元素输入文本或组合键,也可用于输入文件路径到`input`元素(文件间用`\n`间隔)。可选择输入前是否清空元素。
insure 参数为 `True` 时可自动确保输入正确。该功能是为了应对 selenium 原生输入在某些i情况下会失效的问题。但只能用于 input 元素且 `type``text` 的情况。 接收组合键的时候可接收
selenium 的 `Keys` 对象的值。组合键要放在一个 `tuple` 中传入。
!> **注意:** <br>`insure``True` 时不能用于接收组合键。
?> **Tips:** <br>- 有些文本框可以接收回车代替点击按钮,可以直接在文本末尾加上`'\n'`<br>- 非传入`tuple`时,会自动把非`str`转换为`str`
**参数:**
- `vals`:文本值或按键组合
- `clear`:输入前是否清空文本框
- `insure`:是否确保输入正确,不能用于输入组合键
- `timeout`:尝试输入的超时时间,不指定则使用父页面的超时时间,只在 `insure_input` 参数为 `True` 时生效
**返回:**`bool`类型,表示是否成功输入。`insure``False` 时始终返回 `True`
```python
# 输入文本
ele.input('Hello world!')
# 输入文本并回车
ele.input('Hello world!\n')
# 输入组合键
from selenium.webdriver import Keys
ele.input((Keys.CONTROL, 'a'), insure=False)
# 向上传文本控件输入文本路径(传入多个路径)
ele.input('D:\\test1.txt\nD:\\test2.txt')
```
## 📍 `clear()`
此方法用于清空元素文本,可使用确保清空的方式,若元素是不可编辑的,返回`None`
**参数:**
- `insure`:是否确保清空。为`True`则用`input()`确保值变成`''`,为`False`则用 selenium 元素`clear()`方法
**返回:**`bool`,是否清空成功,不能清空的元素返回`None`
```python
ele.clear()
```
## 📍 `run_script()`
此方法用于对元素执行 js 代码,代码中用`arguments[0]`表示自己。
**参数:**
- `script`js 文本
- `*args`:传入 js 的参数
**返回:** js 执行的结果
```python
# 用执行 js 的方式点击元素
ele.run_script('arguments[0].click()')
```
## 📍 `wait_ele()`
此方法用于等待当前元素的某个下级元素到达某种状态。
调用此方法返回一个`ElementWaiter`对象,调用该对象方法实现各种方式的等待。
**参数:**
- `loc_or_ele`:要等待的元素,可以是元素或定位符
- `timeout`:等待超时时间,默认使用页面超时时间
**方法:**
| 方法 | 参数说明 | 功能 |
|:---------:|:----:|:------------:|
| display() | 无 | 等待元素从 DOM 显示 |
| hidden() | 无 | 等待元素从 DOM 隐藏 |
| delete() | 无 | 等待元素从 DOM 删除 |
这些方法返回布尔值,代表是否等待成功。
```python
# 等待 id 为 div1 的元素显示,超时使用页面设置
ele.wait_ele('#div1').display()
# 等待 id 为 div1 的元素被删除(使用 loc 元组设置超时3秒
ele.wait_ele((By.ID, 'div1'), 3).delete()
# 等待已获取到的元素被隐藏
ele2 = ele1.ele('#div1')
ele1.wait_ele(ele2).hidden()
```
## 📍 `screenshot()`
此方法用于对元素进行截图。
如果是图片元素,会自动等待加载结束才截图。
此方法能自动获取能够使用的文件名,避免重名覆盖原有文件。并返回保存路径。
保存格式为 png也可以返回图片的二进制文本。
**参数:**
- `path`:图片保持路径
- `filename`:图片文件名,不传入时以元素`tag`标签命名
- `as_bytes`是否已字节形式返回图片为True时上面两个参数失效
**返回:** 图片完整路径或字节文本
```python
path = ele.screenshot(r'D:\tmp', 'img1') # 保存到路径文件名为img1.png
bytes = ele.screenshot(as_bytes=True) # 返回截图二进制文本
```
## 📍 `set_prop()`
此方法用于设置元素`property`属性。
**参数:**
- `prop` 属性名
- `value` 属性值
**返回:**`bool`,是否设置成功
```python
ele.set_prop('value', 'Hello world!')
```
## 📍 `set_attr()`
此方法用于设置元素`attribute`属性。
**参数:**
- `attr`:属性名
- `value`:属性值
**返回:**`bool`,是否设置成功
```python
ele.set_attr('href', 'http://www.gitee.com')
```
## 📍 `remove_attr()`
此方法用于删除元素`attribute`属性。
**参数:**
- `attr`:属性名
**返回:**`bool`,是否删除成功
```python
ele.remove_attr('href')
```
## 📍 `submit()`
此方法用于提交表单,若元素不在表单内,返回`None`,否则返回`True`
**参数:** 无
**返回:**`True``None`
```python
ele.submit()
```
## 📍 `drag()`
此方法用于拖拽元素到相对于当前的一个新位置,可以设置速度,可以选择是否随机抖动。
**参数:**
- `x`x 变化值
- `y`y 变化值
- `speed`:拖动的速度,传入 0 即瞬间到达
- `shake`:是否随机抖动
**返回:**`None`
```python
# 拖动当前元素到距离 50*50 的位置,速度为 100不随机抖动
ele.drag(50, 50, 100, False)
```
## 📍 `drag_to()`
此方法用于拖拽元素到另一个元素上或一个坐标上。
**参数:**
- `ele_or_loc` 另一个元素或坐标元组,接收元素时坐标是元素中点的坐标。可接收 selenium 的`WebElement`或本库的`DriverElement`
- `speed` 拖动的速度,传入 0 即瞬间到达
- `shake` 是否随机抖动
**返回:**`None`
```python
# 把 ele1 拖拽到 ele2 上
ele1 = page.ele('#div1')
ele2 = page.ele('#div2')
ele1.drag_to(ele2)
# 把 ele1 拖拽到网页 50, 50 的位置
ele1.drag_to((50, 50))
```
## 📍 `scroll`
此属性用于以某种方式滚动元素中的滚动条。
调用此属性返回一个`Scroll`对象,调用该对象方法实现各种方式的滚动。
| 方法 | 参数说明 | 功能 |
|:-----------------:|:------:|:----------------:|
| to_top() | 无 | 滚动到顶端,水平位置不变 |
| to_bottom() | 无 | 滚动到底端,水平位置不变 |
| to_half() | 无 | 滚动到垂直中间位置,水平位置不变 |
| to_rightmost() | 无 | 滚动到最右边,垂直位置不变 |
| to_leftmost() | 无 | 滚动到最左边,垂直位置不变 |
| to_location(x, y) | 滚动条坐标值 | 滚动到指定位置 |
| up(pixel) | 滚动的像素 | 向上滚动若干像素,水平位置不变 |
| down(pixel) | 滚动的像素 | 向下滚动若干像素,水平位置不变 |
| right(pixel) | 滚动的像素 | 向左滚动若干像素,垂直位置不变 |
| left(pixel) | 滚动的像素 | 向右滚动若干像素,垂直位置不变 |
```python
# 滚动到底部
ele.scroll.to_bottom()
# 滚动到最右边
ele.scroll.to_rightmost()
# 向下滚动 200 像素
ele.scroll.down(200)
# 滚动到指定位置
ele.scroll.to_location(100, 300)
```
## 📍 `hover()`
此方法用于模拟鼠标悬停在元素上,可接受偏移量,偏移量相对于元素左上角坐标。不传入`x``y`值时悬停在元素中点。`x``y`值可接受负值。
**参数:**
- `x`:相对元素左上角坐标的 x 轴偏移量
- `y`:相对元素左上角坐标的 y 轴偏移量
**返回:**`None`
```python
# 悬停在元素右上方 50*50 的位置
ele.hover(50, -50)
# 悬停在元素上中部x 相对左上角向右偏移50y 保持在元素中点
ele.hover(x=50)
# 悬停在元素中点
ele.hover()
```
# ✔️ `Select`
`select`元素的操作较为复杂,因此专门做了一个类用于处理它。每个`DriverElement`都有`select`属性,如果是`select`元素,该属性是一个`Select`类,否则该属性为`False`
`select`元素要操作列表时,实际上是对这个`Select`对象进行操作。
```python
ele.select
```
?> **Tips:** <br>网页操作中经常遇到动态变化的表单,这时需要等待列表项加载。`Select`类内置等待功能,默认为页面等待时间。
## 📍 属性
### 📎 `is_multi`
该属性表示当前`select`元素是否多选列表。
```python
ele.select.is_multi
```
### 📎 `options`
该属性以列表形式返回当前`select`元素下所有列表项元素对象,这些对象是`DriverElement`
```python
options = ele.select.options
```
### 📎 `selected_option`
该属性返回第一个被选中的元素对象。
```python
option_ele = ele.select.selected_option
```
### 📎 `selected_options`
该属性以列表形式返回第所有被选中的元素对象。如果是单选列表,返回一个列表
```python
options = ele.select.select.selected_options
```
## 📍 方法
### 📎 `select()`
该方法用于选定下拉列表中子元素。
接收`int`类型时根据序号选择,接收`str`类型时根据文本选择。
接收`list``tuple`时同时选择多个项,多项可序号和文本混排。只能在多选列表使用。
Select 类的`__call__()`方法直接调用这个方法,因此可以直接`ele.select()`来替代这个方法。写法更直观。
**参数:**
- `text_or_index`:根据文本或序号择选项,若允许多选,传入`list``tuple`可多选
- `timeout`:等待列表项出现的时间
**返回:** 是否选择成功
```python
# 根据文本选择下拉列表项等待1秒
ele.select('text', timeout=1)
# 选择第一个下拉列表项
ele.select(0)
# 选择多个文本项
ele.select(('text1', 'text2'))
# 选择多个序号
ele.select((0, 2))
# 文本和序号混合选择
ele.select(('text1', 2))
```
### 📎 `select_by_value()`
**参数:**
- `value``value`属性值,若允许多选,传入`list``tuple`可多选
- `timeout`:等待列表项出现的时间
**返回:** 是否选择成功
此方法用于根据`value`值选择项。当元素是多选列表时,可以接受`list``tuple`,同时选择多个项,可和序号混排。
```python
# 根据 value 值选择列表项
ele.select.select_by_value('value1')
# 根据 value 值选择多个项
ele.select.select_by_value(('value1', 2))
```
### 📎 `deselect()`
此方法用于取消选定下拉列表中子元素。当元素是多选列表时,可以接受`list``tuple`,同时取消选择多个项。
**参数:**
- `text_or_index`:根据文本、值选或序号择选项,若允许多选,传入`list``tuple`可多选
- `timeout`:等待列表项出现的时间
**返回:** 是否取消选择成功
```python
# 根据文本取消选择下拉列表项
ele.select.deselect('text')
# 取消选择第一项
ele.select.deselect(0)
# 取消选择多个文本项
ele.select.deselect(('text1', 'text2'))
# 取消选择多个序号
ele.select.deselect((0, 1))
# 文本和序号混合取消选择
ele.select.deselect(('text1', 2))
```
### 📎 `deselect_by_value()`
此方法用于根据`value`值取消选择项。当元素是多选列表时,可以接受`list``tuple`,同时取消选择多个项,可和序号混排。
**参数:**
- `value``value`属性值,若允许多选,传入`list``tuple`可取消多项
- `timeout`:等待列表项出现的时间
**返回:** 是否取消选择成功
```python
# 根据 value 值取消选择列表项
ele.select.deselect_by_value('value1')
# 根据 value 值取消选择多个项
ele.select.deselect_by_value(('value1', 2))
```
## 📍 多项列表独有功能
### 📎 `clear()`
此方法用于清空多选列表选项。
**参数:** 无
**返回:**`None`
```python
ele.select.clear()
```
### 📎 `invert()`
此方法用于反选多选列表选项。
**参数:** 无
**返回:**`None`
```python
ele.select.invert()
```

View File

@ -1,181 +0,0 @@
本节介绍获取页面对象各种信息的属性和方法。
# ✔️ 两种模式共有属性
## 📍 `url`
此属性返回当前访问的 url。
## 📍 `mode`
此属性返回当前页面对象的模式,`'s'``'d'`
## 📍 `drission`
此属性返回当前页面对象使用的`Drission`对象。
## 📍 `driver`
此属性返回当前页面对象使用的`WebDriver`对象。访问时会自动切换到 d 模式。
## 📍 `session`
此属性返回当前页面对象使用的`Session`对象。访问时不会切换模式。
## 📍 `cookies`
此属性以`dict`方式返回当前页面所使用的 cookies。
d 模式只返回当前标签页的 cookiess 模式则只返回当前访问的 url 的`cookies`
## 📍 `get_cookies()`
此方法获取 cookies 并以 cookie 组成的`list`形式返回。
**参数:**
- `as_dict`:是否以字典方式返回,为`False`返回`cookie`组成的`list`
- `all_domains`:是否返回所有域的`cookies`,只有 s 模式下生效
**返回:**`cookies`信息
```python
from DrissionPage import MixPage
p = MixPage('s')
p.get('http://www.baidu.com')
p.get('http://gitee.com')
for i in p.get_cookies(as_dict=False, all_domains=True):
print(i)
```
**输出:**
```
{'domain': '.baidu.com', 'domain_specified': True, ......}
......
{'domain': 'gitee.com', 'domain_specified': False, ......}
......
```
## 📍 `html`
此属性返回当前页面 html 文本。
## 📍 `title`
此属性返回当前页面`title`文本。
## 📍 `timeout`
s 模式下,此属性代表网络请求超时时间。
d 模式下,此属性为元素查找、点击、处理提示框等操作的超时时间。
默认为 10可对其赋值。
```python
# 创建 MixPage 对象时指定
page = MixPage(timeout=5)
# 修改 timeout
page.timeout = 20
```
## 📍 `retry_times`
此参数为网络连接失败时的重试次数。默认为 3可对其赋值。
```python
# 修改重试次数
page.retry_times = 5
```
## 📍 `retry_interval`
此参数为网络连接失败时的重试等待间隔秒数。默认为 2可对其赋值。
```python
# 修改重试等待间隔时间
page.retry_interval = 1.5
```
## 📍 `url_available`
此属性以布尔值返回当前链接是否可用。
s 模式下根据`Response`对象的`status_code`判断。
d 模式根据`check_page()`方法返回值判断。
# ✔️ s 模式独有属性
## 📍 `response`
此属性为 s 模式请求网站后生成的`Response`对象,本库没实现的功能可直接获取此属性调用 requests 库的原生功能。
```python
# 打印连接状态
r = page.response
print(r.status_code)
```
## 📍 `json`
此属性把请求内容解析成 json。
比如请求接口时,返回内容是 json 格式,那就可以用这个属性获取。
事实上,用 html 属性获取也是可以的,不过 html 属性没有对文本进行解析。
# ✔️ d 模式独有属性
## 📍 `timeouts`
此属性以字典方式返回三种超时时间selenium 4 以上版本可用。
`'implicit'`用于元素查找、点击重试、输入文本重试、处理弹出框重试等;
`'pageLoad'`用于等待页面加载;
`'script'`用于等待脚本执行。
```python
print(page.timeouts)
```
**输出:**
```
{'implicit': 10, 'pageLoad': 30.0, 'script': 30.0}
```
## 📍 `tabs_count`
此属性返回当前浏览器标签页数量。
## 📍 `tab_handles`
此属性以列表形式返回当前浏览器所有标签页的 handle。
!> **注意:**
以下情况会导致获取到的顺序与视觉效果不一致1、自动化过程中手动点击标签页2、浏览器被接管时已打开一个以上标签页。
## 📍 `current_tab_index`
此属性返回当前标签页的序号。
!> **注意:** <br>以下情况会导致获取到的排序号与视觉效果不一致1、自动化过程中手动点击标签页2、浏览器被接管时已打开一个以上标签页。
## 📍 `current_tab_handle`
此属性返回当前标签页的 handle。
## 📍 `active_ele`
此属性返回当前页面上焦点所在元素。类型为`DriverElement`
## 📍 `wait_obj`
此属性返回当前页面对象用于等待的对象。
## 📍 `chrome_downloading()`
此方法返回浏览器下载中的文件列表。
**参数:**
- `path:` 下载文件夹路径,默认从配置信息读取
**返回:** 下载中的文件列表

View File

@ -1,600 +0,0 @@
本节介绍页面对象可以使用的方法,有些方法是两种模式共有的,有些只有某种模式可以使用。当调用独有方法时,会自动切换到该模式。如调用`post()`方法时,会先切换到 s 模式,并同步登录信息。
# ✔️ 两种模式共有的方法
## 📍 `get()`
此方法用于跳转到一个 url详细用法见“访问网页”章节。
## 📍 `change_mode()`
此方法用于转换`MixPage`对象的模式。
切换后默认在目标模式重新跳转到原模式所在 url。
!> **注意:** <br>s 模式转 d 模式时,若浏览器当前网址域名和 s 模式不一样,必定会跳转。
**参数:**
- `mode`:目标模式字符串,`'s'``'d'`,默认转换到另一种
- `go`:转换后是否跳转到原模式所在 url
**返回:**`None`
```python
from DrissionPage import MixPage
from time import sleep
# 创建页面对象,默认 d 模式
page = MixPage()
# 访问个人中心页面(未登录,重定向到登录页面)
page.get('https://gitee.com/profile')
# 打印当前模式和登录前的 title
print('当前模式:', page.mode)
print('登录前title', page.title, '\n')
# 使用 d 模式输入账号密码登录
page.ele('#user_login').input('your_user_name')
page.ele('#user_password').input('your_password\n')
sleep(1)
# 切换到 session 模式
page.change_mode()
# 打印当前模式和登录后的 title
print('当前模式:', page.mode)
print('登录后title', page.title)
```
**输出:**
```
当前模式d
登录前title 登录 - Gitee.com
当前模式s
登录后title 个人资料 - 码云 Gitee.com
```
## 📍 `set_cookies()`
此方法用于设置`cookies`
可以接收`CookieJar``list``tuple``str``dict`格式的`cookies`
**参数:**
- `cookies``cookies`信息
- `refresh`:设置`cookies`后是否刷新页面
**返回:**`None`
```python
cookies = {'name': 'abc'}
page.set_cookies(cookies)
```
## 📍 `cookies_to_session()`
此方法用于从`WebDriver`对象复制`cookies``Session`对象。
**参数:**
- `copy_user_agent`:是否同时复制 user agent 信息
**返回:**`None`
## 📍 `cookies_to_driver()`
此方法用于从`Session`对象复制`cookies``WebDriver`对象。
**参数:**
- `url`:指定 url默认用当前 url
**返回:**`None`
## 📍 `download()`
此方法用于下载文件,详细用法见“下载文件”章节。
## 📍 `close_driver()`
此方法用于关闭`WebDriver`对象和浏览器。
**参数:** 无
**返回:**`None`
## 📍 `close_session()`
此方法用于关闭`Session`对象。
**参数:** 无
**返回:**`None`
# ✔️ s 模式独有方法
## 📍 `post()`
此方法用于以 post 方式访问 url具体用法见“访问网页”章节。
# ✔️ d 模式独有方法
## 📍 `hide_browser()`
此方法用于隐藏浏览器进程窗口,非最小化,任务栏上也可隐藏。
但若操作过程中有新增标签页,浏览器会重新出现。 只支持 Windows 系统,且只有设置了`local_port``debugger_address`时生效。
**参数:** 无
**返回:**`None`
## 📍 `show_browser()`
此方法用于显示被隐藏的浏览器进程。
只支持 Windows 系统,且只有设置了`local_port``debugger_address`时生效。
**参数:** 无
**返回:**`None`
## 📍 `wait_ele()`
此方法用于等待元素到达某种状态。
调用此方法返回一个`ElementWaiter`对象,调用该对象方法实现各种方式的等待。
**参数:**
- `loc_or_ele`:要等待的元素,可以是元素或定位符
- `timeout`:等待超时时间,默认使用页面超时时间
**方法:**
| 方法 | 参数说明 | 功能 |
|:-----------:|:----:|:------------:|
| `display()` | 无 | 等待元素从 DOM 显示 |
| `hidden()` | 无 | 等待元素从 DOM 隐藏 |
| `delete()` | 无 | 等待元素从 DOM 删除 |
这些方法返回布尔值,代表是否等待成功。
```python
# 等待 id 为 div1 的元素显示,超时使用页面设置
page.wait_ele('#div1').display()
# 等待 id 为 div1 的元素被删除(使用 loc 元组设置超时3秒
page.wait_ele((By.ID, 'div1'), 3).delete()
# 等待已获取到的元素被隐藏
ele = page.ele('#div1')
paeg.wait_ele(ele).hidden()
```
## 📍 `run_script()`
此方法用于执行 js 脚本。
**参数:**
- `script`js 脚本文本
- `*args`:传入的参数
**返回:** 脚本执行结果
```python
# 执行 js 脚本显示弹出框显示 Hello world!
page.run_script('alert(arguments[0]+arguments[1])', 'Hello', ' world!')
```
## 📍 `run_async_script()`
此方法用于以异步方式执行 js 脚本。
**参数:**
- `script`js 脚本文本
- `*args`:传入的参数
**返回:** 脚本执行结果
## 📍 `run_cdp()`
此方法用于执行 Chrome DevTools Protocol 语句。cdp
用法详见[Chrome DevTools Protocol](https://chromedevtools.github.io/devtools-protocol/)。
**参数:**
- `cmd`:协议项目方法
- `**cmd_args`:项目参数
**返回:** 该语句返回的值
```python
# 停止页面加载
page.run_cdp('Page.stopLoading')
```
## 📍 `set_timeouts()`
此方法用于设置三种超时时间selenium 4 以上版本生效。
**参数:**
- `implicit`:查找元素超时时间
- `pageLoad`:页面加载超时时间
- `script`:脚本运行超时时间
**返回:**`None`
## 📍 `set_ua_to_tab()`
此方法用于为当前 tab 设置 user agent只在当前 tab 有效。
**参数:**
- `ua`user agent字符串
**返回:**`None`
## 📍 `get_session_storage()`
此方法用于获取 sessionStorage 信息,可获取全部或单个项。
**参数:**
- `item`:要获取的项,不设置则返回全部
**返回:** sessionStorage 一个或所有项内容
## 📍 `get_local_storage()`
此方法用于获取 localStorage 信息,可获取全部或单个项。
**参数:**
- `item`:要获取的项,不设置则返回全部
**返回:** localStorage 一个或所有项内容
## 📍 `set_session_storage()`
此方法用于设置或删除某项 sessionStorage 信息。
**参数:**
- `item`:要设置的项
- `value`项的值设置为False时删除该项
**返回:**`None`
## 📍 `set_local_storage()`
此方法用于设置或删除某项 localStorage 信息。
**参数:**
- `item`:要设置的项
- `value`项的值设置为False时删除该项
**返回:**`None`
## 📍 clean_cache()
此方法用于清除缓存,可选要清除的项。
**参数:**
- `session_storage`:是否清除 sessionstorage
- `local_storage`:是否清除 localStorage
- `cache`:是否清除 cache
- `cookies`:是否清除 cookies
**返回:**`None`
## 📍 `to_frame`
此属性用于将页面焦点移到某个`frame``iframe`
调用此属性返回一个`ToFrame`对象,调用该对象的方法实现焦点转移。
这些方法返回值为当前页面对象,可实现下一步的链式操作。
| 方法 | 参数说明 | 功能 |
|:-----------------:|:-------:|:----------------:|
| `main()` | 无 | 切换到顶层框架 |
| `parent(level)` | 第几层上级框架 | 切换到上级框架,可指定多层 |
| `by_id(id)` | id 属性 | 切换到`id`为该参数的框架 |
| `by_name(name)` | name 属性 | 切换到`name`为该参数的框架 |
| `by_index(index)` | 序号 | 切换到页面第几个框架0 开始 |
| `by_loc(loc)` | 定位符 | 切换到定位符所指框架 |
| `by_ele(ele)` | 框架元素 | 传入框架元素,切换到该框架 |
```python
# 切换到主框架
page.to_frame.main()
# 切换到上 2 级框架
page.to_frame.parent(2)
# 切换到 id 值为 'iframe_id' 的框架
page.to_frame.by_id('iframe_id')
# 切换到 name 值为 'iframe_name' 的框架
page.to_frame.by_name('iframe_name')
# 切换到页面中第一个框架
page.to_frame.by_index(0)
# 使用定位符查找元素,再实现切换
page.to_frame.by_loc('tag:iframe')
# 先获取 iframe 元素,再传入实现切换
iframe = page.ele('tag:iframe')
page.to_frame.by_ele(iframe)
```
`ToFrame`类实现了`__call__()`方法,可自动判断传入类型,使用比较简便。
```python
# 切换到 id 值为 'iframe_id' 的框架
page.to_frame('iframe_id')
# 切换到 name 值为 'iframe_name' 的框架
page.to_frame('iframe_name')
# 切换到页面中第一个框架
page.to_frame(0)
# 使用定位符查找元素,再实现切换
page.to_frame('tag:iframe')
# 先获取 iframe 元素,再传入实现切换
iframe = page.ele('tag:iframe')
page.to_frame(iframe)
```
!> **注意:**<br>新版本删除了`to_frame('main')``to_frame('parent')`的用法。 <br>请改用`page.to_frame.main()``page.to_frame.parent()`的写法。
## 📍 `to_tab()`
此方法把焦点定位到某个标签页。
**参数:**
- `num_or_handle`:标签页序号或 handle 字符串,序号第一个为 0最后为 -1
**返回:**`None`
!> **注意:** <br>以下情况会导致获取到的排序号与视觉效果不一致1、自动化过程中手动点击标签页2、浏览器被接管时已打开一个以上标签页。
```python
# 跳转到第一个标签页
page.to_tab(0)
# 跳转到 handle 为该字符串的标签页
page.to_tab('xxxxxxxxxxxxxxxxxxxx')
```
## 📍 `create_tab()`
此方法用于新建并定位到一个标签页,该标签页在最后面。可指定跳转到一个 url。
**参数:**
- `url`:新标签页跳转到的 url
**返回:**`None`
```python
page.create_tab('http://www.baidu.com')
```
## 📍 `close_tabs()`
此方法用于关闭指定的标签页,标签页可以是序号或 handle 值,可关闭多个。默认关闭当前的。
!> **注意:** <br>以下情况会导致获取到的排序号与视觉效果不一致不能按序号关闭1、自动化过程中手动点击标签页2、浏览器被接管时已打开一个以上标签页。
**参数:**
- `num_or_handles`:要保留的标签页序号或 handle可传入 list 或 tuple为None时关闭当前页
**返回:**`None`
```python
# 关闭当前标签页
page.close_tabs()
# 关闭第1、3个标签页
page.close_tabs((0, 2))
```
## 📍 `close_other_tabs()`
此方法用于关闭指定标签页以外的标签页,标签页可以是序号或 handle 值,可保留多个。默认保留当前的。
!> **注意:** <br>以下情况会导致获取到的排序号与视觉效果不一致不能按序号关闭1、自动化过程中手动点击标签页2、浏览器被接管时已打开一个以上标签页。
**参数:**
- `num_or_handles`:要保留的标签页序号或 handle可传入`list``tuple`,为`None`时关闭当前页
**返回:**`None`
```python
# 关闭除当前标签页外的所有标签页
page.close_other_tabs()
# 关闭除第一个以外的标签页
page.close_other_tabs(0)
# 关闭除 handle 为 aaaaa 和 bbbbb 以外的标签页
reserve_list = ('aaaaa', 'bbbbb')
page.close_other_tabs(reserve_list)
```
## 📍 `scroll`
此属性用于以某种方式滚动页面。
调用此属性返回一个`Scroll`对象,调用该对象方法实现各种方式的滚动。
| 方法 | 参数说明 | 功能 |
|:-------------------:|:------:|:----------------:|
| `to_top()` | 无 | 滚动到顶端,水平位置不变 |
| `to_bottom()` | 无 | 滚动到底端,水平位置不变 |
| `to_half()` | 无 | 滚动到垂直中间位置,水平位置不变 |
| `to_rightmost()` | 无 | 滚动到最右边,垂直位置不变 |
| `to_leftmost()` | 无 | 滚动到最左边,垂直位置不变 |
| `to_location(x, y)` | 滚动条坐标值 | 滚动到指定位置 |
| `up(pixel)` | 滚动的像素 | 向上滚动若干像素,水平位置不变 |
| `down(pixel)` | 滚动的像素 | 向下滚动若干像素,水平位置不变 |
| `right(pixel)` | 滚动的像素 | 向左滚动若干像素,垂直位置不变 |
| `left(pixel)` | 滚动的像素 | 向右滚动若干像素,垂直位置不变 |
```python
# 页面滚动到底部
page.scroll.to_bottom()
# 页面滚动到最右边
page.scroll.to_rightmost()
# 页面向下滚动 200 像素
page.scroll.down(200)
# 滚动到指定位置
page.scroll.to_location(100, 300)
```
## 📍 `scroll_to_see()`
此方法用于滚动页面直到元素可见。
**参数:**
- `loc_or_ele`:元素的定位信息,可以是元素、定位符
**返回:**`None`
```python
# 滚动到某个已获取到的元素
ele = page.ele('tag:div')
page.scroll_to_see(ele)
# 滚动到按定位符查找到的元素
page.scroll_to_see('tag:div')
# 也可用 selenium 定位符
page.scroll_to_see((By.XPATH, '//div'))
```
## 📍 `refresh()`
此方法用于刷新当前页面。
**参数:** 无
**返回:**`None`
## 📍 `stop_loading()`
此方法用于强制当前页面加载。
**参数:** 无
**返回:**`None`
## 📍 `back()`
此方法用于在浏览历史中后退一步。
**参数:** 无
**返回:**`None`
## 📍 `forward()`
此方法用于在浏览历史中前进一步。
**参数:** 无
**返回:**`None`
## 📍 `screenshot()`
此方法用于生成页面可见范围截图。图片为 png 格式。
此方法能够自动处理文件重命名的情况,给新文件后面增加序号。
也可以返回图片的二进制文本。
**参数:**
- `path`:图片保存路径
- `filename`:图片文件名,可不写后缀,不传入时以页面 title 命名
- `as_bytes`是否已字节形式返回图片为True时上面两个参数失效
**返回:** 图片完整路径或字节文本
```python
path = page.screenshot(r'D:\tmp', 'img1') # 保存到路径文件名为img1.png
bytes = page.screenshot(as_bytes=True) # 返回截图二进制文本
```
## 📍 `set_window_size()`
此方法用于设置浏览器窗口大小。
默认最大化,任一参数为 0 最小化。
**参数:**
- `height`:浏览器窗口高度
- `width`:浏览器窗口宽度
**返回:**`None`
```python
# 窗口最大化
page.set_window_size()
# 窗口最小化
page.set_window_size(0)
# 设置窗口为 800*600
page.set_window_size(800, 600)
```
## 📍 `process_alert()`
此方法 用于处理提示框。
它能够设置等待时间,等待提示框出现才进行处理,若超时没等到提示框,返回`None`
它可只获取提示框文本而不处理提示框。
**参数:**
- `ok``True`表示确认,`False`表示取消,其它值不会按按钮但依然返回文本值
- `send`:处理 prompt 提示框时可输入文本
- `timeout`:等待提示框出现的超时时间
**返回:** 提示框内容文本,未等到提示框则返回`None`
```python
# 确认提示框并获取提示框文本
txt = page.process_alert()
# 点击取消
page.process_alert(False)
# 给 prompt 提示框输入文本并点击确定
paeg.process_alert(True, 'some text')
# 不处理提示框,只获取提示框文本
txt = page.process_alert(None)
```
## 📍 `check_page()`
此方法用于检查页面是否符合预期,它默认返回`None`,须由子类实现其功能。
用于 POM 模式时,可派生各种页面子类,从而实现检查页面的功能。

View File

@ -1,17 +0,0 @@
MixPage 支持获取和设置`cookies`,具体使用方法如下:
```python
# 以字典形式返回 cookies只会返回当前域名可用的 cookies
page.cookies
# 以列表形式返回当前域名可用 cookies每个 cookie 包含所有详细信息
page.get_cookies(as_dict=False)
# 以列表形式返回所有 cookies只有 s 模式有效
page.get_cookies(all_domains=True)
# 设置 cookies可传入 RequestsCookieJar, list, tuple, str, dict
page.set_cookies(cookies)
```
?> **Tips:** <br>d 模式设置`cookies`后要刷新页面才能看到效果。 <br>s 模式可在 ini 文件、`SessionOptions`、配置字典中设置`cookies`,在`MixPage`初始化时即可传入d 模式只能用`set_cookies()`函数设置。

View File

@ -1,151 +0,0 @@
`Drission`对象用于管理`WebDriver``Session`对象。在多个页面协同工作时,`Drission`对象用于传递驱动器,使多个页面类可控制同一个浏览器或`Session`对象。
可直接读取 ini 文件配置信息创建,也可以在初始化时传入配置信息。
在“[使用方法->创建页面对象](创建页面对象.md)”章节已经涉及过`Drission`的用法,这里介绍属性和方法。
# ✔️ `Drission`
**初始化参数:**
- `driver_or_options``WebDriver`对象或`DriverOptions``Options`类,传入`False`则创建空配置对象
- `session_or_options``Session`对象或设置字典,传入`False`则创建空配置对象
- `ini_path`ini 文件路径
- `proxy`:代理设置,`dict`类型。格式:{'http': '127.0.0.1:1080', 'https': '127.0.0.1:1080'}
前两个参数可直接接收`WebDriver``Session`对象,这时后面两个参数无效。
若接收配置对象,则按照配置创建`WebDriver``Session`对象。
用 ini 文件信息创建:
```python
# 由默认 ini 文件创建
drission = Drission()
# 由其它 ini 文件创建
drission = Drission(ini_path='D:\\settings.ini')
```
传入配置创建:
```python
from DrissionPage.config import DriverOptions
# 创建 driver 配置对象
do = DriverOptions()
# 传入配置driver_options 和 session_options 都是可选的,须要使用对应模式才须要传入
drission = Drission(driver_options=do)
```
## 📍 `session`
此属性返回该对象管理的`Session`对象。
## 📍 `driver`
此属性返回该对象管理的`WebDriver`对象。
## 📍 `driver_options`
此属性返回用于创建`WebDriver`对象的`DriverOptions`对象。
## 📍 `session_options`
此属性以`dict`形式返回用于创建 Session 对象的配置参数。可传入`dict``SessionOptions`赋值。
## 📍 `proxy`
此属性返回代理信息,`dict`形式。可传入`dict`赋值。格式:{'http': '127.0.0.1:1080', 'https': '127.0.0.1:1080'}
## 📍 `debugger_progress`
此属性返回浏览器进程(如有)。
## 📍 `kill_browser()`
此方法用于关闭浏览器进程。
**参数:** 无
**返回:**`None`
## 📍 `get_browser_progress_id()`
此方法用于获取浏览器进程 id。
**参数:** 无
**返回:**`None`
## 📍 `hide_browser()`
此方法用于隐藏浏览器进程窗口。
**参数:** 无
**返回:**`None`
## 📍 `show_browser()`
此方法用于显示浏览器进程窗口。
**参数:** 无
**返回:**`None`
## 📍 `set_cookies()`
此方法用于设置`cookies`。可选择对某个对象设置。
**参数:**
- `cookies``cookies`信息,可为`CookieJar`,`list`,`tuple`,`str`,`dict`
- `set_session`:是否设置`Session`对象的`cookies`
- `set_driver`:是否设置浏览器的`cookies`
**返回:**`None`
## 📍 `cookies_to_session()`
此方法用于把`WebDriver`对象的`cookies`复制到`Session`对象。
**参数:**
- `copy_user_agent`:是否复制 user agent 信息
**返回:**`None`
## 📍 `cookies_to_driver()`
此方法用于把`Session`对象的`cookies`复制到`WebDriver`对象。
复制`cookies`到浏览器必须指定域名。
**参数:**
- `url`:作用域
**返回:**`None`
## 📍 `close_driver()`
此方法用于关闭`WebDriver`对象,可选择是否关闭浏览器进程。
**参数:**
- `kill`:是否关闭浏览器进程
**返回:**`None`
## 📍 `close_session()`
此方法用于关闭`Session`对象。
**参数:** 无
**返回:**`None`
## 📍 `close()`
此方法用于关闭`Session``WebDriver`对象。
**参数:** 无
**返回:**`None`

View File

@ -1,48 +0,0 @@
星巴克的示例使用`download()`方法实现下载图片,这个示例演示在浏览器中直接保存图片。
这个功能是本库一个特色,无需操作 ui也无需重新下载图片而是直接读取缓存里面的图片保存使用非常方便。
# ✔️ 页面分析
目标网址:[https://book.douban.com/tag/小说](https://book.douban.com/tag/%E5%B0%8F%E8%AF%B4)
`F12`,可以看到每本图书都在`class`属性为`subject-item`的元素中,可批量获取,然后再在其中获取`<img>`元素保存图片。
---
# ✔️ 编码思路
为了演示元素对象的`save()`方法,这里我们使用浏览器操作,把图片文件保存到本地 imgs 文件夹。
---
# ✔️ 示例代码
以下代码可直接运行。
```python
from DrissionPage import ChromiumPage
# 创建页面对象
page = ChromiumPage()
# 访问目标网页
page.get('https://book.douban.com/tag/小说?start=0&type=T')
# 爬取4页
for _ in range(4):
# 遍历一页中所有图书
for book in page.eles('.subject-item'):
# 获取封面图片对象
img = book('t:img')
# 保存图片
img.save(r'.\imgs')
# 点击下一页
page('后页>').click(wait_loading=True)
```
---
# ✔️ 结果
![](https://gitee.com/g1879/DrissionPage/raw/master/docs/imgs/20230105105418.png)

View File

@ -1,49 +0,0 @@
此示例演示使用控制浏览器的方式自动登录 gitee 网站。
# ✔️ 网页分析
网址https://gitee.com/login
![](https://gitee.com/g1879/DrissionPage-demos/raw/master/pics/QQ%E6%88%AA%E5%9B%BE20220118000233.jpg)
`F12`查看代码,可见两个输入框都可用`id`属性定位,如图所示。
![](https://gitee.com/g1879/DrissionPage-demos/raw/master/pics/QQ截图20220118000346.jpg)
---
# ✔️ 编码思路
`id`属性的元素获取非常简单。两个输入框直接用`id`属性定位即可。
登录按钮没有`id`属性,但可观察到它是第一个`value`属性为`'登 录'`的元素,用中文定位也可以增强代码可读性。
由于使用浏览器登录,这里用`ChromiumPage`来控制浏览器。
---
# ✔️ 示例代码
```python
from DrissionPage import ChromiumPage
# 用 d 模式创建页面对象(默认模式)
page = ChromiumPage()
# 跳转到登录页面
page.get('https://gitee.com/login')
# 定位到账号文本框并输入账号
page.ele('#user_login').input('您的账号')
# 定位到密码文本框并输入密码
page.ele('#user_password').input('您的密码')
# 点击登录按钮
page.ele('@value=登 录').click()
```
---
# ✔️ 结果
登录成功。
![](https://gitee.com/g1879/DrissionPage-demos/raw/master/pics/QQ%E6%88%AA%E5%9B%BE20220118000945.jpg)

View File

@ -1,70 +0,0 @@
这个示例演示用浏览器采集数据。
# ✔️ 采集目标
目标网址:[https://www.maoyan.com/board/4](https://www.maoyan.com/board/4)
采集目标:排名、电影名称、演员、上映时间、分数
---
# ✔️ 编码思路
`F12`,可以看到每个电影信息都包含在`<dd>`元素中,因此可批量获取`<dd>`元素,遍历它们并再从其中获取每个电影的信息。
---
# ✔️ 示例代码
以下代码可直接运行。
须要注意的是,这里用到记录器对象,详见[DataRecorder](http://g1879.gitee.io/datarecorder)。
```python
from DrissionPage import ChromiumPage
from DataRecorder import Recorder
# 创建页面对象
page = ChromiumPage()
# 创建记录器对象
recorder = Recorder('data.csv')
# 访问网页
page.get('https://www.maoyan.com/board/4')
while True:
# 遍历页面上所有 dd 元素
for mov in page.eles('t:dd'):
# 获取须要的信息
num = mov('t:i').text
score = mov('.score').text
title = mov('@data-act=boarditem-click').attr('title')
star = mov('.star').text
time = mov('.releasetime').text
# 写入到记录器
recorder.add_data((num, title, star, time, score))
# 获取下一页按钮,有就点击
if btn := page('下一页', timeout=2):
btn.click(wait_loading=True)
# 没有则退出程序
else:
break
recorder.record()
```
---
# ✔️ 结果
程序生成一个结果文件 data.csv内容如下
```csv
1,我不是药神,"主演:徐峥,周一围,王传君",上映时间2018-07-05,9.6
2,肖申克的救赎,"主演:蒂姆·罗宾斯,摩根·弗里曼,鲍勃·冈顿",上映时间1994-09-10(加拿大),9.5
3,海上钢琴师,"主演:蒂姆·罗斯,比尔·努恩 ,克兰伦斯·威廉姆斯三世",上映时间2019-11-15,9.3
4,绿皮书,"主演:维果·莫腾森,马赫沙拉·阿里,琳达·卡德里尼",上映时间2019-03-01,9.5
5,霸王别姬,"主演:张国荣,张丰毅,巩俐",上映时间1993-07-26,9.4
下面省略。。。
```

View File

@ -1,101 +0,0 @@
此示例演示如何使用多个线程同时控制一个浏览器的多个标签页进行采集。
# ✔️ 页面分析
目标网址:
- https://gitee.com/explore/ai
- https://gitee.com/explore/machine-learning
`F12`,可以看到每个标题元素的`class`属性均为`title project-namespace-path`,可批量获取。
---
# ✔️ 编码思路
虽然 gitee 开源项目列表可以用 s 模式采集,但现在为了演示多标签页操作,还是使用浏览器进行操作。
使用`ChromiumPage``get_loc()`方法,分别获取两个标签页的对象,传入不同线程进行操作。
---
# ✔️ 示例代码
以下代码可直接运行。
须要注意的是,这里用到记录器对象,详见[DataRecorder](http://g1879.gitee.io/datarecorder)。
```python
from threading import Thread
from DrissionPage import ChromiumPage
from DataRecorder import Recorder
def collect(tab, recorder, title):
"""用于采集的方法
:param tab: ChromiumTab 对象
:param recorder: Recorder 记录器对象
:param title: 类别标题
:return: None
"""
num = 1 # 当前采集页数
while True:
# 遍历所有标题元素
for i in tab.eles('.title project-namespace-path'):
# 获取某页所有库名称,记录到记录器
recorder.add_data((title, i.text, num))
# 如果有下一页,点击翻页
if btn := tab('@rel=next', timeout=2):
btn.click(wait_loading=True)
num += 1
# 否则,采集完毕
else:
break
def main():
# 新建页面对象
page = ChromiumPage()
# 第一个标签页访问网址
page.get('https://gitee.com/explore/ai')
# 获取第一个标签页对象
tab1 = page.get_tab()
# 新建一个标签页并访问另一个网址
page.new_tab('https://gitee.com/explore/machine-learning')
# 获取第二个标签页对象
tab2 = page.get_tab()
# 新建记录器对象
recorder = Recorder('data.csv')
# 多线程同时处理多个页面
Thread(target=collect, args=(tab1, recorder, 'ai')).start()
Thread(target=collect, args=(tab2, recorder, '机器学习')).start()
if __name__ == '__main__':
main()
```
---
# ✔️ 结果
程序生成一个结果文件 data.csv内容如下
```csv
机器学习,MindSpore/mindspore,1
机器学习,PaddlePaddle/Paddle,1
机器学习,MindSpore/docs,1
机器学习,scruel/Notes-ML-AndrewNg,1
机器学习,MindSpore/graphengine,1
机器学习,inspur-inna/inna1.0,1
ai,drinkjava2/人工生命,1
机器学习,MindSpore/course,1
后面省略。。。
```

View File

@ -1,75 +0,0 @@
这个示例用于演示`download()`方法的功能。
# ✔️ 页面分析
目标网址https://www.starbucks.com.cn/menu/
采集目标:下载所有产品图片,并以产品名称命名。
如图所示:
![](https://gitee.com/g1879/DrissionPage-demos/raw/master/pics/QQ%E6%88%AA%E5%9B%BE20220117233111.jpg)
按 F12 查看页面代码,可以发现每个产品图片都是一个`div`元素,`class`属性都为`preview circle`。且图片网址藏在其`style`属性中。而产品名称则在这个`div`元素后面一个元素中。
如图所示:
![](https://gitee.com/g1879/DrissionPage-demos/raw/master/pics/QQ%E6%88%AA%E5%9B%BE20220117233229.jpg)
---
# ✔️ 编码思路
按照页面规律,我们可以获取所有`class`属性为`preview circle`的元素,然后遍历它们,逐个获取图片路径,以及在后面一个元素中获取产品名称。再将其下载。
并且这个网址页面结构非常简单,没有使用 js 生成的页面,可以直接使用 s 模式访问。
---
# ✔️ 示例代码
```python
from DrissionPage import SessionPage
from re import search
# 以s模式创建页面对象
page = SessionPage()
# 网站证书好像过期了,要设置这个才能正常访问
page.session.verify = False
# 访问目标网页
page.get('https://www.starbucks.com.cn/menu/')
# 获取所有class属性为preview circle的元素
divs = page.eles('.preview circle')
# 遍历这些元素
for div in divs:
# 用相对定位获取当前div元素后一个兄弟元素并获取其文本
name = div.next().text
# 在div元素的style属性中提取图片网址并进行拼接
img_url = div.attr('style')
img_url = search(r'"(.*)"', img_url).group(1)
img_url = f'https://www.starbucks.com.cn{img_url}'
# 执行下载
page.download(img_url, r'.\imgs', rename=name)
```
?> **Tips** <br>程序中无须创建`imgs`文件夹,`download()`方法会自动创建。
---
# ✔️ 结果
![](https://gitee.com/g1879/DrissionPage-demos/raw/master/pics/QQ%E6%88%AA%E5%9B%BE20220117234300.jpg)
程序执行的时候,默认会把下载进度打印出来。
![](https://gitee.com/g1879/DrissionPage-demos/raw/master/pics/QQ%E6%88%AA%E5%9B%BE20220117234232.jpg)
我们注意到有些产品名称是带有`/`的,这是系统路径的非法字符。一般下载方法须程序员自行处理。
但 DrissionPage 的`download()`方法自带去除非法字符功能,保证文件能成功保存。
除此以外,遇到保存路径有重名文件时,该方法也能自动对新文件进行重命名(添加序号),避免路径冲突。
该方法会返回已下载文件的绝对路径,方便程序进一步使用。
?> **Tips** <br>遇到重名时,`download()`方法可选择`skip``overwrite``rename`三种方式处理。

View File

@ -1,114 +0,0 @@
# ✨️ 概述
DrissionPage 是一个基于 python 的网页自动化工具。
它既能控制浏览器,也能收发数据包,甚至能把两者合而为一,
可兼顾浏览器自动化的便利性和 requests 的高效率。
它功能强大,内置无数人性化设计和便捷功能。
它的语法简洁而优雅,代码量少,对新手友好。
***
支持系统Windows、Linux、Mac
python 版本3.6 及以上
支持浏览器Chromium 内核(如 Chrome 和 Edge
***
<a href='https://gitee.com/g1879/DrissionPage/stargazers'><img src='https://gitee.com/g1879/DrissionPage/badge/star.svg?theme=dark' alt='star'></img></a> <a href='https://gitee.com/g1879/DrissionPage/members'><img src='https://gitee.com/g1879/DrissionPage/badge/fork.svg?theme=dark' alt='fork'></img></a>
项目地址:[gitee](https://gitee.com/g1879/DrissionPage) | [github](https://github.com/g1879/DrissionPage)
**交流QQ群** 897838127
**联系邮箱:** g1879@qq.com
---
# 📕 背景
用 requests 做数据采集面对要登录的网站时要分析数据包、JS 源码构造复杂的请求往往还要应付验证码、JS 混淆、签名参数等反爬手段,门槛较高。若数据是由 JS 计算生成的,还须重现计算过程,体验不好,开发效率不高。
使用浏览器,可以很大程度上绕过这些坑,但浏览器运行效率不高。
因此,这个库设计初衷,是将它们合而为一,能够在不同须要时切换相应模式,并提供一种人性化的使用方法,提高开发和运行效率。
除了合并两者,本库还以网页为单位封装了常用功能,提供非常简便的操作和语句,在用于网页自动化操作时,减少考虑细节,专注功能实现,使用更方便。 一切从简,尽量提供简单直接的使用方法,使代码更优雅。
以前的版本是对 selenium 进行重新封装实现的。从 3.0 开始,作者另起炉灶,对底层进行了重新开发,摆脱对 selenium 的依赖,增强了功能,提升了运行效率。
---
# 💡 理念
简洁!易用 !方便!
---
# ☀️ 特性
作者经过长期实践,踩过无数坑,总结出的经验全写到这个库里了。
## 🎇 强大的自研内核
本库采用全自研的内核,内置了 N 多实用功能,对常用功能作了整合和优化,对比 selenium有以下优点
- 无 webdriver 特征,不会被网站识别
- 无需为不同版本的浏览器下载不同的驱动
- 运行速度更快
- 可以跨 iframe 查找元素,无需切入切出
- 把 iframe 看作普通元素,获取后可直接在其中查找元素,逻辑更清晰
- 可以同时操作浏览器中的多个标签页,即使标签页为非激活状态,无需切换
- 可以直接读取浏览器缓存来保存图片,无需用 GUI 点击另存
- 可以对整个网页截图包括视口外的部分90以上版本浏览器支持
- 可处理非`open`状态的 shadow-root
## 🎇 亮点功能
除了以上优点,本库还内置了无数人性化设计。
- 极简的语法规则。集成大量常用功能,代码更优雅
- 定位元素更加容易,功能更强大稳定
- 无处不在的等待和自动重试功能。使不稳定的网络变得易于控制,程序更稳定,编写更省心
- 提供强大的下载工具。操作浏览器时也能享受快捷可靠的下载功能
- 允许反复使用已经打开的浏览器。无须每次运行从头启动浏览器,调试超方便
- 使用 ini 文件保存常用配置,自动调用,提供便捷的设置,远离繁杂的配置项
- 内置 lxml 作为解析引擎,解析速度成几个数量级提升
- 使用 POM 模式封装,可直接用于测试,便于扩展
- 高度集成的便利功能,从每个细节中体现
- 还有很多细节这里不一一列举欢迎实际使用中体验D
---
# 🖐🏻 免责声明
请勿将 DrissionPage 应用到任何可能会违反法律规定和道德约束的工作中,请友善使用 DrissionPage遵守蜘蛛协议不要将 DrissionPage 用于任何非法用途。如您选择使用 DrissionPage
即代表您遵守此协议,作者不承担任何由于您违反此协议带来任何的法律风险和损失,一切后果由您承担。
---
# ☕ 请我喝咖啡
如果本项目对您有所帮助,不妨请作者我喝杯咖啡
![](imgs/code.jpg)

View File

@ -1,14 +0,0 @@
# DrissionPage
DrissionPage 是一个 Web 自动化框架。
以页面为单位整合浏览器和 requests封装了常用操作。
极大地简化了代码,易于使用,并可实现两种模式的无缝切换。
可兼顾浏览器的易用性和 requests 的高性能。
如果本库对你有所帮助,请给予支持!持续维护中。
![Gitee-Stars](https://gitee.com/g1879/DrissionPage/badge/star.svg?theme=dark)
![Forks](https://gitee.com/g1879/DrissionPage/badge/fork.svg?theme=dark)
[Gitee](https://gitee.com/g1879/DrissionPage)
[开始阅读](README.md)

View File

@ -1,2 +0,0 @@
* [DataRecorder](https://gitee.com/g1879/DataRecorder)
* [FlowViewer](http://g1879.gitee.io/flowviewer)

View File

@ -1,90 +0,0 @@
* [📣 1 概述](README.md)
* [☀️ 2 特性和亮点](#)
* [💖 2.1 贴心设计](2_features/1_intimate_design.md)
* [🌟 2.2 特性演示](#)
* [⭐ 与 requests 对比](2_features/2_features_demos/1_compare_with_requests.md)
* [⭐ 与 selenium 对比](2_features/2_features_demos/2_compare_with_selenium.md)
* [⭐ 模式切换](2_features/2_features_demos/3_switch_mode.md)
* [⭐ 获取元素属性](2_features/2_features_demos/4_get_element_attributes.md)
* [⭐ 下载文件](2_features/2_features_demos/5_download_file.md)
* [🧭 3 入门指南](#)
* [🌏 3.1 安装和导入](3_get_start/1_installation_and_import.md)
* [🌏 3.2 准备工作](3_get_start/2_before_start.md)
* [🌏 3.3 上手示例](#)
* [🗺️ 操控浏览器](3_get_start/3_examples/1_control_browser.md)
* [🗺️ 收发数据包](3_get_start/3_examples/2_data_packets.md)
* [🗺️ 模式切换](3_get_start/3_examples/3_switch_mode.md)
* [🌏 3.4 基本概念](3_get_start/4_basic_concept.md)
* [📫 4 SessionPage](#)
* [✉️ 4.0 概述](4_SessionPge/0_introduction.md)
* [✉️ 4.1 创建页面对象](4_SessionPge/1_create_page_object.md)
* [✉️ 4.2 访问网页](4_SessionPge/2_visit_web_page.md)
* [✉️ 4.3 获取网页信息](4_SessionPge/3_get_page_info.md)
* [✉️ 4.4 查找元素](4_SessionPge/4_find_elements.md)
* [✉️ 4.5 获取元素信息](4_SessionPge/5_get_element_info.md)
* [✉️ 4.6 启动配置](4_SessionPge/6_session_options.md)
* [🚀 5 ChromiumPage](#)
* [🛰️ 5.0 概述](5_ChromiumPage/0_introduction.md)
* [🛰️ 5.1 创建页面对象](5_ChromiumPage/1_create_page_object.md)
* [🛰️ 5.2 访问网页](5_ChromiumPage/2_visit_web_page.md)
* [🛰️ 5.3 获取网页信息](5_ChromiumPage/3_get_page_info.md)
* [🛰️ 5.4 页面交互](5_ChromiumPage/4_page_operation.md)
* [🛰️ 5.5 查找元素](5_ChromiumPage/5_find_elements.md)
* [🛰️ 5.6 获取元素信息](5_ChromiumPage/6_get_element_info.md)
* [🛰️ 5.7 元素交互](5_ChromiumPage/7_element_operation.md)
* [🛰️ 5.8 标签页操作](5_ChromiumPage/8_tab_operation.md)
* [🛰️ 5.9 iframe 操作](5_ChromiumPage/9_frame_operation.md)
* [🛰️ 5.10 动作链](5_ChromiumPage/10_action_chains.md)
* [🛰️ 5.11 浏览器启动配置](5_ChromiumPage/11_browser_options.md)
* [🌌 6 WebPage](#)
* [🪐 6.0 概述](6_WebPage/0_introduction.md)
* [🪐 6.1 创建页面对象](6_WebPage/1_create_page_object.md)
* [🪐 6.2 模式切换](6_WebPage/2_mode_switch.md)
* [🪐 6.3 独有的功能](6_WebPage/3_webpage_function.md)
* [🧰 7 进阶使用](#)
* [⚙️ 7.1 配置文件的使用](7_advance/1_ini_file.md)
* [⚙️ 7.2 easy_set 方法](7_advance/2_easy_set.md)
* [⚙️ 7.3 下载文件](7_advance/3_download.md)
* [⚙️ 7.4 加速浏览器数据采集](7_advance/4_accelerate_collecting.md)
* [⚙️ 7.5 打包程序](7_advance/5_packaging.md)
* [⚙️ 7.6 监听浏览器网络](7_advance/6_monitor_network.md)
* [🛠 8 旧版使用方法](#)
* [🔨 8.0 概述](8_MixPage/0_introduction.md)
* [🔨 8.1 创建页面对象](8_MixPage/1_create_page_object.md)
* [🔨 8.2 访问网页](8_MixPage/2_visit_web_page.md)
* [🔨 8.3 查找页面元素](8_MixPage/3_find_page_element.md)
* [🔨 8.4 获取元素信息](8_MixPage/4_get_element_info.md)
* [🔨 8.5 元素操作](8_MixPage/5_element_operation.md)
* [🔨 8.6 获取网页信息](8_MixPage/6_get_page_info.md)
* [🔨 8.7 页面操作](8_MixPage/7_page_operation.md)
* [🔨 8.8 cookies 的使用](8_MixPage/8_cookies.md)
* [🔨 8.9 Drission 对象](8_MixPage/9_Drission.md)
* [🔨 8.10 对接 selenium 及 requests 代码](8_MixPage/10_work_with_selenium_and_requests.md)
* [🔨 8.11 使用其它系统或浏览器](8_MixPage/11_use_other_browser.md)
* [🔨 8.12 DriverPage 和 SessionPage](8_MixPage/12_DriverPage_and_SessionPage.md)
* [⚡️ 9 示例和技巧](#)
* [🌠 自动登录码云](9_demos/login_gitee.md)
* [🌠 采集猫眼电影 TOP100 榜](9_demos/maoyan_TOP100.md)
* [🌠 下载星巴克产品图片](9_demos/starbucks_pics.md)
* [🌠 下载豆瓣图书封面图片](9_demos/douban_book_pics.md)
* [🌠 多线程操作多标签页](9_demos/multithreading_with_tabs.md)
* [🔖 10 版本历史](10_history.md)
* [💐 鸣谢](thx.md)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 409 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 166 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 176 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 130 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 168 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

View File

@ -1,61 +0,0 @@
//default values
var defaultOptions = {
countable: true,
position: "top",
margin: "10px",
float: "right",
fontsize: "0.9em",
color: "rgb(90,90,90)",
language: "english",
isExpected: true,
}
// Docsify plugin functions
function plugin(hook, vm) {
if (!defaultOptions.countable) {
return
}
let wordsCount
hook.beforeEach(function (content) {
// Match regex every time you start parsing .md
wordsCount = content.match(/([\u4e00-\u9fa5]+?|[a-zA-Z0-9]+)/g).length
return content
})
hook.afterEach(function (html, next) {
let str = wordsCount + " words"
let readTime = Math.ceil(wordsCount / 400) + " min"
//Determine whether to use the Chinese style according to the attribute "language"
if (defaultOptions.language === "chinese") {
str = wordsCount + " 字"
readTime = Math.ceil(wordsCount / 400) + " 分钟"
}
//add html string
next(
`
${defaultOptions.position === "bottom" ? html : ""}
<div style="margin-${defaultOptions.position ? "bottom" : "top"}: ${
defaultOptions.margin
};">
<span style="
float: ${defaultOptions.float === "right" ? "right" : "left"};
font-size: ${defaultOptions.fontsize};
color:${defaultOptions.color};">
${str}
${defaultOptions.isExpected ? `&nbsp; | &nbsp;${readTime}` : ""}
</span>
<div style="clear: both"></div>
</div>
${defaultOptions.position !== "bottom" ? html : ""}
`
)
})
}
// Docsify plugin options
window.$docsify["count"] = Object.assign(
defaultOptions,
window.$docsify["count"]
)
window.$docsify.plugins = [].concat(plugin, window.$docsify.plugins)

View File

@ -1,10 +0,0 @@
/*!
* docsify-copy-code
* v2.1.1
* https://github.com/jperasmus/docsify-copy-code
* (c) 2017-2020 JP Erasmus <jperasmus11@gmail.com>
* MIT license
*/
!function(){"use strict";function s(o){return(s="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(o){return typeof o}:function(o){return o&&"function"==typeof Symbol&&o.constructor===Symbol&&o!==Symbol.prototype?"symbol":typeof o})(o)}!function(o,e){void 0===e&&(e={});var t=e.insertAt;if(o&&"undefined"!=typeof document){var n=document.head||document.getElementsByTagName("head")[0],c=document.createElement("style");c.type="text/css","top"===t&&n.firstChild?n.insertBefore(c,n.firstChild):n.appendChild(c),c.styleSheet?c.styleSheet.cssText=o:c.appendChild(document.createTextNode(o))}}(".docsify-copy-code-button,.docsify-copy-code-button span{cursor:pointer;transition:all .25s ease}.docsify-copy-code-button{position:absolute;z-index:1;top:0;right:0;overflow:visible;padding:.65em .8em;border:0;border-radius:0;outline:0;font-size:1em;background:grey;background:var(--theme-color,grey);color:#fff;opacity:0}.docsify-copy-code-button span{border-radius:3px;background:inherit;pointer-events:none}.docsify-copy-code-button .error,.docsify-copy-code-button .success{position:absolute;z-index:-100;top:50%;right:0;padding:.5em .65em;font-size:.825em;opacity:0;-webkit-transform:translateY(-50%);transform:translateY(-50%)}.docsify-copy-code-button.error .error,.docsify-copy-code-button.success .success{right:100%;opacity:1;-webkit-transform:translate(-115%,-50%);transform:translate(-115%,-50%)}.docsify-copy-code-button:focus,pre:hover .docsify-copy-code-button{opacity:1}"),document.querySelector('link[href*="docsify-copy-code"]')&&console.warn("[Deprecation] Link to external docsify-copy-code stylesheet is no longer necessary."),window.DocsifyCopyCodePlugin={init:function(){return function(o,e){o.ready(function(){console.warn("[Deprecation] Manually initializing docsify-copy-code using window.DocsifyCopyCodePlugin.init() is no longer necessary.")})}}},window.$docsify=window.$docsify||{},window.$docsify.plugins=[function(o,r){o.doneEach(function(){var o=Array.apply(null,document.querySelectorAll("pre[data-lang]")),c={buttonText:"Copy to clipboard",errorText:"Error",successText:"Copied"};r.config.copyCode&&Object.keys(c).forEach(function(t){var n=r.config.copyCode[t];"string"==typeof n?c[t]=n:"object"===s(n)&&Object.keys(n).some(function(o){var e=-1<location.href.indexOf(o);return c[t]=e?n[o]:c[t],e})});var e=['<button class="docsify-copy-code-button">','<span class="label">'.concat(c.buttonText,"</span>"),'<span class="error">'.concat(c.errorText,"</span>"),'<span class="success">'.concat(c.successText,"</span>"),"</button>"].join("");o.forEach(function(o){o.insertAdjacentHTML("beforeend",e)})}),o.mounted(function(){document.querySelector(".content").addEventListener("click",function(o){if(o.target.classList.contains("docsify-copy-code-button")){var e="BUTTON"===o.target.tagName?o.target:o.target.parentNode,t=document.createRange(),n=e.parentNode.querySelector("code"),c=window.getSelection();t.selectNode(n),c.removeAllRanges(),c.addRange(t);try{document.execCommand("copy")&&(e.classList.add("success"),setTimeout(function(){e.classList.remove("success")},1e3))}catch(o){console.error("docsify-copy-code: ".concat(o)),e.classList.add("error"),setTimeout(function(){e.classList.remove("error")},1e3)}"function"==typeof(c=window.getSelection()).removeRange?c.removeRange(t):"function"==typeof c.removeAllRanges&&c.removeAllRanges()}})})}].concat(window.$docsify.plugins||[])}();
//# sourceMappingURL=docsify-copy-code.min.js.map

File diff suppressed because one or more lines are too long

View File

@ -1,2 +0,0 @@
!function(e){("object"!=typeof exports||"undefined"==typeof module)&&"function"==typeof define&&define.amd?define(e):e()}(function(){"use strict";function e(e,n){var t,a=(n=void 0===n?{}:n).insertAt;e&&"undefined"!=typeof document&&(t=document.head||document.getElementsByTagName("head")[0],(n=document.createElement("style")).type="text/css","top"===a&&t.firstChild?t.insertBefore(n,t.firstChild):t.appendChild(n),n.styleSheet?n.styleSheet.cssText=e:n.appendChild(document.createTextNode(e)))}var t;function a(e){e&&null!=t&&(e=e.getBoundingClientRect().top,document.querySelector(".sidebar").scrollBy(0,e-t))}function n(){requestAnimationFrame(function(){var e=document.querySelector(".app-sub-sidebar > .active");if(e)for(e.parentNode.parentNode.querySelectorAll(".app-sub-sidebar").forEach(function(e){return e.classList.remove("open")});e.parentNode.classList.contains("app-sub-sidebar")&&!e.parentNode.classList.contains("open");)e.parentNode.classList.add("open"),e=e.parentNode})}function o(e){t=e.target.getBoundingClientRect().top;var n=d(e.target,"LI",2);n&&(n.classList.contains("open")?(n.classList.remove("open"),setTimeout(function(){n.classList.add("collapse")},0)):(function(e){if(e)for(e.classList.remove("open","active");e&&"sidebar-nav"!==e.className&&e.parentNode;)"LI"!==e.parentNode.tagName&&"app-sub-sidebar"!==e.parentNode.className||e.parentNode.classList.remove("open"),e=e.parentNode}(s()),i(n),setTimeout(function(){n.classList.remove("collapse")},0)),a(n))}function s(){var e=document.querySelector(".sidebar-nav .active");return e||(e=d(document.querySelector('.sidebar-nav a[href="'.concat(decodeURIComponent(location.hash).replace(/ /gi,"%20"),'"]')),"LI",2))&&e.classList.add("active"),e}function i(e){if(e)for(e.classList.add("open","active");e&&"sidebar-nav"!==e.className&&e.parentNode;)"LI"!==e.parentNode.tagName&&"app-sub-sidebar"!==e.parentNode.className||e.parentNode.classList.add("open"),e=e.parentNode}function d(e,n,t){if(e&&e.tagName===n)return e;for(var a=0;e;){if(t<++a)return;if(e.parentNode.tagName===n)return e.parentNode;e=e.parentNode}}e(".sidebar-nav > ul > li ul {\n display: none;\n}\n\n.app-sub-sidebar {\n display: none;\n}\n\n.app-sub-sidebar.open {\n display: block;\n}\n\n.sidebar-nav .open > ul:not(.app-sub-sidebar),\n.sidebar-nav .active:not(.collapse) > ul {\n display: block;\n}\n\n/* 抖动 */\n.sidebar-nav li.open:not(.collapse) > ul {\n display: block;\n}\n\n.active + ul.app-sub-sidebar {\n display: block;\n}\n"),document.addEventListener("scroll",n);e("@media screen and (max-width: 768px) {\n /* 移动端适配 */\n .markdown-section {\n max-width: none;\n padding: 16px;\n }\n /* 改变原来按钮热区大小 */\n .sidebar-toggle {\n padding: 0 0 10px 10px;\n }\n /* my pin */\n .sidebar-pin {\n appearance: none;\n outline: none;\n position: fixed;\n bottom: 0;\n border: none;\n width: 40px;\n height: 40px;\n background: transparent;\n }\n}\n");var r,c="DOCSIFY_SIDEBAR_PIN_FLAG";function l(){var e="true"===(e=localStorage.getItem(c));localStorage.setItem(c,!e),e?(document.querySelector(".sidebar").style.transform="translateX(0)",document.querySelector(".content").style.transform="translateX(0)"):(document.querySelector(".sidebar").style.transform="translateX(300px)",document.querySelector(".content").style.transform="translateX(300px)")}768<document.documentElement.clientWidth||(localStorage.setItem(c,!1),(r=document.createElement("button")).classList.add("sidebar-pin"),r.onclick=l,document.body.append(r),window.addEventListener("load",function(){var n=document.querySelector(".content");document.body.onclick=n.onclick=function(e){e.target!==document.body&&e.currentTarget!==n||"true"===localStorage.getItem(c)&&l()}})),function(){if(window.$docsify){for(var e=arguments.length,n=new Array(e),t=0;t<e;t++)n[t]=arguments[t];$docsify.plugins=n.concat($docsify.plugins||[])}else console.error("这是一个docsify插件请先引用docsify库")}(function(e,n){e.doneEach(function(e,n){var t=s();i(t),document.querySelectorAll(".sidebar-nav li").forEach(function(e){e.querySelector("ul:not(.app-sub-sidebar)")?e.classList.add("folder"):e.classList.add("file")}),function n(e,t){e&&e.childNodes&&e.childNodes.forEach(function(e){e.classList&&e.classList.contains("folder")&&(e.classList.add("level-".concat(t)),window.$docsify&&window.$docsify.sidebarDisplayLevel&&"number"==typeof window.$docsify.sidebarDisplayLevel&&t<=window.$docsify.sidebarDisplayLevel&&e.classList.add("open"),e&&1<e.childNodes.length&&n(e.childNodes[1],t+1))})}(document.querySelector(".sidebar-nav > ul"),1),a(t),n(e)}),e.ready(function(){document.querySelector(".sidebar-nav").addEventListener("click",o)})})});

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,2 +0,0 @@
Prism.languages.python={comment:{pattern:/(^|[^\\])#.*/,lookbehind:!0,greedy:!0},"string-interpolation":{pattern:/(?:f|fr|rf)(?:("""|''')[\s\S]*?\1|("|')(?:\\.|(?!\2)[^\\\r\n])*\2)/i,greedy:!0,inside:{interpolation:{pattern:/((?:^|[^{])(?:\{\{)*)\{(?!\{)(?:[^{}]|\{(?!\{)(?:[^{}]|\{(?!\{)(?:[^{}])+\})+\})+\}/,lookbehind:!0,inside:{"format-spec":{pattern:/(:)[^:(){}]+(?=\}$)/,lookbehind:!0},"conversion-option":{pattern:/![sra](?=[:}]$)/,alias:"punctuation"},rest:null}},string:/[\s\S]+/}},"triple-quoted-string":{pattern:/(?:[rub]|br|rb)?("""|''')[\s\S]*?\1/i,greedy:!0,alias:"string"},string:{pattern:/(?:[rub]|br|rb)?("|')(?:\\.|(?!\1)[^\\\r\n])*\1/i,greedy:!0},function:{pattern:/((?:^|\s)def[ \t]+)[a-zA-Z_]\w*(?=\s*\()/g,lookbehind:!0},"class-name":{pattern:/(\bclass\s+)\w+/i,lookbehind:!0},decorator:{pattern:/(^[\t ]*)@\w+(?:\.\w+)*/m,lookbehind:!0,alias:["annotation","punctuation"],inside:{punctuation:/\./}},keyword:/\b(?:_(?=\s*:)|and|as|assert|async|await|break|case|class|continue|def|del|elif|else|except|exec|finally|for|from|global|if|import|in|is|lambda|match|nonlocal|not|or|pass|print|raise|return|try|while|with|yield)\b/,builtin:/\b(?:__import__|abs|all|any|apply|ascii|basestring|bin|bool|buffer|bytearray|bytes|callable|chr|classmethod|cmp|coerce|compile|complex|delattr|dict|dir|divmod|enumerate|eval|execfile|file|filter|float|format|frozenset|getattr|globals|hasattr|hash|help|hex|id|input|int|intern|isinstance|issubclass|iter|len|list|locals|long|map|max|memoryview|min|next|object|oct|open|ord|pow|property|range|raw_input|reduce|reload|repr|reversed|round|set|setattr|slice|sorted|staticmethod|str|sum|super|tuple|type|unichr|unicode|vars|xrange|zip)\b/,boolean:/\b(?:False|None|True)\b/,number:/\b0(?:b(?:_?[01])+|o(?:_?[0-7])+|x(?:_?[a-f0-9])+)\b|(?:\b\d+(?:_\d+)*(?:\.(?:\d+(?:_\d+)*)?)?|\B\.\d+(?:_\d+)*)(?:e[+-]?\d+(?:_\d+)*)?j?(?!\w)/i,operator:/[-+%=]=?|!=|:=|\*\*?=?|\/\/?=?|<[<=>]?|>[=>]?|[&|^~]/,punctuation:/[{}[\];(),.:]/},Prism.languages.python["string-interpolation"].inside.interpolation.inside.rest=Prism.languages.python,Prism.languages.py=Prism.languages.python;

View File

@ -1,2 +0,0 @@
!function(){"use strict";function e(e){var n={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;","/":"&#x2F;"};return String(e).replace(/[&<>"'\/]/g,function(e){return n[e]})}function n(e){var n=[];return h.dom.findAll("a:not([data-nosearch])").map(function(t){var o=t.href,i=t.getAttribute("href"),r=e.parse(o).path;r&&-1===n.indexOf(r)&&!Docsify.util.isAbsolutePath(i)&&n.push(r)}),n}function t(e){localStorage.setItem("docsify.search.expires",Date.now()+e),localStorage.setItem("docsify.search.index",JSON.stringify(g))}function o(e,n,t,o){void 0===n&&(n="");var i,r=window.marked.lexer(n),a=window.Docsify.slugify,s={};return r.forEach(function(n){if("heading"===n.type&&n.depth<=o)i=t.toURL(e,{id:a(n.text)}),s[i]={slug:i,title:n.text,body:""};else{if(!i)return;s[i]?s[i].body?s[i].body+="\n"+(n.text||""):s[i].body=n.text:s[i]={slug:i,title:"",body:""}}}),a.clear(),s}function i(n){var t=[],o=[];Object.keys(g).forEach(function(e){o=o.concat(Object.keys(g[e]).map(function(n){return g[e][n]}))}),n=n.trim();var i=n.split(/[\s\-\\\\/]+/);1!==i.length&&(i=[].concat(n,i));for(var r=0;r<o.length;r++)!function(n){var r=o[n],a=!1,s="",c=r.title&&r.title.trim(),l=r.body&&r.body.trim(),f=r.slug||"";if(c&&l&&(i.forEach(function(n,t){var o=new RegExp(n,"gi"),i=-1,r=-1;if(i=c&&c.search(o),r=l&&l.search(o),i<0&&r<0)a=!1;else{a=!0,r<0&&(r=0);var f=0,d=0;f=r<11?0:r-10,d=0===f?70:r+n.length+60,d>l.length&&(d=l.length);var p="..."+e(l).substring(f,d).replace(o,'<em class="search-keyword">'+n+"</em>")+"...";s+=p}}),a)){var d={title:e(c),content:s,url:f};t.push(d)}}(r);return t}function r(e,i){h=Docsify;var r="auto"===e.paths,a=localStorage.getItem("docsify.search.expires")<Date.now();if(g=JSON.parse(localStorage.getItem("docsify.search.index")),a)g={};else if(!r)return;var s=r?n(i.router):e.paths,c=s.length,l=0;s.forEach(function(n){if(g[n])return l++;h.get(i.router.getFile(n)).then(function(r){g[n]=o(n,r,i.router,e.depth),c===++l&&t(e.maxAge)})})}function a(){Docsify.dom.style("\n.sidebar {\n padding-top: 0;\n}\n\n.search {\n margin-bottom: 20px;\n padding: 6px;\n border-bottom: 1px solid #eee;\n}\n\n.search .results-panel {\n display: none;\n}\n\n.search .results-panel.show {\n display: block;\n}\n\n.search input {\n outline: none;\n border: none;\n width: 100%;\n padding: 7px;\n line-height: 22px;\n font-size: 14px;\n -webkit-appearance: none;\n -moz-appearance: none;\n appearance: none;\n}\n\n.search h2 {\n font-size: 17px;\n margin: 10px 0;\n}\n\n.search a {\n text-decoration: none;\n color: inherit;\n}\n\n.search .matching-post {\n border-bottom: 1px solid #eee;\n}\n\n.search .matching-post:last-child {\n border-bottom: 0;\n}\n\n.search p {\n font-size: 14px;\n overflow: hidden;\n text-overflow: ellipsis;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n}\n\n.search p.empty {\n text-align: center;\n}")}function s(e,n){void 0===n&&(n="");var t='<input type="search" value="'+n+'" /><div class="results-panel"></div></div>',o=Docsify.dom.create("div",t),i=Docsify.dom.find("aside");Docsify.dom.toggleClass(o,"search"),Docsify.dom.before(i,o)}function c(e){var n=Docsify.dom.find("div.search"),t=Docsify.dom.find(n,".results-panel");if(!e)return t.classList.remove("show"),void(t.innerHTML="");var o=i(e),r="";o.forEach(function(e){r+='<div class="matching-post">\n<a href="'+e.url+'"> \n<h2>'+e.title+"</h2>\n<p>"+e.content+"</p>\n</a>\n</div>"}),t.classList.add("show"),t.innerHTML=r||'<p class="empty">'+y+"</p>"}function l(){var e,n=Docsify.dom.find("div.search"),t=Docsify.dom.find(n,"input");Docsify.dom.on(n,"click",function(e){return"A"!==e.target.tagName&&e.stopPropagation()}),Docsify.dom.on(t,"input",function(n){clearTimeout(e),e=setTimeout(function(e){return c(n.target.value.trim())},100)})}function f(e,n){var t=Docsify.dom.getNode('.search input[type="search"]');if(t)if("string"==typeof e)t.placeholder=e;else{var o=Object.keys(e).filter(function(e){return n.indexOf(e)>-1})[0];t.placeholder=e[o]}}function d(e,n){if("string"==typeof e)y=e;else{var t=Object.keys(e).filter(function(e){return n.indexOf(e)>-1})[0];y=e[t]}}function p(e,n){var t=n.router.parse().query.s;a(),s(e,t),l(),t&&setTimeout(function(e){return c(t)},500)}function u(e,n){f(e.placeholder,n.route.path),d(e.noData,n.route.path)}var h,g={},y="",m={placeholder:"Type to search",noData:"No Results!",paths:"auto",depth:2,maxAge:864e5},v=function(e,n){var t=Docsify.util,o=n.config.search||m;Array.isArray(o)?m.paths=o:"object"==typeof o&&(m.paths=Array.isArray(o.paths)?o.paths:"auto",m.maxAge=t.isPrimitive(o.maxAge)?o.maxAge:m.maxAge,m.placeholder=o.placeholder||m.placeholder,m.noData=o.noData||m.noData,m.depth=o.depth||m.depth);var i="auto"===m.paths;e.mounted(function(e){p(m,n),!i&&r(m,n)}),e.doneEach(function(e){u(m,n),i&&r(m,n)})};$docsify.plugins=[].concat(v,$docsify.plugins)}();

File diff suppressed because one or more lines are too long

View File

@ -1,660 +0,0 @@
(function () {
/*! medium-zoom 1.0.6 | MIT License | https://github.com/francoischalifour/medium-zoom */
var _extends = Object.assign || function (target) {
var arguments$1 = arguments;
for (var i = 1; i < arguments.length; i++) {
var source = arguments$1[i];
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
target[key] = source[key];
}
}
}
return target;
};
var isSupported = function isSupported(node) {
return node.tagName === 'IMG';
};
/* eslint-disable-next-line no-prototype-builtins */
var isNodeList = function isNodeList(selector) {
return NodeList.prototype.isPrototypeOf(selector);
};
var isNode = function isNode(selector) {
return selector && selector.nodeType === 1;
};
var isSvg = function isSvg(image) {
var source = image.currentSrc || image.src;
return source.substr(-4).toLowerCase() === '.svg';
};
var getImagesFromSelector = function getImagesFromSelector(selector) {
try {
if (Array.isArray(selector)) {
return selector.filter(isSupported);
}
if (isNodeList(selector)) {
// Do not use spread operator or Array.from() for IE support
return [].slice.call(selector).filter(isSupported);
}
if (isNode(selector)) {
return [selector].filter(isSupported);
}
if (typeof selector === 'string') {
// Do not use spread operator or Array.from() for IE support
return [].slice.call(document.querySelectorAll(selector)).filter(isSupported);
}
return [];
} catch (err) {
throw new TypeError('The provided selector is invalid.\n' + 'Expects a CSS selector, a Node element, a NodeList or an array.\n' + 'See: https://github.com/francoischalifour/medium-zoom');
}
};
var createOverlay = function createOverlay(background) {
var overlay = document.createElement('div');
overlay.classList.add('medium-zoom-overlay');
overlay.style.background = background;
return overlay;
};
var cloneTarget = function cloneTarget(template) {
var _template$getBounding = template.getBoundingClientRect(),
top = _template$getBounding.top,
left = _template$getBounding.left,
width = _template$getBounding.width,
height = _template$getBounding.height;
var clone = template.cloneNode();
var scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0;
var scrollLeft = window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft || 0;
clone.removeAttribute('id');
clone.style.position = 'absolute';
clone.style.top = top + scrollTop + 'px';
clone.style.left = left + scrollLeft + 'px';
clone.style.width = width + 'px';
clone.style.height = height + 'px';
clone.style.transform = '';
return clone;
};
var createCustomEvent = function createCustomEvent(type, params) {
var eventParams = _extends({
bubbles: false,
cancelable: false,
detail: undefined
}, params);
if (typeof window.CustomEvent === 'function') {
return new CustomEvent(type, eventParams);
}
var customEvent = document.createEvent('CustomEvent');
customEvent.initCustomEvent(type, eventParams.bubbles, eventParams.cancelable, eventParams.detail);
return customEvent;
};
var mediumZoom = function mediumZoom(selector) {
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
/**
* Ensure the compatibility with IE11 if no Promise polyfill are used.
*/
var Promise = window.Promise || function Promise(fn) {
function noop() {}
fn(noop, noop);
};
var _handleClick = function _handleClick(event) {
var target = event.target;
if (target === overlay) {
close();
return;
}
if (images.indexOf(target) === -1) {
return;
}
toggle({ target: target });
};
var _handleScroll = function _handleScroll() {
if (isAnimating || !active.original) {
return;
}
var currentScroll = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0;
if (Math.abs(scrollTop - currentScroll) > zoomOptions.scrollOffset) {
setTimeout(close, 150);
}
};
var _handleKeyUp = function _handleKeyUp(event) {
var key = event.key || event.keyCode;
// Close if escape key is pressed
if (key === 'Escape' || key === 'Esc' || key === 27) {
close();
}
};
var update = function update() {
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
var newOptions = options;
if (options.background) {
overlay.style.background = options.background;
}
if (options.container && options.container instanceof Object) {
newOptions.container = _extends({}, zoomOptions.container, options.container);
}
if (options.template) {
var template = isNode(options.template) ? options.template : document.querySelector(options.template);
newOptions.template = template;
}
zoomOptions = _extends({}, zoomOptions, newOptions);
images.forEach(function (image) {
image.dispatchEvent(createCustomEvent('medium-zoom:update', {
detail: { zoom: zoom }
}));
});
return zoom;
};
var clone = function clone() {
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
return mediumZoom(_extends({}, zoomOptions, options));
};
var attach = function attach() {
var arguments$1 = arguments;
for (var _len = arguments.length, selectors = Array(_len), _key = 0; _key < _len; _key++) {
selectors[_key] = arguments$1[_key];
}
var newImages = selectors.reduce(function (imagesAccumulator, currentSelector) {
return [].concat(imagesAccumulator, getImagesFromSelector(currentSelector));
}, []);
newImages.filter(function (newImage) {
return images.indexOf(newImage) === -1;
}).forEach(function (newImage) {
images.push(newImage);
newImage.classList.add('medium-zoom-image');
});
eventListeners.forEach(function (_ref) {
var type = _ref.type,
listener = _ref.listener,
options = _ref.options;
newImages.forEach(function (image) {
image.addEventListener(type, listener, options);
});
});
return zoom;
};
var detach = function detach() {
var arguments$1 = arguments;
for (var _len2 = arguments.length, selectors = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
selectors[_key2] = arguments$1[_key2];
}
if (active.zoomed) {
close();
}
var imagesToDetach = selectors.length > 0 ? selectors.reduce(function (imagesAccumulator, currentSelector) {
return [].concat(imagesAccumulator, getImagesFromSelector(currentSelector));
}, []) : images;
imagesToDetach.forEach(function (image) {
image.classList.remove('medium-zoom-image');
image.dispatchEvent(createCustomEvent('medium-zoom:detach', {
detail: { zoom: zoom }
}));
});
images = images.filter(function (image) {
return imagesToDetach.indexOf(image) === -1;
});
return zoom;
};
var on = function on(type, listener) {
var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
images.forEach(function (image) {
image.addEventListener('medium-zoom:' + type, listener, options);
});
eventListeners.push({ type: 'medium-zoom:' + type, listener: listener, options: options });
return zoom;
};
var off = function off(type, listener) {
var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
images.forEach(function (image) {
image.removeEventListener('medium-zoom:' + type, listener, options);
});
eventListeners = eventListeners.filter(function (eventListener) {
return !(eventListener.type === 'medium-zoom:' + type && eventListener.listener.toString() === listener.toString());
});
return zoom;
};
var open = function open() {
var _ref2 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
target = _ref2.target;
var _animate = function _animate() {
var container = {
width: document.documentElement.clientWidth,
height: document.documentElement.clientHeight,
left: 0,
top: 0,
right: 0,
bottom: 0
};
var viewportWidth = void 0;
var viewportHeight = void 0;
if (zoomOptions.container) {
if (zoomOptions.container instanceof Object) {
// The container is given as an object with properties like width, height, left, top
container = _extends({}, container, zoomOptions.container);
// We need to adjust custom options like container.right or container.bottom
viewportWidth = container.width - container.left - container.right - zoomOptions.margin * 2;
viewportHeight = container.height - container.top - container.bottom - zoomOptions.margin * 2;
} else {
// The container is given as an element
var zoomContainer = isNode(zoomOptions.container) ? zoomOptions.container : document.querySelector(zoomOptions.container);
var _zoomContainer$getBou = zoomContainer.getBoundingClientRect(),
_width = _zoomContainer$getBou.width,
_height = _zoomContainer$getBou.height,
_left = _zoomContainer$getBou.left,
_top = _zoomContainer$getBou.top;
container = _extends({}, container, {
width: _width,
height: _height,
left: _left,
top: _top
});
}
}
viewportWidth = viewportWidth || container.width - zoomOptions.margin * 2;
viewportHeight = viewportHeight || container.height - zoomOptions.margin * 2;
var zoomTarget = active.zoomedHd || active.original;
var naturalWidth = isSvg(zoomTarget) ? viewportWidth : zoomTarget.naturalWidth || viewportWidth;
var naturalHeight = isSvg(zoomTarget) ? viewportHeight : zoomTarget.naturalHeight || viewportHeight;
var _zoomTarget$getBoundi = zoomTarget.getBoundingClientRect(),
top = _zoomTarget$getBoundi.top,
left = _zoomTarget$getBoundi.left,
width = _zoomTarget$getBoundi.width,
height = _zoomTarget$getBoundi.height;
var scaleX = Math.min(naturalWidth, viewportWidth) / width;
var scaleY = Math.min(naturalHeight, viewportHeight) / height;
var scale = Math.min(scaleX, scaleY);
var translateX = (-left + (viewportWidth - width) / 2 + zoomOptions.margin + container.left) / scale;
var translateY = (-top + (viewportHeight - height) / 2 + zoomOptions.margin + container.top) / scale;
var transform = 'scale(' + scale + ') translate3d(' + translateX + 'px, ' + translateY + 'px, 0)';
active.zoomed.style.transform = transform;
if (active.zoomedHd) {
active.zoomedHd.style.transform = transform;
}
};
return new Promise(function (resolve) {
if (target && images.indexOf(target) === -1) {
resolve(zoom);
return;
}
var _handleOpenEnd = function _handleOpenEnd() {
isAnimating = false;
active.zoomed.removeEventListener('transitionend', _handleOpenEnd);
active.original.dispatchEvent(createCustomEvent('medium-zoom:opened', {
detail: { zoom: zoom }
}));
resolve(zoom);
};
if (active.zoomed) {
resolve(zoom);
return;
}
if (target) {
// The zoom was triggered manually via a click
active.original = target;
} else if (images.length > 0) {
var _images = images;
active.original = _images[0];
} else {
resolve(zoom);
return;
}
active.original.dispatchEvent(createCustomEvent('medium-zoom:open', {
detail: { zoom: zoom }
}));
scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0;
isAnimating = true;
active.zoomed = cloneTarget(active.original);
document.body.appendChild(overlay);
if (zoomOptions.template) {
var template = isNode(zoomOptions.template) ? zoomOptions.template : document.querySelector(zoomOptions.template);
active.template = document.createElement('div');
active.template.appendChild(template.content.cloneNode(true));
document.body.appendChild(active.template);
}
document.body.appendChild(active.zoomed);
window.requestAnimationFrame(function () {
document.body.classList.add('medium-zoom--opened');
});
active.original.classList.add('medium-zoom-image--hidden');
active.zoomed.classList.add('medium-zoom-image--opened');
active.zoomed.addEventListener('click', close);
active.zoomed.addEventListener('transitionend', _handleOpenEnd);
if (active.original.getAttribute('data-zoom-src')) {
active.zoomedHd = active.zoomed.cloneNode();
// Reset the `scrset` property or the HD image won't load.
active.zoomedHd.removeAttribute('srcset');
active.zoomedHd.removeAttribute('sizes');
active.zoomedHd.src = active.zoomed.getAttribute('data-zoom-src');
active.zoomedHd.onerror = function () {
clearInterval(getZoomTargetSize);
console.warn('Unable to reach the zoom image target ' + active.zoomedHd.src);
active.zoomedHd = null;
_animate();
};
// We need to access the natural size of the full HD
// target as fast as possible to compute the animation.
var getZoomTargetSize = setInterval(function () {
if ( active.zoomedHd.complete) {
clearInterval(getZoomTargetSize);
active.zoomedHd.classList.add('medium-zoom-image--opened');
active.zoomedHd.addEventListener('click', close);
document.body.appendChild(active.zoomedHd);
_animate();
}
}, 10);
} else if (active.original.hasAttribute('srcset')) {
// If an image has a `srcset` attribuet, we don't know the dimensions of the
// zoomed (HD) image (like when `data-zoom-src` is specified).
// Therefore the approach is quite similar.
active.zoomedHd = active.zoomed.cloneNode();
// Resetting the sizes attribute tells the browser to load the
// image best fitting the current viewport size, respecting the `srcset`.
active.zoomedHd.removeAttribute('sizes');
// In Firefox, the `loading` attribute needs to be set to `eager` (default
// value) for the load event to be fired.
active.zoomedHd.removeAttribute('loading');
// Wait for the load event of the hd image. This will fire if the image
// is already cached.
var loadEventListener = active.zoomedHd.addEventListener('load', function () {
active.zoomedHd.removeEventListener('load', loadEventListener);
active.zoomedHd.classList.add('medium-zoom-image--opened');
active.zoomedHd.addEventListener('click', close);
document.body.appendChild(active.zoomedHd);
_animate();
});
} else {
_animate();
}
});
};
var close = function close() {
return new Promise(function (resolve) {
if (isAnimating || !active.original) {
resolve(zoom);
return;
}
var _handleCloseEnd = function _handleCloseEnd() {
active.original.classList.remove('medium-zoom-image--hidden');
document.body.removeChild(active.zoomed);
if (active.zoomedHd) {
document.body.removeChild(active.zoomedHd);
}
document.body.removeChild(overlay);
active.zoomed.classList.remove('medium-zoom-image--opened');
if (active.template) {
document.body.removeChild(active.template);
}
isAnimating = false;
active.zoomed.removeEventListener('transitionend', _handleCloseEnd);
active.original.dispatchEvent(createCustomEvent('medium-zoom:closed', {
detail: { zoom: zoom }
}));
active.original = null;
active.zoomed = null;
active.zoomedHd = null;
active.template = null;
resolve(zoom);
};
isAnimating = true;
document.body.classList.remove('medium-zoom--opened');
active.zoomed.style.transform = '';
if (active.zoomedHd) {
active.zoomedHd.style.transform = '';
}
// Fade out the template so it's not too abrupt
if (active.template) {
active.template.style.transition = 'opacity 150ms';
active.template.style.opacity = 0;
}
active.original.dispatchEvent(createCustomEvent('medium-zoom:close', {
detail: { zoom: zoom }
}));
active.zoomed.addEventListener('transitionend', _handleCloseEnd);
});
};
var toggle = function toggle() {
var _ref3 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
target = _ref3.target;
if (active.original) {
return close();
}
return open({ target: target });
};
var getOptions = function getOptions() {
return zoomOptions;
};
var getImages = function getImages() {
return images;
};
var getZoomedImage = function getZoomedImage() {
return active.original;
};
var images = [];
var eventListeners = [];
var isAnimating = false;
var scrollTop = 0;
var zoomOptions = options;
var active = {
original: null,
zoomed: null,
zoomedHd: null,
template: null
// If the selector is omitted, it's replaced by the options
};if (Object.prototype.toString.call(selector) === '[object Object]') {
zoomOptions = selector;
} else if (selector || typeof selector === 'string' // to process empty string as a selector
) {
attach(selector);
}
// Apply the default option values
zoomOptions = _extends({
margin: 0,
background: '#fff',
scrollOffset: 40,
container: null,
template: null
}, zoomOptions);
var overlay = createOverlay(zoomOptions.background);
document.addEventListener('click', _handleClick);
document.addEventListener('keyup', _handleKeyUp);
document.addEventListener('scroll', _handleScroll);
window.addEventListener('resize', close);
var zoom = {
open: open,
close: close,
toggle: toggle,
update: update,
clone: clone,
attach: attach,
detach: detach,
on: on,
off: off,
getOptions: getOptions,
getImages: getImages,
getZoomedImage: getZoomedImage
};
return zoom;
};
function styleInject(css, ref) {
if ( ref === void 0 ) { ref = {}; }
var insertAt = ref.insertAt;
if (!css || typeof document === 'undefined') { return; }
var head = document.head || document.getElementsByTagName('head')[0];
var style = document.createElement('style');
style.type = 'text/css';
if (insertAt === 'top') {
if (head.firstChild) {
head.insertBefore(style, head.firstChild);
} else {
head.appendChild(style);
}
} else {
head.appendChild(style);
}
if (style.styleSheet) {
style.styleSheet.cssText = css;
} else {
style.appendChild(document.createTextNode(css));
}
}
var css = ".medium-zoom-overlay{position:fixed;top:0;right:0;bottom:0;left:0;opacity:0;transition:opacity .3s;will-change:opacity}.medium-zoom--opened .medium-zoom-overlay{cursor:pointer;cursor:zoom-out;opacity:1}.medium-zoom-image{cursor:pointer;cursor:zoom-in;transition:transform .3s cubic-bezier(.2,0,.2,1)!important}.medium-zoom-image--hidden{visibility:hidden}.medium-zoom-image--opened{position:relative;cursor:pointer;cursor:zoom-out;will-change:transform}";
styleInject(css);
/* eslint-disable no-unused-vars */
var matchesSelector =
Element.prototype.matches ||
Element.prototype.webkitMatchesSelector ||
Element.prototype.msMatchesSelector;
function install(hook) {
var zoom;
hook.doneEach(function (_) {
var elms = Array.apply(
null,
document.querySelectorAll(
'.markdown-section img:not(.emoji):not([data-no-zoom])'
)
);
elms = elms.filter(function (elm) { return matchesSelector.call(elm, 'a img') === false; });
if (zoom) {
zoom.detach();
}
zoom = mediumZoom(elms);
});
}
$docsify.plugins = [].concat(install, $docsify.plugins);
}());

View File

@ -1,4 +0,0 @@
- 剩爪(感谢建群)
- 激石网页设计-小徐感谢找bug和提建议
- Bumblebee47大怪兽47感谢测试
- kkjj828感谢建议