From 71dfe3037c873887e4909dcb620a0e367d29d1c6 Mon Sep 17 00:00:00 2001 From: g1879 Date: Thu, 21 Dec 2023 10:48:48 +0800 Subject: [PATCH] =?UTF-8?q?wait.ele=5Fdisplay()=E5=92=8Cwait.ele=5Fhidden(?= =?UTF-8?q?)=E4=BC=9A=E7=AD=89=E5=BE=85=E5=85=83=E7=B4=A0=E5=8A=A0?= =?UTF-8?q?=E8=BD=BD=EF=BC=9B=20=E4=BF=AE=E5=A4=8DPage=E8=8E=B7=E5=8F=96?= =?UTF-8?q?=E5=85=83=E7=B4=A0=E6=9C=89=E6=97=B6=E4=BC=9A=E8=BF=94=E5=9B=9E?= =?UTF-8?q?str=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DrissionPage/__init__.py | 2 +- DrissionPage/_base/base.py | 13 ++--- DrissionPage/_elements/chromium_element.py | 61 ++++++++++++++++++++- DrissionPage/_elements/chromium_element.pyi | 28 ++++++---- DrissionPage/_pages/chromium_base.py | 34 +++++------- DrissionPage/_pages/chromium_frame.py | 2 - DrissionPage/_pages/chromium_frame.pyi | 2 +- DrissionPage/_units/waiter.py | 50 ++++++++++++----- DrissionPage/_units/waiter.pyi | 27 ++++++--- 9 files changed, 153 insertions(+), 66 deletions(-) diff --git a/DrissionPage/__init__.py b/DrissionPage/__init__.py index e51d275..2b4c57c 100644 --- a/DrissionPage/__init__.py +++ b/DrissionPage/__init__.py @@ -13,4 +13,4 @@ from ._configs.chromium_options import ChromiumOptions from ._configs.session_options import SessionOptions __all__ = ['ChromiumPage', 'ChromiumOptions', 'SessionOptions', 'SessionPage', 'WebPage', '__version__'] -__version__ = '4.0.0b24' +__version__ = '4.0.0b25' diff --git a/DrissionPage/_base/base.py b/DrissionPage/_base/base.py index b71730a..c3dbec1 100644 --- a/DrissionPage/_base/base.py +++ b/DrissionPage/_base/base.py @@ -447,14 +447,13 @@ class BasePage(BaseParser): r = self._find_elements(loc_or_ele, timeout=timeout, single=single, raise_err=raise_err) - if not single: + if r or isinstance(r, list): return r - if isinstance(r, NoneElement): - if Settings.raise_when_ele_not_found or raise_err is True: - raise ElementNotFoundError(None, method, {'loc_or_str': loc_or_ele}) - else: - r.method = method - r.args = {'loc_or_str': loc_or_ele} + if Settings.raise_when_ele_not_found or raise_err is True: + raise ElementNotFoundError(None, method, {'loc_or_str': loc_or_ele}) + + r.method = method + r.args = {'loc_or_str': loc_or_ele} return r @abstractmethod diff --git a/DrissionPage/_elements/chromium_element.py b/DrissionPage/_elements/chromium_element.py index a99eaed..5528b28 100644 --- a/DrissionPage/_elements/chromium_element.py +++ b/DrissionPage/_elements/chromium_element.py @@ -1040,7 +1040,7 @@ class ShadowRoot(BaseElement): else: nod_ids = self.page.run_cdp('DOM.querySelectorAll', nodeId=self._node_id, selector=loc[1])['nodeId'] - result = [make_chromium_ele(self.page, node_id=n) for n in nod_ids] + result = make_chromium_eles(self.page, node_ids=nod_ids, single=False) else: eles = make_session_ele(self.html).eles(loc) @@ -1205,6 +1205,7 @@ def make_chromium_ele(page, node_id=None, obj_id=None): if node_id: node = page.run_cdp('DOM.describeNode', nodeId=node_id) if node['node']['nodeName'] in ('#text', '#comment'): + # todo: Node() return node['node']['nodeValue'] backend_id = node['node']['backendNodeId'] obj_id = page.run_cdp('DOM.resolveNode', nodeId=node_id)['object']['objectId'] @@ -1212,6 +1213,7 @@ def make_chromium_ele(page, node_id=None, obj_id=None): elif obj_id: node = page.run_cdp('DOM.describeNode', objectId=obj_id) if node['node']['nodeName'] in ('#text', '#comment'): + # todo: Node() return node['node']['nodeValue'] backend_id = node['node']['backendNodeId'] node_id = node['node']['nodeId'] @@ -1227,6 +1229,61 @@ def make_chromium_ele(page, node_id=None, obj_id=None): return ele +def make_chromium_eles(page, node_ids=None, obj_ids=None, single=True, ele_only=True): + """根据node id或object id生成相应元素对象 + :param page: ChromiumPage对象 + :param node_ids: 元素的node id + :param obj_ids: 元素的object id + :param single: 是否获取但个元素 + :param ele_only: 是否只要ele + :return: ChromiumElement对象或ChromiumFrame对象 + """ + nodes = [] + if node_ids: + for node_id in node_ids: + if not node_id: + return False + node = page.run_cdp('DOM.describeNode', nodeId=node_id) + if node['node']['nodeName'] in ('#text', '#comment'): + if ele_only: + continue + else: + # todo: Node() + pass + + obj_id = page.run_cdp('DOM.resolveNode', nodeId=node_id)['object']['objectId'] + ele = ChromiumElement(page, obj_id=obj_id, node_id=node_id, backend_id=node['node']['backendNodeId']) + if ele.tag in __FRAME_ELEMENT__: + from .._pages.chromium_frame import ChromiumFrame + ele = ChromiumFrame(page, ele) + if single: + return ele + nodes.append(ele) + + if obj_ids: + for obj_id in obj_ids: + if not obj_id: + return False + node = page.run_cdp('DOM.describeNode', objectId=obj_id) + if node['node']['nodeName'] in ('#text', '#comment'): + if ele_only: + continue + else: + # todo: Node + pass + + ele = ChromiumElement(page, obj_id=obj_id, node_id=node['node']['nodeId'], + backend_id=node['node']['backendNodeId']) + if ele.tag in __FRAME_ELEMENT__: + from .._pages.chromium_frame import ChromiumFrame + ele = ChromiumFrame(page, ele) + if single: + return ele + nodes.append(ele) + + return NoneElement(page) if single and not nodes else nodes + + def make_js_for_find_ele_by_xpath(xpath, type_txt, node_txt): """生成用xpath在元素中查找元素的js文本 :param xpath: xpath文本 @@ -1347,7 +1404,7 @@ def parse_js_result(page, ele, result): elif class_name == 'HTMLDocument': return result else: - return make_chromium_ele(page, obj_id=result['objectId']) + return make_chromium_eles(page, obj_ids=(result['objectId'],)) elif sub_type == 'array': r = page.run_cdp('Runtime.getProperties', objectId=result['objectId'], diff --git a/DrissionPage/_elements/chromium_element.pyi b/DrissionPage/_elements/chromium_element.pyi index 7dd3a52..f006917 100644 --- a/DrissionPage/_elements/chromium_element.pyi +++ b/DrissionPage/_elements/chromium_element.pyi @@ -26,10 +26,10 @@ PIC_TYPE = Literal['jpg', 'jpeg', 'png', 'webp', True] class ChromiumElement(DrissionElement): - def __init__(self, page: ChromiumBase, node_id: str = None, obj_id: str = None, backend_id: int = None): + def __init__(self, page: ChromiumBase, node_id: int = None, obj_id: str = None, backend_id: int = None): self._tag: str = ... self.page: Union[ChromiumPage, WebPage] = ... - self._node_id: str = ... + self._node_id: int = ... self._obj_id: str = ... self._backend_id: int = ... self._doc_id: str = ... @@ -217,11 +217,11 @@ class ChromiumElement(DrissionElement): def drag_to(self, ele_or_loc: Union[tuple, ChromiumElement], duration: float = 0.5) -> None: ... - def _get_obj_id(self, node_id: str = None, backend_id: int = None) -> str: ... + def _get_obj_id(self, node_id: int = None, backend_id: int = None) -> str: ... - def _get_node_id(self, obj_id: str = None, backend_id: int = None) -> str: ... + def _get_node_id(self, obj_id: str = None, backend_id: int = None) -> int: ... - def _get_backend_id(self, node_id: str) -> str: ... + def _get_backend_id(self, node_id: int) -> int: ... def _get_ele_path(self, mode: str) -> str: ... @@ -230,7 +230,7 @@ class ShadowRoot(BaseElement): def __init__(self, parent_ele: ChromiumElement, obj_id: str = None, backend_id: int = None): self._obj_id: str = ... - self._node_id: str = ... + self._node_id: int = ... self._backend_id: int = ... self.page: ChromiumPage = ... self.parent_ele: ChromiumElement = ... @@ -294,11 +294,11 @@ class ShadowRoot(BaseElement): -> Union[ChromiumElement, ChromiumFrame, NoneElement, str, List[Union[ChromiumElement, ChromiumFrame, str]]]: ... - def _get_node_id(self, obj_id: str) -> str: ... + def _get_node_id(self, obj_id: str) -> int: ... - def _get_obj_id(self, back_id: str) -> str: ... + def _get_obj_id(self, back_id: int) -> str: ... - def _get_backend_id(self, node_id: str) -> int: ... + def _get_backend_id(self, node_id: int) -> int: ... def find_in_chromium_ele(ele: ChromiumElement, loc: Union[str, Tuple[str, str]], @@ -314,10 +314,18 @@ def find_by_css(ele: ChromiumElement, selector: str, single: bool, timeout: float) -> Union[ChromiumElement, List[ChromiumElement], NoneElement]: ... -def make_chromium_ele(page: ChromiumBase, node_id: str = ..., obj_id: str = ...) \ +def make_chromium_ele(page: ChromiumBase, node_id: int = ..., obj_id: str = ...) \ -> Union[ChromiumElement, ChromiumFrame, str]: ... +def make_chromium_eles(page: ChromiumBase, + node_ids: Union[tuple, list] = None, + obj_ids: Union[tuple, list] = None, + single: bool = True, + ele_only: bool = True) -> Union[ChromiumElement, ChromiumFrame, NoneElement, +List[Union[ChromiumElement, ChromiumFrame]]]: ... + + def make_js_for_find_ele_by_xpath(xpath: str, type_txt: str, node_txt: str) -> str: ... diff --git a/DrissionPage/_pages/chromium_base.py b/DrissionPage/_pages/chromium_base.py index 86821db..bfa068a 100644 --- a/DrissionPage/_pages/chromium_base.py +++ b/DrissionPage/_pages/chromium_base.py @@ -11,13 +11,13 @@ from time import perf_counter, sleep from urllib.parse import quote from .._base.base import BasePage +from .._elements.chromium_element import ChromiumElement, run_js, make_chromium_eles +from .._elements.none_element import NoneElement +from .._elements.session_element import make_session_ele from .._functions.locator import get_loc, is_loc from .._functions.settings import Settings from .._functions.tools import get_usable_path, raise_error from .._functions.web import location_in_viewport -from .._elements.chromium_element import ChromiumElement, run_js, make_chromium_ele -from .._elements.none_element import NoneElement -from .._elements.session_element import make_session_ele from .._units.actions import Actions from .._units.listener import Listener from .._units.rect import TabRect @@ -26,7 +26,7 @@ from .._units.scroller import PageScroller from .._units.setter import ChromiumBaseSetter from .._units.states import PageStates from .._units.waiter import BaseWaiter -from ..errors import ContextLostError, ElementLostError, CDPError, PageClosedError, ElementNotFoundError +from ..errors import ContextLostError, CDPError, PageClosedError, ElementNotFoundError __ERROR__ = 'error' @@ -586,9 +586,11 @@ class ChromiumBase(BasePage): timeout = timeout if timeout is not None else self.timeout end_time = perf_counter() + timeout + search_ids = [] try: search_result = self.run_cdp_loaded('DOM.performSearch', query=loc, includeUserAgentShadowDOM=True) count = search_result['resultCount'] + search_ids.append(search_result['searchId']) except ContextLostError: search_result = None count = 0 @@ -606,33 +608,27 @@ class ChromiumBase(BasePage): pass if ok: - try: - if single: - r = make_chromium_ele(self, node_id=nodeIds['nodeIds'][0]) - break - - else: - r = [make_chromium_ele(self, node_id=i) for i in nodeIds['nodeIds']] - break - - except ElementLostError: + r = make_chromium_eles(self, node_ids=nodeIds['nodeIds'], single=single) + if r is not False: + break + else: ok = False try: search_result = self.run_cdp_loaded('DOM.performSearch', query=loc, includeUserAgentShadowDOM=True) count = search_result['resultCount'] + search_ids.append(search_result['searchId']) except ContextLostError: pass if perf_counter() >= end_time: - return NoneElement(self) if single else [] + return None if single else [] sleep(.1) - try: - self.run_cdp('DOM.discardSearchResults', searchId=search_result['searchId']) - except: - pass + for _id in search_ids: + self._driver.run('DOM.discardSearchResults', searchId=_id) + return r def refresh(self, ignore_cache=False): diff --git a/DrissionPage/_pages/chromium_frame.py b/DrissionPage/_pages/chromium_frame.py index 228d96f..873f331 100644 --- a/DrissionPage/_pages/chromium_frame.py +++ b/DrissionPage/_pages/chromium_frame.py @@ -586,9 +586,7 @@ class ChromiumFrame(ChromiumBase): """ if isinstance(loc_or_ele, ChromiumElement): return loc_or_ele - self.wait.load_complete() - return self.doc_ele._ele(loc_or_ele, timeout, raise_err=raise_err) if single else self.doc_ele.eles(loc_or_ele, timeout) diff --git a/DrissionPage/_pages/chromium_frame.pyi b/DrissionPage/_pages/chromium_frame.pyi index 8bb10ba..8dc1380 100644 --- a/DrissionPage/_pages/chromium_frame.pyi +++ b/DrissionPage/_pages/chromium_frame.pyi @@ -96,7 +96,7 @@ class ChromiumFrame(ChromiumBase): def _obj_id(self) -> str: ... @property - def _node_id(self) -> str: ... + def _node_id(self) -> int: ... @property def active_ele(self) -> ChromiumElement: ... diff --git a/DrissionPage/_units/waiter.py b/DrissionPage/_units/waiter.py index e02ca44..042a0e6 100644 --- a/DrissionPage/_units/waiter.py +++ b/DrissionPage/_units/waiter.py @@ -36,7 +36,16 @@ class BaseWaiter(object): :param raise_err: 等待失败时是否报错,为None时根据Settings设置 :return: 是否等待成功 """ - ele = self._driver._ele(loc_or_ele, raise_err=False, timeout=0) + if timeout is None: + timeout = self._driver.timeout + end_time = perf_counter() + timeout + ele = self._driver._ele(loc_or_ele, raise_err=False, timeout=timeout) + timeout = end_time - perf_counter() + if timeout <= 0: + if raise_err is True or Settings.raise_when_wait_failed is True: + raise WaitTimeoutError('等待元素显示失败。') + else: + return False return ele.wait.display(timeout, raise_err=raise_err) def ele_hidden(self, loc_or_ele, timeout=None, raise_err=None): @@ -46,7 +55,16 @@ class BaseWaiter(object): :param raise_err: 等待失败时是否报错,为None时根据Settings设置 :return: 是否等待成功 """ - ele = self._driver._ele(loc_or_ele, raise_err=False, timeout=0) + if timeout is None: + timeout = self._driver.timeout + end_time = perf_counter() + timeout + ele = self._driver._ele(loc_or_ele, raise_err=False, timeout=timeout) + timeout = end_time - perf_counter() + if timeout <= 0: + if raise_err is True or Settings.raise_when_wait_failed is True: + raise WaitTimeoutError('等待元素显示失败。') + else: + return False return ele.wait.hidden(timeout, raise_err=raise_err) def ele_loaded(self, loc, timeout=None, raise_err=None): @@ -296,7 +314,7 @@ class ElementWaiter(object): :param raise_err: 等待失败时是否报错,为None时根据Settings设置 :return: 是否等待成功 """ - return self._wait_state('is_alive', False, timeout, raise_err) + return self._wait_state('is_alive', False, timeout, raise_err, err_text='等待元素被删除失败。') def display(self, timeout=None, raise_err=None): """等待元素从dom显示 @@ -304,7 +322,7 @@ class ElementWaiter(object): :param raise_err: 等待失败时是否报错,为None时根据Settings设置 :return: 是否等待成功 """ - return self._wait_state('is_displayed', True, timeout, raise_err) + return self._wait_state('is_displayed', True, timeout, raise_err, err_text='等待元素显示失败。') def hidden(self, timeout=None, raise_err=None): """等待元素从dom隐藏 @@ -312,7 +330,7 @@ class ElementWaiter(object): :param raise_err: 等待失败时是否报错,为None时根据Settings设置 :return: 是否等待成功 """ - return self._wait_state('is_displayed', False, timeout, raise_err) + return self._wait_state('is_displayed', False, timeout, raise_err, err_text='等待元素隐藏失败。') def covered(self, timeout=None, raise_err=None): """等待当前元素被遮盖 @@ -320,15 +338,15 @@ class ElementWaiter(object): :param raise_err: 等待失败时是否报错,为None时根据Settings设置 :return: 是否等待成功 """ - return self._wait_state('is_covered', True, timeout, raise_err) + return self._wait_state('is_covered', True, timeout, raise_err, err_text='等待元素被覆盖失败。') def not_covered(self, timeout=None, raise_err=None): - """等待当前元素被遮盖 + """等待当前元素不被遮盖 :param timeout: 超时时间,为None使用元素所在页面timeout属性 :param raise_err: 等待失败时是否报错,为None时根据Settings设置 :return: 是否等待成功 """ - return self._wait_state('is_covered', False, timeout, raise_err) + return self._wait_state('is_covered', False, timeout, raise_err, err_text='等待元素不被覆盖失败。') def enabled(self, timeout=None, raise_err=None): """等待当前元素变成可用 @@ -336,15 +354,15 @@ class ElementWaiter(object): :param raise_err: 等待失败时是否报错,为None时根据Settings设置 :return: 是否等待成功 """ - return self._wait_state('is_enabled', True, timeout, raise_err) + return self._wait_state('is_enabled', True, timeout, raise_err, err_text='等待元素变成可用失败。') def disabled(self, timeout=None, raise_err=None): - """等待当前元素变成可用 + """等待当前元素变成不可用 :param timeout: 超时时间,为None使用元素所在页面timeout属性 :param raise_err: 等待失败时是否报错,为None时根据Settings设置 :return: 是否等待成功 """ - return self._wait_state('is_enabled', False, timeout, raise_err) + return self._wait_state('is_enabled', False, timeout, raise_err, err_text='等待元素变成不可用失败。') def disabled_or_deleted(self, timeout=None, raise_err=None): """等待当前元素变成不可用或从DOM移除 @@ -361,7 +379,7 @@ class ElementWaiter(object): sleep(.05) if raise_err is True or Settings.raise_when_wait_failed is True: - raise WaitTimeoutError('等待元素隐藏或删除失败。') + raise WaitTimeoutError('等待元素隐藏或被删除失败。') else: return False @@ -397,14 +415,16 @@ class ElementWaiter(object): else: return False - def _wait_state(self, attr, mode=False, timeout=None, raise_err=None): - """等待元素某个bool状态到达指定状态 + def _wait_state(self, attr, mode=False, timeout=None, raise_err=None, err_text=None): + """等待元素某个元素状态到达指定状态 :param attr: 状态名称 :param mode: True或False :param timeout: 超时时间,为None使用元素所在页面timeout属性 :param raise_err: 等待失败时是否报错,为None时根据Settings设置 + :param err_text: 抛出错误时显示的信息 :return: 是否等待成功 """ + err_text = err_text or '等待元素状态改变失败。' if timeout is None: timeout = self._page.timeout end_time = perf_counter() + timeout @@ -414,7 +434,7 @@ class ElementWaiter(object): sleep(.05) if raise_err is True or Settings.raise_when_wait_failed is True: - raise WaitTimeoutError('等待元素状态改变失败。') + raise WaitTimeoutError(err_text) else: return False diff --git a/DrissionPage/_units/waiter.pyi b/DrissionPage/_units/waiter.pyi index f41d2ad..1782f86 100644 --- a/DrissionPage/_units/waiter.pyi +++ b/DrissionPage/_units/waiter.pyi @@ -18,17 +18,23 @@ class BaseWaiter(object): def __call__(self, second: float) -> None: ... - def ele_deleted(self, loc_or_ele: Union[str, tuple, ChromiumElement], timeout: float = None, - raise_err: bool = None) -> bool: ... + def ele_deleted(self, + loc_or_ele: Union[str, tuple, ChromiumElement], + timeout: float = None, + raise_err: bool = None) -> bool: ... - def ele_display(self, loc_or_ele: Union[str, tuple, ChromiumElement], timeout: float = None, + def ele_display(self, + loc_or_ele: Union[str, tuple, ChromiumElement], + timeout: float = None, raise_err: bool = None) -> bool: ... def ele_hidden(self, loc_or_ele: Union[str, tuple, ChromiumElement], timeout: float = None, raise_err: bool = None) -> bool: ... - def ele_loaded(self, loc: Union[str, tuple], timeout: float = None, - raise_err: bool = None) -> Union[bool, ChromiumElement]: ... + def ele_loaded(self, + loc: Union[str, tuple], + timeout: float = None, + raise_err: bool = None) -> Union[bool, ChromiumElement]: ... def _loading(self, timeout: float = None, start: bool = True, gap: float = .01, raise_err: bool = None) -> bool: ... @@ -64,9 +70,7 @@ class PageWaiter(TabWaiter): class ElementWaiter(object): - def __init__(self, - page: ChromiumBase, - ele: ChromiumElement): + def __init__(self, page: ChromiumBase, ele: ChromiumElement): self._ele: ChromiumElement = ... self._page: ChromiumBase = ... @@ -90,7 +94,12 @@ class ElementWaiter(object): def stop_moving(self, gap: float = .1, timeout: float = None, raise_err: bool = None) -> bool: ... - def _wait_state(self, attr: str, mode: bool = False, timeout: float = None, raise_err: bool = None) -> bool: ... + def _wait_state(self, + attr: str, + mode: bool = False, + timeout: float = None, + raise_err: bool = None, + err_text: str = None) -> bool: ... class FrameWaiter(BaseWaiter, ElementWaiter):