各种点击操作集成到click属性

This commit is contained in:
g1879 2023-02-15 22:19:51 +08:00
parent b9801caee7
commit 81b5827438
2 changed files with 185 additions and 105 deletions

View File

@ -31,6 +31,7 @@ class ChromiumElement(DrissionElement):
super().__init__(page)
self._select = None
self._scroll = None
self._click = None
self._tag = None
self._wait = None
@ -196,6 +197,13 @@ class ChromiumElement(DrissionElement):
self._scroll = ChromiumScroll(self)
return self._scroll
@property
def click(self):
"""返回用于点击的对象"""
if self._click is None:
self._click = Click(self)
return self._click
def parent(self, level_or_loc=1):
"""返回上面某一级父元素,可指定层数或用查询语法定位
:param level_or_loc: 第几级父元素或定位符
@ -593,101 +601,6 @@ class ChromiumElement(DrissionElement):
else:
self.input(('\ue009', 'a', '\ue017'), clear=False)
def click(self, by_js=None, retry=False, timeout=.2, wait_loading=0):
"""点击元素
如果遇到遮挡会重新尝试点击直到超时若都失败就改用js点击
:param by_js: 是否用js点击为True时直接用js点击为False时重试失败也不会改用js
:param retry: 遇到其它元素遮挡时是否重试
:param timeout: 尝试点击的超时时间不指定则使用父页面的超时时间retry为True时才生效
:param wait_loading: 等待页面进入加载状态超时时间
:return: 是否点击成功
"""
def do_it(cx, cy, lx, ly):
"""无遮挡返回True有遮挡返回False无元素返回None"""
try:
r = self.page.run_cdp('DOM.getNodeForLocation', x=lx, y=ly)
except Exception:
return None
if retry and r.get('nodeId') != self._node_id:
return False
self._click(cx, cy)
return True
if not by_js:
self.page.scroll.to_see(self)
if self.is_in_viewport:
client_x, client_y = self._client_click_point
if client_x:
loc_x, loc_y = self._click_point
click = do_it(client_x, client_y, loc_x, loc_y)
if click:
self.page.wait.load_start(wait_loading)
return True
timeout = timeout if timeout is not None else self.page.timeout
end_time = perf_counter() + timeout
while click is False and perf_counter() < end_time:
click = do_it(client_x, client_y, loc_x, loc_y)
if click is not None:
self.page.wait.load_start(wait_loading)
return True
if by_js is not False:
self.run_js('this.click();')
self.page.wait.load_start(wait_loading)
return True
return False
def click_at(self, offset_x=None, offset_y=None, button='left'):
"""带偏移量点击本元素相对于左上角坐标。不传入x或y值时点击元素左上角可接受点击的点
:param offset_x: 相对元素左上角坐标的x轴偏移量
:param offset_y: 相对元素左上角坐标的y轴偏移量
:param button: 左键还是右键
:return: None
"""
self.page.scroll.to_see(self)
x, y = offset_scroll(self, offset_x, offset_y)
self._click(x, y, button)
def r_click(self):
"""右键单击"""
self.page.scroll.to_see(self)
x, y = self._client_click_point
self._click(x, y, 'right')
def r_click_at(self, offset_x=None, offset_y=None):
"""带偏移量右键单击本元素相对于左上角坐标。不传入x或y值时点击元素中点
:param offset_x: 相对元素左上角坐标的x轴偏移量
:param offset_y: 相对元素左上角坐标的y轴偏移量
:return: None
"""
self.click_at(offset_x, offset_y, 'right')
def m_click(self):
"""中键单击"""
self.page.scroll.to_see(self)
x, y = self._client_click_point
self._click(x, y, 'middle')
def _click(self, client_x, client_y, button='left'):
"""实施点击
:param client_x: 视口中的x坐标
:param client_y: 视口中的y坐标
:param button: 'left' 'right'
:return: None
"""
self.page.run_cdp('Input.dispatchMouseEvent', type='mousePressed',
x=client_x, y=client_y, button=button, clickCount=1)
sleep(.05)
self.page.run_cdp('Input.dispatchMouseEvent', type='mouseReleased',
x=client_x, y=client_y, button=button)
def hover(self, offset_x=None, offset_y=None):
"""鼠标悬停可接受偏移量偏移量相对于元素左上角坐标。不传入x或y值时悬停在元素中点
:param offset_x: 相对元素左上角坐标的x轴偏移量
@ -845,6 +758,35 @@ class ChromiumElement(DrissionElement):
warn("此方法即将弃用请用wait.ele_xxxx()方法代替。", DeprecationWarning)
return ChromiumElementWaiter(self, loc_or_ele, timeout)
def click_at(self, offset_x=None, offset_y=None, button='left'):
"""带偏移量点击本元素相对于左上角坐标。不传入x或y值时点击元素左上角可接受点击的点
:param offset_x: 相对元素左上角坐标的x轴偏移量
:param offset_y: 相对元素左上角坐标的y轴偏移量
:param button: 左键还是右键
:return: None
"""
warn("此方法即将弃用请用click.left_at()方法代替。", DeprecationWarning)
self.click.left_at(offset_x, offset_y, button)
def r_click(self):
"""右键单击"""
warn("此方法即将弃用请用click.right()方法代替。", DeprecationWarning)
self.click.right()
def r_click_at(self, offset_x=None, offset_y=None):
"""带偏移量右键单击本元素相对于左上角坐标。不传入x或y值时点击元素中点
:param offset_x: 相对元素左上角坐标的x轴偏移量
:param offset_y: 相对元素左上角坐标的y轴偏移量
:return: None
"""
warn("此方法即将弃用请用click.right_at()方法代替。", DeprecationWarning)
self.click.right_at(offset_x, offset_y)
def m_click(self):
"""中键单击"""
warn("此方法即将弃用请用click.middle()方法代替。", DeprecationWarning)
self.click.middle()
class ChromiumShadowRootElement(BaseElement):
"""ChromiumShadowRootElement是用于处理ShadowRoot的类使用方法和ChromiumElement基本一致"""
@ -1170,7 +1112,8 @@ def find_by_xpath(ele, xpath, single, timeout, relative=True):
userGesture=True)
if single:
return NoneElement() if r['result']['subtype'] == 'null' else make_chromium_ele(ele.page, obj_id=r['result']['objectId'])
return NoneElement() if r['result']['subtype'] == 'null' \
else make_chromium_ele(ele.page, obj_id=r['result']['objectId'])
if r['result']['description'] == 'NodeList(0)':
return []
@ -1207,7 +1150,8 @@ def find_by_css(ele, selector, single, timeout):
userGesture=True)
if single:
return NoneElement() if r['result']['subtype'] == 'null' else make_chromium_ele(ele.page, obj_id=r['result']['objectId'])
return NoneElement() if r['result']['subtype'] == 'null' \
else make_chromium_ele(ele.page, obj_id=r['result']['objectId'])
if r['result']['description'] == 'NodeList(0)':
return []
@ -1407,6 +1351,120 @@ def send_key(ele, modifier, key):
ele.page.run_cdp('Input.dispatchKeyEvent', **data)
class Click(object):
def __init__(self, ele):
"""
:param ele: ChromiumElement
"""
self._ele = ele
def __call__(self, by_js=None, retry=False, timeout=.2, wait_loading=0):
"""点击元素
如果遇到遮挡会重新尝试点击直到超时若都失败就改用js点击
:param by_js: 是否用js点击为True时直接用js点击为False时重试失败也不会改用js
:param retry: 遇到其它元素遮挡时是否重试
:param timeout: 尝试点击的超时时间不指定则使用父页面的超时时间retry为True时才生效
:param wait_loading: 等待页面进入加载状态超时时间
:return: 是否点击成功
"""
return self.left(by_js, retry, timeout, wait_loading)
def left(self, by_js=None, retry=False, timeout=.2, wait_loading=0):
"""点击元素
如果遇到遮挡会重新尝试点击直到超时若都失败就改用js点击
:param by_js: 是否用js点击为True时直接用js点击为False时重试失败也不会改用js
:param retry: 遇到其它元素遮挡时是否重试
:param timeout: 尝试点击的超时时间不指定则使用父页面的超时时间retry为True时才生效
:param wait_loading: 等待页面进入加载状态超时时间
:return: 是否点击成功
"""
def do_it(cx, cy, lx, ly):
"""无遮挡返回True有遮挡返回False无元素返回None"""
try:
r = self._ele.page.run_cdp('DOM.getNodeForLocation', x=lx, y=ly)
except Exception:
return None
if retry and r.get('nodeId') != self._ele.node_id:
return False
self._click(cx, cy)
return True
if not by_js:
self._ele.page.scroll.to_see(self._ele)
if self._ele.is_in_viewport:
client_x, client_y = self._ele._client_click_point
if client_x:
loc_x, loc_y = self._ele._click_point
click = do_it(client_x, client_y, loc_x, loc_y)
if click:
self._ele.page.wait.load_start(wait_loading)
return True
timeout = timeout if timeout is not None else self._ele.page.timeout
end_time = perf_counter() + timeout
while click is False and perf_counter() < end_time:
click = do_it(client_x, client_y, loc_x, loc_y)
if click is not None:
self._ele.page.wait.load_start(wait_loading)
return True
if by_js is not False:
self._ele.run_js('this.click();')
self._ele.page.wait.load_start(wait_loading)
return True
return False
def left_at(self, offset_x=None, offset_y=None, button='left'):
"""带偏移量点击本元素相对于左上角坐标。不传入x或y值时点击元素左上角可接受点击的点
:param offset_x: 相对元素左上角坐标的x轴偏移量
:param offset_y: 相对元素左上角坐标的y轴偏移量
:param button: 左键还是右键
:return: None
"""
self._ele.page.scroll.to_see(self._ele)
x, y = offset_scroll(self._ele, offset_x, offset_y)
self._click(x, y, button)
def right(self):
"""右键单击"""
self._ele.page.scroll.to_see(self._ele)
x, y = self._ele._client_click_point
self._click(x, y, 'right')
def right_at(self, offset_x=None, offset_y=None):
"""带偏移量右键单击本元素相对于左上角坐标。不传入x或y值时点击元素中点
:param offset_x: 相对元素左上角坐标的x轴偏移量
:param offset_y: 相对元素左上角坐标的y轴偏移量
:return: None
"""
self.left_at(offset_x, offset_y, 'right')
def middle(self):
"""中键单击"""
self._ele.page.scroll.to_see(self._ele)
x, y = self._ele._client_click_point
self._click(x, y, 'middle')
def _click(self, client_x, client_y, button='left'):
"""实施点击
:param client_x: 视口中的x坐标
:param client_y: 视口中的y坐标
:param button: 'left' 'right'
:return: None
"""
self._ele.page.run_cdp('Input.dispatchMouseEvent', type='mousePressed',
x=client_x, y=client_y, button=button, clickCount=1)
sleep(.05)
self._ele.page.run_cdp('Input.dispatchMouseEvent', type='mouseReleased',
x=client_x, y=client_y, button=button)
class ChromiumScroll(object):
"""用于滚动的对象"""

View File

@ -27,6 +27,7 @@ class ChromiumElement(DrissionElement):
self._backend_id: str = ...
self._doc_id: str = ...
self._scroll: ChromiumScroll = ...
self._click: Click = ...
self._select: ChromiumSelect = ...
self._wait: ChromiumWaiter = ...
@ -103,6 +104,9 @@ class ChromiumElement(DrissionElement):
@property
def scroll(self) -> ChromiumScroll: ...
@property
def click(self) -> Click: ...
def parent(self, level_or_loc: Union[tuple, str, int] = 1) -> Union[ChromiumElement, None]: ...
def prev(self,
@ -197,7 +201,8 @@ class ChromiumElement(DrissionElement):
def _ele(self,
loc_or_str: Union[Tuple[str, str], str],
timeout: float = None, single: bool = True, relative: bool = False) \
-> Union[ChromiumElement, ChromiumFrame, str, NoneElement, List[Union[ChromiumElement, ChromiumFrame, str]]]: ...
-> Union[ChromiumElement, ChromiumFrame, str, NoneElement,
List[Union[ChromiumElement, ChromiumFrame, str]]]: ...
def style(self, style: str, pseudo_ele: str = '') -> str: ...
@ -213,9 +218,6 @@ class ChromiumElement(DrissionElement):
def clear(self, by_js: bool = False) -> None: ...
def click(self, by_js: bool = None, retry: bool = False, timeout: float = 0.2,
wait_loading: Union[bool, float] = 0) -> bool: ...
def click_at(self, offset_x: int = None, offset_y: int = None, button: str = 'left') -> None: ...
def r_click(self) -> None: ...
@ -224,8 +226,6 @@ class ChromiumElement(DrissionElement):
def m_click(self) -> None: ...
def _click(self, client_x: int, client_y: int, button: str = 'left') -> None: ...
def hover(self, offset_x: int = None, offset_y: int = None) -> None: ...
def drag(self, offset_x: int = 0, offset_y: int = 0, speed: int = 40, shake: bool = True) -> None: ...
@ -330,7 +330,8 @@ class ChromiumShadowRootElement(BaseElement):
loc_or_str: Union[Tuple[str, str], str],
timeout: float = None,
single: bool = True, relative: bool = False) \
-> Union[ChromiumElement, ChromiumFrame, NoneElement, str, List[Union[ChromiumElement, ChromiumFrame, str]]]: ...
-> Union[
ChromiumElement, ChromiumFrame, NoneElement, str, List[Union[ChromiumElement, ChromiumFrame, str]]]: ...
def _get_node_id(self, obj_id: str) -> str: ...
@ -343,7 +344,7 @@ def find_in_chromium_ele(ele: ChromiumElement,
loc: Union[str, Tuple[str, str]],
single: bool = True,
timeout: float = None,
relative: bool = True)\
relative: bool = True) \
-> Union[ChromiumElement, str, NoneElement, List[Union[ChromiumElement, str]]]: ...
@ -383,6 +384,27 @@ def send_enter(ele: ChromiumElement) -> None: ...
def send_key(ele: ChromiumElement, modifier: int, key: str) -> None: ...
class Click(object):
def __init__(self, ele: ChromiumElement):
self._ele: ChromiumElement = ...
def __call__(self, by_js: bool = None, retry: bool = False, timeout: float = 0.2,
wait_loading: Union[bool, float] = 0) -> bool: ...
def left(self, by_js: bool = None, retry: bool = False, timeout: float = 0.2,
wait_loading: Union[bool, float] = 0) -> bool: ...
def left_at(self, offset_x: int = None, offset_y: int = None, button: str = 'left') -> None: ...
def right(self): ...
def right_at(self, offset_x: int = None, offset_y: int = None) -> None: ...
def middle(self): ...
def _click(self, client_x: int, client_y: int, button: str = 'left') -> None: ...
class ChromiumScroll(object):
def __init__(self, page_or_ele: Union[ChromiumBase, ChromiumElement, ChromiumFrame]):