wait.ele_display()和wait.ele_hidden()会等待元素加载;

修复Page获取元素有时会返回str问题
This commit is contained in:
g1879 2023-12-21 10:48:48 +08:00
parent 4adb8247fd
commit 71dfe3037c
9 changed files with 153 additions and 66 deletions

View File

@ -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'

View File

@ -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

View File

@ -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'],

View File

@ -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: ...

View File

@ -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):

View File

@ -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)

View File

@ -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: ...

View File

@ -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

View File

@ -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):