From 3c0a4e45cdc6698771d1021ce3e82d71498aeb8e Mon Sep 17 00:00:00 2001 From: g1879 Date: Tue, 21 Feb 2023 17:14:29 +0800 Subject: [PATCH] =?UTF-8?q?=E8=A7=A3=E5=86=B3=E5=90=8C=E5=9F=9F=E5=92=8C?= =?UTF-8?q?=E5=BC=82=E5=9F=9F=E9=97=B4=E8=B7=B3=E8=BD=AC=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DrissionPage/chromium_base.py | 27 +++++++++++---------------- DrissionPage/chromium_base.pyi | 3 --- DrissionPage/chromium_element.py | 10 +++++++--- DrissionPage/chromium_frame.py | 26 +++++++++++++++++++++----- DrissionPage/common/errors.py | 2 +- 5 files changed, 40 insertions(+), 28 deletions(-) diff --git a/DrissionPage/chromium_base.py b/DrissionPage/chromium_base.py index bf8d206..8496de2 100644 --- a/DrissionPage/chromium_base.py +++ b/DrissionPage/chromium_base.py @@ -233,12 +233,6 @@ class ChromiumBase(BasePage): raise RuntimeError('浏览器已关闭或链接已断开。') return self._tab_obj - @property - def _wait_driver(self): - """返回用于控制浏览器的ChromiumDriver对象,会先等待页面加载完毕""" - self.wait.load_complete() - return self.driver - @property def is_loading(self): """返回页面是否正在加载状态""" @@ -341,15 +335,16 @@ class ChromiumBase(BasePage): if ERROR not in r: return r - if r[ERROR] == 'Cannot find context with specified id': + error = r[ERROR] + if error == 'Cannot find context with specified id': raise ContextLossError - elif r[ERROR] in ('Could not find node with given id', 'Could not find object with given id'): + elif error.startswith('Could not find ') and error.endswith(' id'): raise ElementLossError - elif r[ERROR] == 'tab closed': + elif error == 'tab closed': raise TabClosedError - elif r[ERROR] == 'alert exists': + elif error == 'alert exists': pass - elif r[ERROR] in ('Node does not have a layout object', 'Could not compute box model.'): + elif error in ('Node does not have a layout object', 'Could not compute box model.'): raise NoRectError elif r['type'] == 'call_method_error': raise CallMethodError(f'\n错误:{r["error"]}\nmethod:{r["method"]}\nargs:{r["args"]}') @@ -468,19 +463,19 @@ class ChromiumBase(BasePage): else: raise ValueError('loc_or_str参数只能是tuple、str、ChromiumElement类型。') - timeout = timeout if timeout is not None else self.timeout + ok = False + nodeIds = None search_result = self.run_cdp_loaded('DOM.performSearch', query=loc, includeUserAgentShadowDOM=True) count = search_result['resultCount'] - nodeIds = None + timeout = timeout if timeout is not None else self.timeout end_time = perf_counter() + timeout - ok = False while True: if count > 0: count = 1 if single else count try: - nodeIds = self._wait_driver.DOM.getSearchResults(searchId=search_result['searchId'], - fromIndex=0, toIndex=count) + nodeIds = self.run_cdp_loaded('DOM.getSearchResults', searchId=search_result['searchId'], + fromIndex=0, toIndex=count) if nodeIds['nodeIds'][0] != 0: ok = True diff --git a/DrissionPage/chromium_base.pyi b/DrissionPage/chromium_base.pyi index 7d3fc11..fc9fb09 100644 --- a/DrissionPage/chromium_base.pyi +++ b/DrissionPage/chromium_base.pyi @@ -76,9 +76,6 @@ class ChromiumBase(BasePage): @property def driver(self) -> ChromiumDriver: ... - @property - def _wait_driver(self) -> ChromiumDriver: ... - @property def is_loading(self) -> bool: ... diff --git a/DrissionPage/chromium_element.py b/DrissionPage/chromium_element.py index 8486689..4303ef9 100644 --- a/DrissionPage/chromium_element.py +++ b/DrissionPage/chromium_element.py @@ -11,7 +11,8 @@ from warnings import warn from .base import DrissionElement, BaseElement from .common.constants import FRAME_ELEMENT, NoneElement, Settings -from .common.errors import ContextLossError, ElementLossError, JavaScriptError, NoRectError, ElementNotFoundError +from .common.errors import ContextLossError, ElementLossError, JavaScriptError, NoRectError, ElementNotFoundError, \ + CallMethodError from .common.keys import keys_to_typing, keyDescriptionForString, keyDefinitions from .common.locator import get_loc from .common.web import make_absolute_link, get_ele_txt, format_html, is_js_func, location_in_viewport, offset_scroll @@ -93,8 +94,11 @@ class ChromiumElement(DrissionElement): @property def attrs(self): """返回元素所有attribute属性""" - attrs = self.page.run_cdp('DOM.getAttributes', nodeId=self._node_id)['attributes'] - return {attrs[i]: attrs[i + 1] for i in range(0, len(attrs), 2)} + try: + attrs = self.page.run_cdp('DOM.getAttributes', nodeId=self._node_id)['attributes'] + return {attrs[i]: attrs[i + 1] for i in range(0, len(attrs), 2)} + except CallMethodError: + return {} @property def text(self): diff --git a/DrissionPage/chromium_frame.py b/DrissionPage/chromium_frame.py index 8229448..79c1657 100644 --- a/DrissionPage/chromium_frame.py +++ b/DrissionPage/chromium_frame.py @@ -68,6 +68,10 @@ class ChromiumFrame(ChromiumBase): def _reload(self): """重新获取document""" + debug = self._debug + if debug: + print('reload') + self._frame_ele = ChromiumElement(self.page, backend_id=self._backend_id) node = self.page.run_cdp('DOM.describeNode', backendNodeId=self._frame_ele.ids.backend_id)['node'] @@ -75,24 +79,34 @@ class ChromiumFrame(ChromiumBase): 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) + self._debug = debug else: self._is_diff_domain = True self._tab_obj.stop() super().__init__(self.address, self.frame_id, self.page.timeout) obj_id = super().run_js('document;', as_expr=True)['objectId'] self.doc_ele = ChromiumElement(self, obj_id=obj_id) + self._debug = debug def _check_ok(self): - """检查iframe元素是否还能使用,不能使用则重新加载""" + """用于应付同域异域之间跳转导致元素丢失问题""" if self._tab_obj._stopped.is_set(): self._reload() try: - self.page.run_cdp('DOM.describeNode', backendNodeId=self.ids.backend_id) + self.page.run_cdp('DOM.describeNode', nodeId=self.ids.node_id) except Exception: self._reload() # sleep(2) + def _onLoadEventFired(self, **kwargs): + """在页面刷新、变化后重新读取页面内容""" + # 用于覆盖父类方法,不能删 + if self._debug: + print('loadEventFired') + if self._debug_recorder: + self._debug_recorder.add_data((perf_counter(), '加载流程', 'loadEventFired')) + def _get_new_document(self): """刷新cdp使用的document数据""" if not self._is_reading: @@ -104,8 +118,7 @@ class ChromiumFrame(ChromiumBase): while True: try: if self._is_diff_domain is False: - node = self.page.run_cdp('DOM.describeNode', - backendNodeId=self.ids.backend_id)['node'] + node = self.page.run_cdp('DOM.describeNode', backendNodeId=self.ids.backend_id)['node'] self.doc_ele = ChromiumElement(self.page, backend_id=node['contentDocument']['backendNodeId']) else: @@ -414,12 +427,14 @@ class ChromiumFrame(ChromiumBase): :param raise_err: 找不到元素是是否抛出异常,为None时根据全局设置 :return: ChromiumElement对象 """ + self._check_ok() 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) + return self.doc_ele._ele(loc_or_ele, timeout, raise_err=raise_err) \ + if single else self.doc_ele.eles(loc_or_ele, timeout) def _d_connect(self, to_url, times=0, interval=1, show_errmsg=False, timeout=None): """尝试连接,重试若干次 @@ -439,6 +454,7 @@ class ChromiumFrame(ChromiumBase): result = self.driver.Page.navigate(url=to_url, frameId=self.frame_id) is_timeout = not self._wait_loaded(timeout) + sleep(.5) self.wait.load_complete() if is_timeout: diff --git a/DrissionPage/common/errors.py b/DrissionPage/common/errors.py index 25a259e..840dd3f 100644 --- a/DrissionPage/common/errors.py +++ b/DrissionPage/common/errors.py @@ -21,7 +21,7 @@ class ContextLossError(BaseError): class ElementLossError(BaseError): - _info = '页面内无此对象,可能因刷新已失效。' + _info = '元素对象因刷新已失效。' class CallMethodError(BaseError):