3.0.30增加set_headers()、m_click();动作链增加功能

This commit is contained in:
g1879 2023-01-05 16:52:18 +08:00
parent 14a761e145
commit 126dade689
19 changed files with 295 additions and 85 deletions

View File

@ -25,7 +25,7 @@ class ActionChains:
def move_to(self, ele_or_loc, offset_x=0, offset_y=0):
"""鼠标移动到元素中点,或页面上的某个绝对坐标。可设置偏移量 \n
当带偏移量时偏移量相对于元素左上角坐标
:param ele_or_loc: 元素对象或绝对坐标坐标为tuple(int, int)形式
:param ele_or_loc: 元素对象绝对坐标或文本定位符坐标为tuple(int, int)形式
:param offset_x: 偏移量x
:param offset_y: 偏移量y
:return: self
@ -33,7 +33,8 @@ class ActionChains:
if isinstance(ele_or_loc, (tuple, list)):
lx = ele_or_loc[0] + offset_x
ly = ele_or_loc[1] + offset_y
elif 'ChromiumElement' in str(type(ele_or_loc)):
elif isinstance(ele_or_loc, str) or 'ChromiumElement' in str(type(ele_or_loc)):
ele_or_loc = self.page(ele_or_loc)
x, y = ele_or_loc.location if offset_x or offset_y else ele_or_loc.midpoint
lx = x + offset_x
ly = y + offset_y
@ -60,51 +61,111 @@ class ActionChains:
self._dr.Input.dispatchMouseEvent(type='mouseMoved', x=self.curr_x, y=self.curr_y, modifiers=self.modifier)
return self
def hold(self, on_ele=None):
"""点击并按住当前坐标或指定元素 \n
:param on_ele: ChromiumElement对象
:return: self
"""
if on_ele:
self.move_to(on_ele)
self._dr.Input.dispatchMouseEvent(type='mousePressed', button='left',
x=self.curr_x, y=self.curr_y, modifiers=self.modifier)
return self
def click(self, on_ele=None):
"""点击鼠标左键,可先移动到元素上 \n
:param on_ele: ChromiumElement元素
:param on_ele: ChromiumElement元素或文本定位符
:return: self
"""
if on_ele:
self.move_to(on_ele)
self._dr.Input.dispatchMouseEvent(type='mousePressed', button='left',
x=self.curr_x, y=self.curr_y, modifiers=self.modifier)
self._dr.Input.dispatchMouseEvent(type='mouseReleased', button='left',
x=self.curr_x, y=self.curr_y, modifiers=self.modifier)
self._hold('left').wait(.05)._release('left')
return self
def r_click(self, on_ele=None):
"""点击鼠标右键,可先移动到元素上 \n
:param on_ele: ChromiumElement元素
:param on_ele: ChromiumElement元素或文本定位符
:return: self
"""
if on_ele:
self.move_to(on_ele)
self._dr.Input.dispatchMouseEvent(type='mousePressed', button='right',
x=self.curr_x, y=self.curr_y, modifiers=self.modifier)
self._dr.Input.dispatchMouseEvent(type='mouseReleased', button='right',
x=self.curr_x, y=self.curr_y, modifiers=self.modifier)
self._hold('right').wait(.05)._release('right')
return self
def m_click(self, on_ele=None):
"""点击鼠标中键,可先移动到元素上 \n
:param on_ele: ChromiumElement元素或文本定位符
:return: self
"""
if on_ele:
self.move_to(on_ele)
self._hold('middle').wait(.05)._release('middle')
return self
def hold(self, on_ele=None):
"""按住鼠标左键,可先移动到元素上 \n
:param on_ele: ChromiumElement元素或文本定位符
:return: self
"""
if on_ele:
self.move_to(on_ele)
self._hold('left')
return self
def release(self, on_ele=None):
"""释放鼠标左键,可先移动到元素再释放 \n
:param on_ele: ChromiumElement对象
"""释放鼠标左键,可先移动到元素 \n
:param on_ele: ChromiumElement元素或文本定位符
:return: self
"""
if on_ele:
self.move_to(on_ele)
self._dr.Input.dispatchMouseEvent(type='mouseReleased', button='left',
self._release('left')
return self
def r_hold(self, on_ele=None):
"""按住鼠标右键,可先移动到元素上 \n
:param on_ele: ChromiumElement元素或文本定位符
:return: self
"""
if on_ele:
self.move_to(on_ele)
self._hold('right')
return self
def r_release(self, on_ele=None):
"""释放鼠标右键,可先移动到元素上 \n
:param on_ele: ChromiumElement元素或文本定位符
:return: self
"""
if on_ele:
self.move_to(on_ele)
self._release('right')
return self
def m_hold(self, on_ele=None):
"""按住鼠标中键,可先移动到元素上 \n
:param on_ele: ChromiumElement元素或文本定位符
:return: self
"""
if on_ele:
self.move_to(on_ele)
self._hold('middle')
return self
def m_release(self, on_ele=None):
"""释放鼠标中键,可先移动到元素上 \n
:param on_ele: ChromiumElement元素或文本定位符
:return: self
"""
if on_ele:
self.move_to(on_ele)
self._release('middle')
return self
def _hold(self, button):
"""按下鼠标按键 \n
:param button: 要按下的按键
:return: self
"""
self._dr.Input.dispatchMouseEvent(type='mousePressed', button=button, clickCount=1,
x=self.curr_x, y=self.curr_y, modifiers=self.modifier)
return self
def _release(self, button):
"""释放鼠标按键 \n
:param button: 要释放的按键
:return: self
"""
self._dr.Input.dispatchMouseEvent(type='mouseReleased', button=button, clickCount=1,
x=self.curr_x, y=self.curr_y, modifiers=self.modifier)
return self
@ -163,6 +224,17 @@ class ActionChains:
self.page.run_cdp('Input.dispatchKeyEvent', **data)
return self
def type(self, text):
"""输入文本 \n
:param text: 要输入的文本
:return: self
"""
for i in text:
self.key_down(i)
sleep(.05)
self.key_up(i)
return self
def wait(self, second):
"""等待若干秒"""
sleep(second)

View File

@ -20,20 +20,34 @@ class ActionChains:
self.curr_y: int = ...
self.modifier: int = ...
def move_to(self, ele_or_loc: Union[ChromiumElement, Tuple[int, int]],
def move_to(self, ele_or_loc: Union[ChromiumElement, Tuple[int, int], str],
offset_x: int = ..., offset_y: int = ...) -> ActionChains: ...
def move(self, offset_x: int = ..., offset_y: int = ...) -> ActionChains: ...
def hold(self, on_ele:ChromiumElement=...) -> ActionChains: ...
def click(self, on_ele: Union[ChromiumElement, str] = ...) -> ActionChains: ...
def click(self, on_ele:ChromiumElement=...) -> ActionChains: ...
def r_click(self, on_ele: Union[ChromiumElement, str] = ...) -> ActionChains: ...
def r_click(self, on_ele:ChromiumElement=...) -> ActionChains: ...
def m_click(self, on_ele: Union[ChromiumElement, str] = ...) -> ActionChains: ...
def release(self, on_ele:ChromiumElement=...) -> ActionChains: ...
def hold(self, on_ele: Union[ChromiumElement, str] = ...) -> ActionChains: ...
def scroll(self, delta_x: int = ..., delta_y: int = ..., on_ele:ChromiumElement=...) -> ActionChains: ...
def release(self, on_ele: Union[ChromiumElement, str] = ...) -> ActionChains: ...
def r_hold(self, on_ele: Union[ChromiumElement, str] = ...) -> ActionChains: ...
def r_release(self, on_ele: Union[ChromiumElement, str] = ...) -> ActionChains: ...
def m_hold(self, on_ele: Union[ChromiumElement, str] = ...) -> ActionChains: ...
def m_release(self, on_ele: Union[ChromiumElement, str] = ...) -> ActionChains: ...
def _hold(self, button: str) -> ActionChains: ...
def _release(self, button: str) -> ActionChains: ...
def scroll(self, delta_x: int = ..., delta_y: int = ..., on_ele: Union[ChromiumElement, str] = ...) -> ActionChains: ...
def up(self, pixel: int) -> ActionChains: ...
@ -47,6 +61,8 @@ class ActionChains:
def key_up(self, key: str) -> ActionChains: ...
def type(self, text: str) -> ActionChains: ...
def wait(self, second: float) -> ActionChains: ...
def _get_key_data(self, key: str, action: str) -> dict: ...

View File

@ -329,6 +329,8 @@ class ChromiumBase(BasePage):
:param timeout: 超时时间
:return: 等待结束时是否进入加载状态
"""
if timeout:
timeout = 2 if timeout is True else timeout
end_time = perf_counter() + timeout
while perf_counter() < end_time:
if self.is_loading:
@ -363,12 +365,12 @@ class ChromiumBase(BasePage):
result_cookies.append(c)
self._wait_driver.Network.setCookies(cookies=result_cookies)
# def set_headers(self, headers: dict) -> None:
# """设置固定发送的headers \n
# :param headers: dict格式的headers数据
# :return: None
# """
# self.run_cdp('Network.setExtraHTTPHeaders', headers=headers)
def set_headers(self, headers: dict) -> None:
"""设置固定发送的headers \n
:param headers: dict格式的headers数据
:return: None
"""
self.run_cdp('Network.setExtraHTTPHeaders', headers=headers, not_change=True)
def ele(self, loc_or_ele, timeout=None):
"""获取第一个符合条件的元素对象 \n

View File

@ -582,13 +582,13 @@ class ChromiumElement(DrissionElement):
else:
self.input(('\ue009', 'a', '\ue017'), clear=False)
def click(self, by_js=None, retry=False, timeout=.2, wait_loading=False):
def click(self, by_js=None, retry=False, timeout=.2, wait_loading=0):
"""点击元素 \n
如果遇到遮挡会重新尝试点击直到超时若都失败就改用js点击 \n
:param by_js: 是否用js点击为True时直接用js点击为False时重试失败也不会改用js
:param retry: 遇到其它元素遮挡时是否重试
:param timeout: 尝试点击的超时时间不指定则使用父页面的超时时间retry为True时才生效
:param wait_loading: 是否等待页面进入加载状态
:param wait_loading: 等待页面进入加载状态超时时间
:return: 是否点击成功
"""
@ -614,8 +614,7 @@ class ChromiumElement(DrissionElement):
click = do_it(client_x, client_y, loc_x, loc_y)
if click:
if wait_loading:
self.page.wait_loading()
self.page.wait_loading(wait_loading)
return True
timeout = timeout if timeout is not None else self.page.timeout
@ -624,14 +623,12 @@ class ChromiumElement(DrissionElement):
click = do_it(client_x, client_y, loc_x, loc_y)
if click is not None:
if wait_loading:
self.page.wait_loading()
self.page.wait_loading(wait_loading)
return True
if by_js is not False:
self.run_script('this.click();')
if wait_loading:
self.page.wait_loading()
self.page.wait_loading(wait_loading)
return True
return False
@ -652,6 +649,12 @@ class ChromiumElement(DrissionElement):
x, y = self._client_click_point
self._click(x, y, 'right')
def m_click(self):
"""中键单击"""
self.page.scroll_to_see(self)
x, y = self._client_click_point
self._click(x, y, 'middle')
def r_click_at(self, offset_x=None, offset_y=None):
"""带偏移量右键单击本元素相对于左上角坐标。不传入x或y值时点击元素中点 \n
:param offset_x: 相对元素左上角坐标的x轴偏移量
@ -669,7 +672,7 @@ class ChromiumElement(DrissionElement):
"""
self.page.driver.Input.dispatchMouseEvent(type='mousePressed', x=client_x, y=client_y, button=button,
clickCount=1)
sleep(.1)
sleep(.05)
self.page.driver.Input.dispatchMouseEvent(type='mouseReleased', x=client_x, y=client_y, button=button)
def hover(self, offset_x=None, offset_y=None):

View File

@ -204,12 +204,15 @@ class ChromiumElement(DrissionElement):
def clear(self, by_js: bool = ...) -> None: ...
def click(self, by_js: bool = ..., retry: bool = ..., timeout: float = ..., wait_loading: bool = ...) -> bool: ...
def click(self, by_js: bool = ..., retry: bool = ..., timeout: float = ...,
wait_loading: Union[bool, float] = ...) -> bool: ...
def click_at(self, offset_x: Union[int, str] = ..., offset_y: Union[int, str] = ..., button: str = ...) -> None: ...
def r_click(self) -> None: ...
def m_click(self) -> None: ...
def r_click_at(self, offset_x: Union[int, str] = ..., offset_y: Union[int, str] = ...) -> None: ...
def _click(self, client_x: int, client_y: int, button: str = ...) -> None: ...

View File

@ -73,6 +73,15 @@ class SessionPage(BasePage):
self.session.cookies.set(cookie['name'], cookie['value'], **kwargs)
def set_headers(self, headers):
"""设置通用的headers设置的headers值回逐个覆盖原有的不会清理原来的 \n
:param headers: dict形式的headers
:return: None
"""
headers = CaseInsensitiveDict(headers)
for i in headers:
self.session.headers[i] = headers[i]
def __call__(self, loc_or_str, timeout=None):
"""在内部查找元素 \n
ele2 = ele1('@id=ele_id') \n

View File

@ -34,6 +34,8 @@ class SessionPage(BasePage):
def set_cookies(self, cookies: Union[RequestsCookieJar, list, tuple, str, dict]) -> None: ...
def set_headers(self, headers: dict) -> None: ...
def __call__(self,
loc_or_str: Union[Tuple[str, str], str, SessionElement],
timeout: float = ...) -> Union[SessionElement, str, None]: ...

View File

@ -46,7 +46,7 @@ DrissionPage即 driver 和 session 组合而成的 page。
3.0 版已经发布,目前正在测试,欢迎试用并提出意见,让我做得更好。
文档正在更新,目前还是旧版,以后实例主要使用`WebPage``MixPage`与其不一致的地方才会说明。
# 💡 特性和亮点

View File

@ -0,0 +1,22 @@
以前的版本是对 selenium 进行重新封装实现的。从 3.0 开始,作者另起炉灶,对底层进行了重新开发,摆脱对 selenium 的依赖,增强了功能,提升了运行效率。
3.0 全新开发的页面对象是`WebPage`,支持 chromium 内核的浏览器(如 chrome 和 edge。除了保持之前的功能比依赖 selenium 的`MixPage`有以下优点:
- 无 webdriver 特征,不会被网站识别
- 无需为不同版本的浏览器下载不同的驱动
- 运行速度更快
- 可以跨 iframe 查找元素,无需切入切出
- 把 iframe 看作普通元素,获取后可直接在其中查找元素,逻辑更清晰
- 可以同时操作浏览器中的多个标签页,即使标签页为非激活状态,无需切换
- 可以直接读取浏览器缓存来保存图片,无需用 GUI 点击另存
- 可以对整个网页截图包括视口外的部分90以上版本浏览器支持
- 对 Linux 提供良好支持
新版是自己实现的功能,开发不会受太多限制,以后将主要对`WebPage`进行更新。旧版只会修 bug。

View File

@ -26,7 +26,7 @@ ac = ActionChains(page)
**参数:**
- `ele_or_loc`:元素对象或绝对坐标,坐标为`tuple`(int, int) 形式
- `ele_or_loc`:元素对象、文本定位符或绝对坐标,坐标为`tuple`(int, int) 形式
- `offset_x`:偏移量 x
@ -107,7 +107,7 @@ ac.up(50) # 鼠标向上移动 50 像素
**参数:**
- `on_ele``ChromiumElement`元素
- `on_ele``ChromiumElement`元素或文本定位符
**返回:**`ActionChains`对象自己
@ -117,7 +117,17 @@ ac.up(50) # 鼠标向上移动 50 像素
**参数:**
- `on_ele``ChromiumElement`元素
- `on_ele``ChromiumElement`元素或文本定位符
**返回:**`ActionChains`对象自己
## 📍 `m_click()`
此方法用于单击鼠标中键,单击前可先移动到元素上。
**参数:**
- `on_ele``ChromiumElement`元素或文本定位符
**返回:**`ActionChains`对象自己
@ -127,7 +137,7 @@ ac.up(50) # 鼠标向上移动 50 像素
**参数:**
- `on_ele``ChromiumElement`元素
- `on_ele``ChromiumElement`元素或文本定位符
**返回:**`ActionChains`对象自己
@ -137,7 +147,47 @@ ac.up(50) # 鼠标向上移动 50 像素
**参数:**
- `on_ele``ChromiumElement`元素
- `on_ele``ChromiumElement`元素或文本定位符
**返回:**`ActionChains`对象自己
## 📍 `r_hold()`
此方法用于按住鼠标右键不放,按住前可先移动到元素上。
**参数:**
- `on_ele``ChromiumElement`元素或文本定位符
**返回:**`ActionChains`对象自己
## 📍 `r_release()`
此方法用于释放鼠标右键,释放前可先移动到元素上。
**参数:**
- `on_ele``ChromiumElement`元素或文本定位符
**返回:**`ActionChains`对象自己
## 📍 `m_hold()`
此方法用于按住鼠标中键不放,按住前可先移动到元素上。
**参数:**
- `on_ele``ChromiumElement`元素或文本定位符
**返回:**`ActionChains`对象自己
## 📍 `m_release()`
此方法用于释放鼠标中键,释放前可先移动到元素上。
**参数:**
- `on_ele``ChromiumElement`元素或文本定位符
**返回:**`ActionChains`对象自己
@ -151,7 +201,7 @@ ac.up(50) # 鼠标向上移动 50 像素
- `delta_x`:滚轮变化值 x
- `delta_y`:滚轮变化值 y
- `on_ele``ChromiumElement`元素
- `on_ele``ChromiumElement`元素或文本定位符
**返回:**`ActionChains`对象自己
@ -183,6 +233,16 @@ ac.key_down(Keys.CTRL) # 按下 ctrl 键
**返回:**`ActionChains`对象自己
## 📍 `type()`
此方法用于输入一段文本。
**参数:**
- `text`:文本字符串
**返回:**`ActionChains`对象自己
# ✔ 等待
## 📍 `wait()`
@ -212,5 +272,3 @@ ac = ActionCHains(page)
# 链式操作
ac.move_to('tag:h1').right(30).click().key_down('a').key_up('a')
```

View File

@ -15,15 +15,14 @@ iframe 元素`ChromiumFrame`使用方法后面章节单独介绍。
此设计除了可保证点击成功,还可以用于检测页面上的遮罩层是否消失。遮罩层经常出现在 js 方式翻页的时候,它的覆盖会阻碍模拟点击,所以可以通过对其下面的元素不断重试点击,来判断遮罩层是否存在。当然,这个方法是否可行要看具体网页设计。
而如果直接使用 js 进行点击,则可无视任何遮挡,只要元素在 DOM 内,就能点击得到,这样可以根据须要灵活地对元素进行操作。
通常,点击链接后立刻获取新页面的元素,程序可自动等待元素加载,但若跳转前的页面拥有和跳转后页面相同定位符的元素,会导致过早获取元素,跳转后失效的问题。可以把`wait_loading`参数设为`True`,点击后程序会等待页面进入
loading 状态,才会继续往下执行,从而避免上述问题。
通常,点击链接后立刻获取新页面的元素,程序可自动等待元素加载,但若跳转前的页面拥有和跳转后页面相同定位符的元素,会导致过早获取元素,跳转后失效的问题。可以把`wait_loading`参数设置一个超时时间,点击后程序会等待页面进入 loading 状态,才会继续往下执行,从而避免上述问题。
**参数:**
- `by_js`:是否用 js 方式点击,为`None`时如`retry``True`,先用模拟方法点击,重试失败超时后改为用 js 点击;为`True`时直接用 js 点击;为`False`时即使重试超时也不会改用 js
- `retry`:遇到其它元素遮挡时,是否重试
- `timeout`:点击失败重试超时时间,为`None`时使用父页面`timeout`设置
- `wait_loading`是否等待页面进入加载状态
- `wait_loading`:等待页面进入加载状态的超时时间,默认 0
**返回:**`bool`,表示是否点击成功。
@ -92,6 +91,18 @@ ele.r_click()
ele.r_click_at(50, 50)
```
## 📍 `m_click()`
此方法实现中键单击元素。
**参数:** 无
**返回:**`None`
```python
ele.m_click()
```
# ✔️ 输入内容
## 📍 `clear()`

View File

@ -1,4 +1,4 @@
* [⭐️ 1 简介](README.md)
* [⭐️ 1 概述](README.md)
* [🧭 2 入门指南](#)
@ -14,6 +14,7 @@
* [🛠 3 WebPage 使用方法](#)
* [🔨 3.0 简介](WebPage使用方法\3.0概述.md)
* [🔨 3.1 创建页面对象](WebPage使用方法\3.1创建页面对象.md)
* [🔨 3.2 访问网页](WebPage使用方法\3.2访问网页.md)
* [🔨 3.3 查找元素](WebPage使用方法\3.3查找元素.md)
@ -53,11 +54,12 @@
* [⚙️ 6.2 监听浏览器网络](进阶使用\监听浏览器网络.md)
* [⚙️ 6.3 下载文件](进阶使用\下载文件.md)
* [⚡️ 7 实用示例](#)
* [⚡️ 7 示例和技巧](#)
* [🌠 自动登录码云](实用示例\自动登录码云.md)
* [🌠 采集猫眼电影TOP100榜](实用示例\采集猫眼电影TOP100榜.md)
* [🌠 下载星巴克产品图片](实用示例\下载星巴克产品图片.md)
* [🌠 下载豆瓣图书封面图片](实用示例\下载豆瓣图书封面图片.md)
* [🌠 多线程操作多标签页](实用示例\多线程操作多标签页.md)
* [🔖 9 版本历史](版本历史.md)

Binary file not shown.

After

Width:  |  Height:  |  Size: 409 KiB

View File

@ -1,3 +1,13 @@
# 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`返回
@ -14,7 +24,7 @@
- `change_mode()`增加`copy_cookies`参数
- ###### 调整`WebPage`生成的元素对象的`prev()``next()``before()``after()`参数顺序
- 调整`WebPage`生成的元素对象的`prev()``next()``before()``after()`参数顺序
- 修复读取页面时小概率失效问题

View File

@ -1,6 +1,6 @@
这个示例演示用浏览器采集数据。
# ✔️ 页面分析
# ✔️ 采集目标
目标网址:[https://www.maoyan.com/board/4](https://www.maoyan.com/board/4)

View File

@ -6,7 +6,7 @@ with open("README.md", "r", encoding='utf-8') as fh:
setup(
name="DrissionPage",
version="3.0.28",
version="3.0.30",
author="g1879",
author_email="g1879@qq.com",
description="A module that integrates selenium and requests session, encapsulates common page operations.",