修复点击圆形控件不准问题;修复一个位置数据类型;继续修改ChromiumFrame;修改文档

This commit is contained in:
g1879 2023-01-04 00:33:10 +08:00
parent 51cb918b32
commit 57273ca79d
8 changed files with 193 additions and 23 deletions

View File

@ -13,3 +13,4 @@ from .chromium_page import ChromiumPage
from .session_page import SessionPage
from .drission import Drission
from .config import DriverOptions, SessionOptions
from .action_chains import ActionChains

View File

@ -156,7 +156,7 @@ class ChromiumElement(DrissionElement):
def _client_click_point(self):
"""返回元素左上角可接受点击的点视口坐标"""
m = self._get_client_rect('padding')
return (m[0], m[1]) if m else (0, 0)
return (self.client_midpoint[0], m[1]) if m else (0, 0)
@property
def _click_point(self):
@ -664,7 +664,7 @@ class ChromiumElement(DrissionElement):
"""实施点击 \n
:param client_x: 视口中的x坐标
:param client_y: 视口中的y坐标
:param button: 'left''right'
:param button: 'left' 'right'
:return: None
"""
self.page.driver.Input.dispatchMouseEvent(type='mousePressed', x=client_x, y=client_y, button=button,
@ -1385,17 +1385,17 @@ def _offset_scroll(ele, offset_x, offset_y):
:param offset_y: 偏移量y
:return: 视口中的坐标
"""
location = ele.location
midpoint = ele._click_point
lx = location['x'] + offset_x if offset_x else midpoint['x']
ly = location['y'] + offset_y if offset_y else midpoint['y']
loc_x, loc_y = ele.location
cp_x, cp_y = ele._click_point
lx = loc_x + offset_x if offset_x else cp_x
ly = loc_y + offset_y if offset_y else cp_y
if not _location_in_viewport(ele.page, lx, ly):
ele.page.scroll.to_location(lx, ly)
cl = ele.client_location
cm = ele._client_click_point
cx = cl['x'] + offset_x if offset_x else cm['x']
cy = cl['y'] + offset_y if offset_y else cm['y']
cl_x, cl_y = ele.client_location
ccp_x, ccp_y = ele._client_click_point
cx = cl_x + offset_x if offset_x else ccp_x
cy = cl_y + offset_y if offset_y else ccp_y
return cx, cy

View File

@ -13,7 +13,7 @@ from .chromium_element import ChromiumElement
class ChromiumFrame(ChromiumBase):
def __init__(self, page, ele):
self.page = page
self.address = self.page.address
self.address = page.address
node = page.run_cdp('DOM.describeNode', nodeId=ele.node_id, not_change=True)['node']
self.frame_id = node['frameId']
self._backend_id = ele.backend_id
@ -48,18 +48,25 @@ class ChromiumFrame(ChromiumBase):
node = self.page.run_cdp('DOM.describeNode', nodeId=self._frame_ele.node_id, not_change=True)['node']
if self._is_inner_frame():
print('-111')
self._is_diff_domain = False
self.doc_ele = ChromiumElement(self.page, backend_id=node['contentDocument']['backendNodeId'])
super().__init__(self.address, self.page.tab_id, self.page.timeout)
print('+111')
else:
print('-222')
self._is_diff_domain = True
self._tab_obj.stop()
super().__init__(self.address, self.frame_id, self.page.timeout)
obj_id = super().run_script('document;', as_expr=True)['objectId']
self.doc_ele = ChromiumElement(self, obj_id=obj_id)
print('+222')
def _check_ok(self):
if self._tab_obj._stopped.is_set():
self._reload()
try:
self._tab_obj.DOM.describeNode(nodeId=self.node_id)
except:
self._reload()
sleep(2)
def _get_new_document(self):
"""刷新cdp使用的document数据"""
@ -94,7 +101,6 @@ class ChromiumFrame(ChromiumBase):
def _onFrameStartedLoading(self, **kwargs):
"""页面开始加载时触发"""
# print('开始', kwargs['frameId'])
if kwargs['frameId'] == self.frame_id:
self._is_loading = True
if self._debug:
@ -102,7 +108,6 @@ class ChromiumFrame(ChromiumBase):
def _onFrameStoppedLoading(self, **kwargs):
"""页面加载完成后触发"""
# print('停止', kwargs['frameId'])
if kwargs['frameId'] == self.frame_id and self._first_run is False and self._is_loading:
if self._debug:
print('页面停止加载 FrameStoppedLoading')
@ -136,16 +141,19 @@ class ChromiumFrame(ChromiumBase):
@property
def tag(self):
"""返回元素tag"""
self._check_ok()
return self.frame_ele.tag
@property
def url(self):
"""返回frame当前访问的url"""
self._check_ok()
return self.doc_ele.run_script('return this.location.href;')
@property
def html(self):
"""返回元素outerHTML文本"""
self._check_ok()
tag = self.tag
out_html = self.page.run_cdp('DOM.getOuterHTML',
nodeId=self.frame_ele.node_id, not_change=True)['outerHTML']
@ -155,30 +163,31 @@ class ChromiumFrame(ChromiumBase):
@property
def inner_html(self):
"""返回元素innerHTML文本"""
self._check_ok()
return self.doc_ele.run_script('return this.documentElement.outerHTML;')
@property
def title(self):
"""返回页面title"""
while True:
try:
return self.ele('t:title').text
except:
self._reload()
self._check_ok()
return self.ele('t:title').text
@property
def cookies(self):
"""以dict格式返回cookies"""
self._check_ok()
return super().cookies if self._is_diff_domain else self.doc_ele.run_script('return this.cookie;')
@property
def attrs(self):
"""返回frame元素所有attribute属性"""
self._check_ok()
return self.frame_ele.attrs
@property
def frame_size(self):
"""返回frame内页面尺寸格式(长, 高)"""
self._check_ok()
w = self.doc_ele.run_script('return this.body.scrollWidth')
h = self.doc_ele.run_script('return this.body.scrollHeight')
return w, h
@ -186,31 +195,37 @@ class ChromiumFrame(ChromiumBase):
@property
def size(self):
"""返回frame元素大小"""
self._check_ok()
return self.frame_ele.size
@property
def active_ele(self):
"""返回当前焦点所在元素"""
self._check_ok()
return self.doc_ele.run_script('return this.activeElement;')
@property
def location(self):
"""返回frame元素左上角的绝对坐标"""
self._check_ok()
return self.frame_ele.location
@property
def is_displayed(self):
"""返回frame元素是否显示"""
self._check_ok()
return self.frame_ele.is_displayed
@property
def xpath(self):
"""返回frame的xpath绝对路径"""
self._check_ok()
return self.frame_ele.xpath
@property
def css_path(self):
"""返回frame的css selector绝对路径"""
self._check_ok()
return self.frame_ele.css_path
@property
@ -231,6 +246,7 @@ class ChromiumFrame(ChromiumBase):
def refresh(self):
"""刷新frame页面"""
self._check_ok()
self.doc_ele.run_script('this.location.reload();')
def attr(self, attr):
@ -238,6 +254,7 @@ class ChromiumFrame(ChromiumBase):
:param attr: 属性名
:return: 属性值文本没有该属性返回None
"""
self._check_ok()
return self.frame_ele.attr(attr)
def set_attr(self, attr, value):
@ -246,6 +263,7 @@ class ChromiumFrame(ChromiumBase):
:param value: 属性值
:return: None
"""
self._check_ok()
self.frame_ele.set_attr(attr, value)
def remove_attr(self, attr):
@ -253,6 +271,7 @@ class ChromiumFrame(ChromiumBase):
:param attr: 属性名
:return: None
"""
self._check_ok()
self.frame_ele.remove_attr(attr)
def run_script(self, script, as_expr=False, *args):
@ -262,6 +281,7 @@ class ChromiumFrame(ChromiumBase):
:param args: 参数按顺序在js文本中对应argument[0]argument[1]...
:return: 运行的结果
"""
self._check_ok()
return self.doc_ele.run_script(script, as_expr=as_expr, *args)
def parent(self, level_or_loc=1):
@ -269,6 +289,7 @@ class ChromiumFrame(ChromiumBase):
:param level_or_loc: 第几级父元素或定位符
:return: 上级元素对象
"""
self._check_ok()
return self.frame_ele.parent(level_or_loc)
def prev(self, filter_loc='', index=1, timeout=0):
@ -278,6 +299,7 @@ class ChromiumFrame(ChromiumBase):
:param timeout: 查找元素的超时时间
:return: 兄弟元素
"""
self._check_ok()
return self.frame_ele.prev(filter_loc, index, timeout)
def next(self, filter_loc='', index=1, timeout=0):
@ -287,6 +309,7 @@ class ChromiumFrame(ChromiumBase):
:param timeout: 查找元素的超时时间
:return: 兄弟元素
"""
self._check_ok()
return self.frame_ele.next(filter_loc, index, timeout)
def before(self, filter_loc='', index=1, timeout=None):
@ -296,6 +319,7 @@ class ChromiumFrame(ChromiumBase):
:param timeout: 查找元素的超时时间
:return: 本元素前面的某个元素或节点
"""
self._check_ok()
return self.frame_ele.before(filter_loc, index, timeout)
def after(self, filter_loc='', index=1, timeout=None):
@ -305,6 +329,7 @@ class ChromiumFrame(ChromiumBase):
:param timeout: 查找元素的超时时间
:return: 本元素后面的某个元素或节点
"""
self._check_ok()
return self.frame_ele.after(filter_loc, index, timeout)
def prevs(self, filter_loc='', timeout=0):
@ -313,6 +338,7 @@ class ChromiumFrame(ChromiumBase):
:param timeout: 查找元素的超时时间
:return: 兄弟元素或节点文本组成的列表
"""
self._check_ok()
return self.frame_ele.prevs(filter_loc, timeout)
def nexts(self, filter_loc='', timeout=0):
@ -321,6 +347,7 @@ class ChromiumFrame(ChromiumBase):
:param timeout: 查找元素的超时时间
:return: 兄弟元素或节点文本组成的列表
"""
self._check_ok()
return self.frame_ele.nexts(filter_loc, timeout)
def befores(self, filter_loc='', timeout=None):
@ -329,6 +356,7 @@ class ChromiumFrame(ChromiumBase):
:param timeout: 查找元素的超时时间
:return: 本元素前面的元素或节点组成的列表
"""
self._check_ok()
return self.frame_ele.befores(filter_loc, timeout)
def _ele(self, loc_or_ele, timeout=None, single=True, relative=False):
@ -354,6 +382,7 @@ class ChromiumFrame(ChromiumBase):
:param timeout: 连接超时时间
:return: 是否成功返回None表示不确定
"""
self._check_ok()
err = None
timeout = timeout if timeout is not None else self.timeouts.page_load

View File

@ -30,6 +30,8 @@ class ChromiumFrame(ChromiumBase):
def _reload(self) -> None: ...
def _check_ok(self) -> None: ...
def _get_new_document(self) -> None: ...
def _onFrameAttached(self, **kwargs): ...

View File

@ -294,7 +294,7 @@ def _get_chrome_path(ini_path: str = None,
# -----------从系统变量中获取--------------
if from_system_path:
paths = popen('set path').read().lower()
paths = popen('set path').read().encode('gbk').decode('utf-8').lower()
r = search(r'[^;]*chrome[^;]*', paths)
if r:

View File

@ -0,0 +1,128 @@
参考 selenium 的`ActionChains`,本库也提供了自己的`ActionChains`。语法更简洁易用,每个动作执行即生效,无须`perform()`
# ✔ 创建对象
## 📍 `ActionChains`
创建动作链对象非常简单,只要把`WebPage`对象或`ChromiumPage`对象传入即可。动作链只在这个页面上生效。
**参数:**
- `page``WebPage`对象或`ChromiumPage`对象
```python
# 创建前先导入
from DrissionPage import WebPage, ActionChains
page = WebPage()
ac = ActionChains(page)
```
# ✔ 移动鼠标
## 📍 `move_to()`
此方法用于移动鼠标到元素中点,或页面上的某个绝对坐标。可设置偏移量,当带偏移量时,偏移量相对于元素左上角坐标。
**参数:**
- `ele_or_loc`:元素对象或绝对坐标,坐标为`tuple`(int, int) 形式
- `offset_x`:偏移量 x
- `offset_y`:偏移量 y
**返回:**`ActionChains`对象自己
```python
ele = page('tag:a')
ac.move_to(ele_or_loc=ele) # 使鼠标移动过到 ele 元素上
```
## 📍 `move()`
此方法用于使鼠标相对当前位置移动若干距离。
**参数:**
- `offset_x`:偏移量 x
- `offset_y`:偏移量 y
**返回:**`ActionChains`对象自己
```python
ac.move(300, 0) # 鼠标向右移动 300 像素
```
## 📍 `up()`
此方法用于使鼠标相对当前位置向上移动若干距离。
**参数:**
- `pixel`:移动距离
**返回:**`ActionChains`对象自己
```python
ac.up(50) # 鼠标向上移动 50 像素
```
## 📍 `down()`
此方法用于使鼠标相对当前位置向下移动若干距离。
**参数:**
- `pixel`:移动距离
**返回:**`ActionChains`对象自己
## 📍 `left()`
此方法用于使鼠标相对当前位置向左移动若干距离。
**参数:**
- `pixel`:移动距离
**返回:**`ActionChains`对象自己
## 📍 `right()`
此方法用于使鼠标相对当前位置向右移动若干距离。
**参数:**
- `pixel`:移动距离
**返回:**`ActionChains`对象自己
# ✔ 点击鼠标
## 📍 `click()`
## 📍 `r_click()`
## 📍 `hold()`
## 📍 `release()`
# ✔ 滚动滚轮
## 📍 `scroll()`
# ✔ 键盘按键
## 📍 `key_down()`
## 📍 `key_up()`
# ✔ 等待
## 📍 `wait()`
# ✔ 链式操作

View File

@ -131,6 +131,15 @@ print(r.status_code)
此属性返回当前标签页的 id。
## 📍 `address`
此属性返回当前对象控制的页面地址和端口。
```python
print(page.address)
# 输出127.0.0.1:9222
```
## 📍 `driver`
此属性返回当前页面对象使用的`ChromiumDriver`对象。

View File

@ -22,6 +22,7 @@
* [🔨 3.7 页面操作](WebPage使用方法\3.7页面操作.md)
* [🔨 3.8 标签页操作](WebPage使用方法\3.8标签页操作.md)
* [🔨 3.9 iframe操作](WebPage使用方法\3.9iframe操作.md)
* [🔨 3.10 动作链](WebPage使用方法\3.10动作链.md)
* [🛠 4 MixPage 使用方法](#)