diff --git a/DrissionPage/__init__.py b/DrissionPage/__init__.py index 5ee4cc9..098d123 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.0b28' +__version__ = '4.0.0b29' diff --git a/DrissionPage/_base/base.py b/DrissionPage/_base/base.py index 8549d78..8f2598d 100644 --- a/DrissionPage/_base/base.py +++ b/DrissionPage/_base/base.py @@ -144,7 +144,7 @@ class DrissionElement(BaseElement): """返回直接子元素元素或节点组成的列表,可用查询语法筛选 :param filter_loc: 用于筛选的查询语法 :param index: 第几个查询结果,1开始 - :param timeout: 查找节点的超时时间 + :param timeout: 查找节点的超时时间(秒) :param ele_only: 是否只获取元素,为False时把文本、注释节点也纳入 :return: 直接子元素或节点文本组成的列表 """ @@ -174,7 +174,7 @@ class DrissionElement(BaseElement): """返回前面的一个兄弟元素,可用查询语法筛选,可指定返回筛选结果的第几个 :param filter_loc: 用于筛选的查询语法 :param index: 前面第几个查询结果,1开始 - :param timeout: 查找节点的超时时间 + :param timeout: 查找节点的超时时间(秒) :param ele_only: 是否只获取元素,为False时把文本、注释节点也纳入 :return: 兄弟元素 """ @@ -194,7 +194,7 @@ class DrissionElement(BaseElement): """返回后面的一个兄弟元素,可用查询语法筛选,可指定返回筛选结果的第几个 :param filter_loc: 用于筛选的查询语法 :param index: 后面第几个查询结果,1开始 - :param timeout: 查找节点的超时时间 + :param timeout: 查找节点的超时时间(秒) :param ele_only: 是否只获取元素,为False时把文本、注释节点也纳入 :return: 兄弟元素 """ @@ -214,7 +214,7 @@ class DrissionElement(BaseElement): """返回前面的一个兄弟元素,可用查询语法筛选,可指定返回筛选结果的第几个 :param filter_loc: 用于筛选的查询语法 :param index: 前面第几个查询结果,1开始 - :param timeout: 查找节点的超时时间 + :param timeout: 查找节点的超时时间(秒) :param ele_only: 是否只获取元素,为False时把文本、注释节点也纳入 :return: 本元素前面的某个元素或节点 """ @@ -234,7 +234,7 @@ class DrissionElement(BaseElement): """返回后面的一个兄弟元素,可用查询语法筛选,可指定返回筛选结果的第几个 :param filter_loc: 用于筛选的查询语法 :param index: 后面第几个查询结果,1开始 - :param timeout: 查找节点的超时时间 + :param timeout: 查找节点的超时时间(秒) :param ele_only: 是否只获取元素,为False时把文本、注释节点也纳入 :return: 本元素后面的某个元素或节点 """ @@ -253,7 +253,7 @@ class DrissionElement(BaseElement): def children(self, filter_loc='', timeout=None, ele_only=True): """返回直接子元素元素或节点组成的列表,可用查询语法筛选 :param filter_loc: 用于筛选的查询语法 - :param timeout: 查找节点的超时时间 + :param timeout: 查找节点的超时时间(秒) :param ele_only: 是否只获取元素,为False时把文本、注释节点也纳入 :return: 直接子元素或节点文本组成的列表 """ @@ -272,7 +272,7 @@ class DrissionElement(BaseElement): def prevs(self, filter_loc='', timeout=None, ele_only=True): """返回前面全部兄弟元素或节点组成的列表,可用查询语法筛选 :param filter_loc: 用于筛选的查询语法 - :param timeout: 查找节点的超时时间 + :param timeout: 查找节点的超时时间(秒) :param ele_only: 是否只获取元素,为False时把文本、注释节点也纳入 :return: 兄弟元素或节点文本组成的列表 """ @@ -281,7 +281,7 @@ class DrissionElement(BaseElement): def nexts(self, filter_loc='', timeout=None, ele_only=True): """返回后面全部兄弟元素或节点组成的列表,可用查询语法筛选 :param filter_loc: 用于筛选的查询语法 - :param timeout: 查找节点的超时时间 + :param timeout: 查找节点的超时时间(秒) :param ele_only: 是否只获取元素,为False时把文本、注释节点也纳入 :return: 兄弟元素或节点文本组成的列表 """ @@ -290,7 +290,7 @@ class DrissionElement(BaseElement): def befores(self, filter_loc='', timeout=None, ele_only=True): """返回后面全部兄弟元素或节点组成的列表,可用查询语法筛选 :param filter_loc: 用于筛选的查询语法 - :param timeout: 查找节点的超时时间 + :param timeout: 查找节点的超时时间(秒) :param ele_only: 是否只获取元素,为False时把文本、注释节点也纳入 :return: 本元素前面的元素或节点组成的列表 """ @@ -300,7 +300,7 @@ class DrissionElement(BaseElement): def afters(self, filter_loc='', timeout=None, ele_only=True): """返回前面全部兄弟元素或节点组成的列表,可用查询语法筛选 :param filter_loc: 用于筛选的查询语法 - :param timeout: 查找节点的超时时间 + :param timeout: 查找节点的超时时间(秒) :param ele_only: 是否只获取元素,为False时把文本、注释节点也纳入 :return: 本元素后面的元素或节点组成的列表 """ @@ -314,7 +314,7 @@ class DrissionElement(BaseElement): :param filter_loc: 用于筛选的查询语法 :param direction: 'following' 或 'preceding',查找的方向 :param brother: 查找范围,在同级查找还是整个dom前后查找 - :param timeout: 查找等待时间 + :param timeout: 查找等待时间(秒) :return: 元素对象或字符串 """ if index is not None and index < 1: diff --git a/DrissionPage/_base/browser.py b/DrissionPage/_base/browser.py index 881b794..2d8b891 100644 --- a/DrissionPage/_base/browser.py +++ b/DrissionPage/_base/browser.py @@ -160,7 +160,7 @@ class Browser(object): def quit(self, timeout=5, force=False): """关闭浏览器 - :param timeout: 等待浏览器关闭超时时间 + :param timeout: 等待浏览器关闭超时时间(秒) :param force: 是否立刻强制终止进程 :return: None """ diff --git a/DrissionPage/_base/driver.py b/DrissionPage/_base/driver.py index caf2404..46a52fb 100644 --- a/DrissionPage/_base/driver.py +++ b/DrissionPage/_base/driver.py @@ -3,13 +3,14 @@ @Author : g1879 @Contact : g1879@qq.com """ -from json import dumps, loads +from json import dumps, loads, JSONDecodeError from queue import Queue, Empty from threading import Thread, Event from time import perf_counter from requests import get -from websocket import WebSocketTimeoutException, WebSocketConnectionClosedException, create_connection +from websocket import (WebSocketTimeoutException, WebSocketConnectionClosedException, create_connection, + WebSocketException) class Driver(object): @@ -74,7 +75,7 @@ class Driver(object): except (OSError, WebSocketConnectionClosedException): self.method_results.pop(ws_id, None) - return None + return {'error': {'message': 'page closed'}} while not self._stopped.is_set(): try: @@ -102,7 +103,7 @@ class Driver(object): msg = loads(msg_json) except WebSocketTimeoutException: continue - except: + except (WebSocketException, OSError, WebSocketConnectionClosedException, JSONDecodeError): self.stop() return @@ -119,10 +120,10 @@ class Driver(object): if 'method' in msg: if msg['method'].startswith('Page.javascriptDialog'): self.alert_flag = msg['method'].endswith('Opening') - if msg['method'] in self.immediate_event_handlers: - function = self.immediate_event_handlers.get(msg['method']) - if function: - function(**msg['params']) + function = self.immediate_event_handlers.get(msg['method']) + if function: + Thread(target=function, kwargs=msg['params']).start() + # function(**msg['params']) else: self.event_queue.put(msg) @@ -154,12 +155,10 @@ class Driver(object): :return: 执行结果 """ if self._stopped.is_set(): - return {'error': 'tab closed', 'type': 'tab_closed'} + return {'error': 'page closed', 'type': 'page_closed'} timeout = kwargs.pop('_timeout', 15) result = self._send({'method': _method, 'params': kwargs}, timeout=timeout) - if result is None: - return {'error': 'tab closed', 'type': 'tab_closed'} if 'result' not in result and 'error' in result: return {'error': result['error']['message'], 'type': result.get('type', 'call_method_error'), 'method': _method, 'args': kwargs} @@ -169,8 +168,7 @@ class Driver(object): def start(self): """启动连接""" self._stopped.clear() - self._ws = create_connection(self._websocket_url, enable_multithread=True, - suppress_origin=True) + self._ws = create_connection(self._websocket_url, enable_multithread=True, suppress_origin=True) self._recv_th.start() self._handle_event_th.start() return True diff --git a/DrissionPage/_elements/chromium_element.py b/DrissionPage/_elements/chromium_element.py index 63f0ff0..ce88ff5 100644 --- a/DrissionPage/_elements/chromium_element.py +++ b/DrissionPage/_elements/chromium_element.py @@ -80,7 +80,7 @@ class ChromiumElement(DrissionElement): def __call__(self, loc_or_str, timeout=None): """在内部查找元素 :param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串 - :param timeout: 超时时间 + :param timeout: 超时时间(秒) :return: ChromiumElement对象或属性、文本 """ return self.ele(loc_or_str, timeout) @@ -214,31 +214,31 @@ class ChromiumElement(DrissionElement): """ return super().parent(level_or_loc, index) - def child(self, filter_loc='', index=1, timeout=0, ele_only=True): + def child(self, filter_loc='', index=1, timeout=None, ele_only=True): """返回当前元素的一个符合条件的直接子元素,可用查询语法筛选,可指定返回筛选结果的第几个 :param filter_loc: 用于筛选的查询语法 :param index: 第几个查询结果,1开始 - :param timeout: 查找节点的超时时间 + :param timeout: 查找节点的超时时间(秒) :param ele_only: 是否只获取元素,为False时把文本、注释节点也纳入 :return: 直接子元素或节点文本 """ return super().child(filter_loc, index, timeout, ele_only=ele_only) - def prev(self, filter_loc='', index=1, timeout=0, ele_only=True): + def prev(self, filter_loc='', index=1, timeout=None, ele_only=True): """返回当前元素前面一个符合条件的同级元素,可用查询语法筛选,可指定返回筛选结果的第几个 :param filter_loc: 用于筛选的查询语法 :param index: 前面第几个查询结果,1开始 - :param timeout: 查找节点的超时时间 + :param timeout: 查找节点的超时时间(秒) :param ele_only: 是否只获取元素,为False时把文本、注释节点也纳入 :return: 兄弟元素或节点文本 """ return super().prev(filter_loc, index, timeout, ele_only=ele_only) - def next(self, filter_loc='', index=1, timeout=0, ele_only=True): + def next(self, filter_loc='', index=1, timeout=None, ele_only=True): """返回当前元素后面一个符合条件的同级元素,可用查询语法筛选,可指定返回筛选结果的第几个 :param filter_loc: 用于筛选的查询语法 :param index: 第几个查询结果,1开始 - :param timeout: 查找节点的超时时间 + :param timeout: 查找节点的超时时间(秒) :param ele_only: 是否只获取元素,为False时把文本、注释节点也纳入 :return: 兄弟元素或节点文本 """ @@ -249,7 +249,7 @@ class ChromiumElement(DrissionElement): 查找范围不限同级元素,而是整个DOM文档 :param filter_loc: 用于筛选的查询语法 :param index: 前面第几个查询结果,1开始 - :param timeout: 查找节点的超时时间 + :param timeout: 查找节点的超时时间(秒) :param ele_only: 是否只获取元素,为False时把文本、注释节点也纳入 :return: 本元素前面的某个元素或节点 """ @@ -260,34 +260,34 @@ class ChromiumElement(DrissionElement): 查找范围不限同级元素,而是整个DOM文档 :param filter_loc: 用于筛选的查询语法 :param index: 第几个查询结果,1开始 - :param timeout: 查找节点的超时时间 + :param timeout: 查找节点的超时时间(秒) :param ele_only: 是否只获取元素,为False时把文本、注释节点也纳入 :return: 本元素后面的某个元素或节点 """ return super().after(filter_loc, index, timeout, ele_only=ele_only) - def children(self, filter_loc='', timeout=0, ele_only=True): + def children(self, filter_loc='', timeout=None, ele_only=True): """返回当前元素符合条件的直接子元素或节点组成的列表,可用查询语法筛选 :param filter_loc: 用于筛选的查询语法 - :param timeout: 查找节点的超时时间 + :param timeout: 查找节点的超时时间(秒) :param ele_only: 是否只获取元素,为False时把文本、注释节点也纳入 :return: 直接子元素或节点文本组成的列表 """ return super().children(filter_loc, timeout, ele_only=ele_only) - def prevs(self, filter_loc='', timeout=0, ele_only=True): + def prevs(self, filter_loc='', timeout=None, ele_only=True): """返回当前元素前面符合条件的同级元素或节点组成的列表,可用查询语法筛选 :param filter_loc: 用于筛选的查询语法 - :param timeout: 查找节点的超时时间 + :param timeout: 查找节点的超时时间(秒) :param ele_only: 是否只获取元素,为False时把文本、注释节点也纳入 :return: 兄弟元素或节点文本组成的列表 """ return super().prevs(filter_loc, timeout, ele_only=ele_only) - def nexts(self, filter_loc='', timeout=0, ele_only=True): + def nexts(self, filter_loc='', timeout=None, ele_only=True): """返回当前元素后面符合条件的同级元素或节点组成的列表,可用查询语法筛选 :param filter_loc: 用于筛选的查询语法 - :param timeout: 查找节点的超时时间 + :param timeout: 查找节点的超时时间(秒) :param ele_only: 是否只获取元素,为False时把文本、注释节点也纳入 :return: 兄弟元素或节点文本组成的列表 """ @@ -297,7 +297,7 @@ class ChromiumElement(DrissionElement): """返回文档中当前元素前面符合条件的元素或节点组成的列表,可用查询语法筛选 查找范围不限同级元素,而是整个DOM文档 :param filter_loc: 用于筛选的查询语法 - :param timeout: 查找节点的超时时间 + :param timeout: 查找节点的超时时间(秒) :param ele_only: 是否只获取元素,为False时把文本、注释节点也纳入 :return: 本元素前面的元素或节点组成的列表 """ @@ -307,7 +307,7 @@ class ChromiumElement(DrissionElement): """返回文档中当前元素后面符合条件的元素或节点组成的列表,可用查询语法筛选 查找范围不限同级元素,而是整个DOM文档 :param filter_loc: 用于筛选的查询语法 - :param timeout: 查找节点的超时时间 + :param timeout: 查找节点的超时时间(秒) :param ele_only: 是否只获取元素,为False时把文本、注释节点也纳入 :return: 本元素后面的元素或节点组成的列表 """ @@ -367,7 +367,7 @@ class ChromiumElement(DrissionElement): :param script: js文本 :param args: 参数,按顺序在js文本中对应arguments[0]、arguments[1]... :param as_expr: 是否作为表达式运行,为True时args无效 - :param timeout: js超时时间,为None则使用页面timeouts.script设置 + :param timeout: js超时时间(秒),为None则使用页面timeouts.script设置 :return: 运行的结果 """ return run_js(self, script, as_expr, self.page.timeouts.script if timeout is None else timeout, args) @@ -377,7 +377,7 @@ class ChromiumElement(DrissionElement): :param script: js文本 :param args: 参数,按顺序在js文本中对应arguments[0]、arguments[1]... :param as_expr: 是否作为表达式运行,为True时args无效 - :param timeout: js超时时间,为None则使用页面timeouts.script设置 + :param timeout: js超时时间(秒),为None则使用页面timeouts.script设置 :return: None """ from threading import Thread @@ -387,7 +387,7 @@ class ChromiumElement(DrissionElement): def ele(self, loc_or_str, timeout=None): """返回当前元素下级符合条件的第一个元素、属性或节点文本 :param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串 - :param timeout: 查找元素超时时间,默认与元素所在页面等待时间一致 + :param timeout: 查找元素超时时间(秒),默认与元素所在页面等待时间一致 :return: ChromiumElement对象或属性、文本 """ return self._ele(loc_or_str, timeout, method='ele()') @@ -395,7 +395,7 @@ class ChromiumElement(DrissionElement): def eles(self, loc_or_str, timeout=None): """返回当前元素下级所有符合条件的子元素、属性或节点文本 :param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串 - :param timeout: 查找元素超时时间,默认与元素所在页面等待时间一致 + :param timeout: 查找元素超时时间(秒),默认与元素所在页面等待时间一致 :return: ChromiumElement对象或属性、文本组成的列表 """ return self._ele(loc_or_str, timeout=timeout, single=False) @@ -429,7 +429,7 @@ class ChromiumElement(DrissionElement): def _find_elements(self, loc_or_str, timeout=None, single=True, relative=False, raise_err=None): """返回当前元素下级符合条件的子元素、属性或节点文本,默认返回第一个 :param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串 - :param timeout: 查找元素超时时间 + :param timeout: 查找元素超时时间(秒) :param single: True则返回第一个,False则返回全部 :param relative: WebPage用的表示是否相对定位的参数 :param raise_err: 找不到元素是是否抛出异常,为None时根据全局设置 @@ -449,7 +449,7 @@ class ChromiumElement(DrissionElement): def get_src(self, timeout=None, base64_to_bytes=True): """返回元素src资源,base64的可转为bytes返回,其它返回str - :param timeout: 等待资源加载的超时时间 + :param timeout: 等待资源加载的超时时间(秒) :param base64_to_bytes: 为True时,如果是base64数据,转换为bytes格式 :return: 资源内容 """ @@ -532,7 +532,7 @@ class ChromiumElement(DrissionElement): """保存图片或其它有src属性的元素的资源 :param path: 文件保存路径,为None时保存到当前文件夹 :param name: 文件名称,为None时从资源url获取 - :param timeout: 等待资源加载的超时时间 + :param timeout: 等待资源加载的超时时间(秒) :return: 返回保存路径 """ data = self.get_src(timeout=timeout) @@ -603,7 +603,6 @@ class ChromiumElement(DrissionElement): if clear and vals not in ('\n', '\ue007'): self.clear(by_js=False) else: - self.wait.has_rect() self._input_focus() input_text_or_keys(self.page, vals) @@ -793,7 +792,7 @@ class ShadowRoot(BaseElement): """在内部查找元素 例:ele2 = ele1('@id=ele_id') :param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串 - :param timeout: 超时时间 + :param timeout: 超时时间(秒) :return: 元素对象或属性、文本 """ return self.ele(loc_or_str, timeout) @@ -825,7 +824,7 @@ class ShadowRoot(BaseElement): :param script: js文本 :param args: 参数,按顺序在js文本中对应arguments[0]、arguments[1]... :param as_expr: 是否作为表达式运行,为True时args无效 - :param timeout: js超时时间,为None则使用页面timeouts.script设置 + :param timeout: js超时时间(秒),为None则使用页面timeouts.script设置 :return: 运行的结果 """ return run_js(self, script, as_expr, self.page.timeouts.script if timeout is None else timeout, args) @@ -835,7 +834,7 @@ class ShadowRoot(BaseElement): :param script: js文本 :param args: 参数,按顺序在js文本中对应arguments[0]、arguments[1]... :param as_expr: 是否作为表达式运行,为True时args无效 - :param timeout: js超时时间,为None则使用页面timeouts.script设置 + :param timeout: js超时时间(秒),为None则使用页面timeouts.script设置 :return: None """ from threading import Thread @@ -986,7 +985,7 @@ class ShadowRoot(BaseElement): def ele(self, loc_or_str, timeout=None): """返回当前元素下级符合条件的第一个元素 :param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串 - :param timeout: 查找元素超时时间,默认与元素所在页面等待时间一致 + :param timeout: 查找元素超时时间(秒),默认与元素所在页面等待时间一致 :return: ChromiumElement对象 """ return self._ele(loc_or_str, timeout, method='ele()') @@ -994,7 +993,7 @@ class ShadowRoot(BaseElement): def eles(self, loc_or_str, timeout=None): """返回当前元素下级所有符合条件的子元素 :param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串 - :param timeout: 查找元素超时时间,默认与元素所在页面等待时间一致 + :param timeout: 查找元素超时时间(秒),默认与元素所在页面等待时间一致 :return: ChromiumElement对象组成的列表 """ return self._ele(loc_or_str, timeout=timeout, single=False) @@ -1020,7 +1019,7 @@ class ShadowRoot(BaseElement): def _find_elements(self, loc_or_str, timeout=None, single=True, relative=False, raise_err=None): """返回当前元素下级符合条件的子元素、属性或节点文本,默认返回第一个 :param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串 - :param timeout: 查找元素超时时间 + :param timeout: 查找元素超时时间(秒) :param single: True则返回第一个,False则返回全部 :param relative: WebPage用的表示是否相对定位的参数 :param raise_err: 找不到元素是是否抛出异常,为None时根据全局设置 @@ -1030,42 +1029,47 @@ class ShadowRoot(BaseElement): if loc[0] == 'css selector' and str(loc[1]).startswith(':root'): loc = loc[0], loc[1][5:] - result = None - timeout = timeout if timeout is not None else self.page.timeout - end_time = perf_counter() + timeout - while perf_counter() <= end_time: + def do_find(): if loc[0] == 'css selector': if single: nod_id = self.page.run_cdp('DOM.querySelector', nodeId=self._node_id, selector=loc[1])['nodeId'] - result = make_chromium_ele(self.page, node_id=nod_id) if nod_id else NoneElement(self.page) + if nod_id: + r = make_chromium_ele(self.page, node_id=nod_id) + return None if r is False else r else: nod_ids = self.page.run_cdp('DOM.querySelectorAll', nodeId=self._node_id, selector=loc[1])['nodeId'] - result = make_chromium_eles(self.page, node_ids=nod_ids, single=False) + r = make_chromium_eles(self.page, node_ids=nod_ids, single=False) + return None if r is False else r else: eles = make_session_ele(self.html).eles(loc) if not eles: - result = NoneElement(self.page) if single else eles - continue + return None css = [i.css_path[61:] for i in eles] if single: node_id = self.page.run_cdp('DOM.querySelector', nodeId=self._node_id, selector=css[0])['nodeId'] - result = make_chromium_ele(self.page, node_id=node_id) if node_id else NoneElement(self.page) - + r = make_chromium_ele(self.page, node_id=node_id) + return None if r is False else r else: - result = [] - for i in css: - node_id = self.page.run_cdp('DOM.querySelector', nodeId=self._node_id, selector=i)['nodeId'] - if node_id: - result.append(make_chromium_ele(self.page, node_id=node_id)) + node_ids = [self.page.run_cdp('DOM.querySelector', nodeId=self._node_id, selector=i)['nodeId'] + for i in css] + if 0 in node_ids: + return None + r = make_chromium_eles(self.page, node_ids=node_ids, single=False) + return None if r is False else r - if result: - break + timeout = timeout if timeout is not None else self.page.timeout + end_time = perf_counter() + timeout + result = do_find() + while result is None and perf_counter() <= end_time: sleep(.1) + result = do_find() - return result + if result: + return result + return NoneElement(self.page) if single else [] def _get_node_id(self, obj_id): """返回元素node id""" @@ -1087,7 +1091,7 @@ def find_in_chromium_ele(ele, loc, single=True, timeout=None, relative=True): :param ele: ChromiumElement对象 :param loc: 元素定位元组 :param single: True则返回第一个,False则返回全部 - :param timeout: 查找元素超时时间 + :param timeout: 查找元素超时时间(秒) :param relative: WebPage用于标记是否相对定位使用 :return: 返回ChromiumElement元素或它们组成的列表 """ @@ -1119,45 +1123,57 @@ def find_by_xpath(ele, xpath, single, timeout, relative=True): :param ele: 在此元素中查找 :param xpath: 查找语句 :param single: 是否只返回第一个结果 - :param timeout: 超时时间 + :param timeout: 超时时间(秒) :param relative: 是否相对定位 :return: ChromiumElement或其组成的列表 """ type_txt = '9' if single else '7' node_txt = 'this.contentDocument' if ele.tag in __FRAME_ELEMENT__ and not relative else 'this' js = make_js_for_find_ele_by_xpath(xpath, type_txt, node_txt) - r = ele.page.run_cdp_loaded('Runtime.callFunctionOn', functionDeclaration=js, objectId=ele._obj_id, - returnByValue=False, awaitPromise=True, userGesture=True) - if r['result']['type'] == 'string': - return r['result']['value'] + ele.page.wait.load_complete() + + def do_find(): + res = ele.page.run_cdp('Runtime.callFunctionOn', functionDeclaration=js, objectId=ele._obj_id, + returnByValue=False, awaitPromise=True, userGesture=True) + if res['result']['type'] == 'string': + return res['result']['value'] + if 'exceptionDetails' in res: + if 'The result is not a node set' in res['result']['description']: + js1 = make_js_for_find_ele_by_xpath(xpath, '1', node_txt) + res = ele.page.run_cdp('Runtime.callFunctionOn', functionDeclaration=js1, objectId=ele._obj_id, + returnByValue=False, awaitPromise=True, userGesture=True) + return res['result']['value'] + else: + raise SyntaxError(f'查询语句错误:\n{res}') + + if res['result']['subtype'] == 'null' or res['result']['description'] in ('NodeList(0)', 'Array(0)'): + return None + + if single: + r = make_chromium_ele(ele.page, obj_id=res['result']['objectId']) + return None if r is False else r - if 'exceptionDetails' in r: - if 'The result is not a node set' in r['result']['description']: - js = make_js_for_find_ele_by_xpath(xpath, '1', node_txt) - r = ele.page.run_cdp_loaded('Runtime.callFunctionOn', functionDeclaration=js, objectId=ele._obj_id, - returnByValue=False, awaitPromise=True, userGesture=True) - return r['result']['value'] else: - raise SyntaxError(f'查询语句错误:\n{r}') + # from pprint import pprint + # for i in ele.page.run_cdp('Runtime.getProperties', + # objectId=res['result']['objectId'], + # ownProperties=True)['result'][:-1]: + # pprint(i) + r = [make_chromium_ele(ele.page, obj_id=i['value']['objectId']) if i['value']['type'] == 'object' else + i['value']['value'] for i in ele.page.run_cdp('Runtime.getProperties', + objectId=res['result']['objectId'], + ownProperties=True)['result'][:-1]] + return None if not r or r is False in r else r end_time = perf_counter() + timeout - while ((r['result']['subtype'] == 'null' or r['result']['description'] in ('NodeList(0)', 'Array(0)')) - and perf_counter() < end_time): - r = ele.page.run_cdp_loaded('Runtime.callFunctionOn', functionDeclaration=js, objectId=ele._obj_id, - returnByValue=False, awaitPromise=True, userGesture=True) + result = do_find() + while result is None and perf_counter() < end_time: + sleep(.1) + result = do_find() - if single: - return NoneElement(ele.page) if r['result']['subtype'] == 'null' \ - else make_chromium_ele(ele.page, obj_id=r['result']['objectId']) - - if r['result']['description'] in ('NodeList(0)', 'Array(0)'): - return [] - else: - r = ele.page.run_cdp_loaded('Runtime.getProperties', objectId=r['result']['objectId'], - ownProperties=True)['result'] - return [make_chromium_ele(ele.page, obj_id=i['value']['objectId']) - if i['value']['type'] == 'object' else i['value']['value'] - for i in r[:-1]] + if result: + return result + return NoneElement(ele.page) if single else [] def find_by_css(ele, selector, single, timeout): @@ -1165,35 +1181,45 @@ def find_by_css(ele, selector, single, timeout): :param ele: 在此元素中查找 :param selector: 查找语句 :param single: 是否只返回第一个结果 - :param timeout: 超时时间 + :param timeout: 超时时间(秒) :return: ChromiumElement或其组成的列表 """ selector = selector.replace('"', r'\"') find_all = '' if single else 'All' node_txt = 'this.contentDocument' if ele.tag in ('iframe', 'frame', 'shadow-root') else 'this' js = f'function(){{return {node_txt}.querySelector{find_all}("{selector}");}}' - r = ele.page.run_cdp_loaded('Runtime.callFunctionOn', functionDeclaration=js, objectId=ele._obj_id, - returnByValue=False, awaitPromise=True, userGesture=True) + + ele.page.wait.load_complete() + + def do_find(): + res = ele.page.run_cdp('Runtime.callFunctionOn', functionDeclaration=js, objectId=ele._obj_id, + returnByValue=False, awaitPromise=True, userGesture=True) + + if 'exceptionDetails' in res: + raise SyntaxError(f'查询语句错误:\n{res}') + if res['result']['subtype'] == 'null' or res['result']['description'] in ('NodeList(0)', 'Array(0)'): + return None + + if single: + r = make_chromium_ele(ele.page, obj_id=res['result']['objectId']) + return None if r is False else r + + else: + node_ids = [i['value']['objectId'] for i in ele.page.run_cdp('Runtime.getProperties', + objectId=res['result']['objectId'], + ownProperties=True)['result'][:-1]] + r = make_chromium_eles(ele.page, obj_ids=node_ids, single=False, ele_only=False) + return None if r is False else r end_time = perf_counter() + timeout - while ('exceptionDetails' in r or r['result']['subtype'] == 'null' or - r['result']['description'] in ('NodeList(0)', 'Array(0)')) and perf_counter() < end_time: - r = ele.page.run_cdp_loaded('Runtime.callFunctionOn', functionDeclaration=js, objectId=ele._obj_id, - returnByValue=False, awaitPromise=True, userGesture=True) + result = do_find() + while result is None and perf_counter() < end_time: + sleep(.1) + result = do_find() - if 'exceptionDetails' in r: - raise SyntaxError(f'查询语句错误:\n{r}') - - if single: - return NoneElement(ele.page) if r['result']['subtype'] == 'null' \ - else make_chromium_ele(ele.page, obj_id=r['result']['objectId']) - - if r['result']['description'] in ('NodeList(0)', 'Array(0)'): - return [] - else: - r = ele.page.run_cdp_loaded('Runtime.getProperties', objectId=r['result']['objectId'], - ownProperties=True)['result'] - return [make_chromium_ele(ele.page, obj_id=i['value']['objectId']) for i in r] + if result: + return result + return NoneElement(ele.page) if single else [] def make_chromium_ele(page, node_id=None, obj_id=None): @@ -1201,18 +1227,24 @@ def make_chromium_ele(page, node_id=None, obj_id=None): :param page: ChromiumPage对象 :param node_id: 元素的node id :param obj_id: 元素的object id - :return: ChromiumElement对象或ChromiumFrame对象 + :return: ChromiumElement对象或ChromiumFrame对象,生成失败返回False """ if node_id: - node = page.run_cdp('DOM.describeNode', nodeId=node_id) + node = page.driver.run('DOM.describeNode', nodeId=node_id) + if 'error' in node: + return False 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'] + if 'error' in obj_id: + return False elif obj_id: - node = page.run_cdp('DOM.describeNode', objectId=obj_id) + node = page.driver.run('DOM.describeNode', objectId=obj_id) + if 'error' in node: + return False if node['node']['nodeName'] in ('#text', '#comment'): # todo: Node() return node['node']['nodeValue'] @@ -1220,12 +1252,12 @@ def make_chromium_ele(page, node_id=None, obj_id=None): node_id = node['node']['nodeId'] else: - raise ElementLostError + return False ele = ChromiumElement(page, obj_id=obj_id, node_id=node_id, backend_id=backend_id) if ele.tag in __FRAME_ELEMENT__: from .._pages.chromium_frame import ChromiumFrame - ele = ChromiumFrame(page, ele) + ele = ChromiumFrame(page, ele, node) return ele @@ -1237,14 +1269,16 @@ def make_chromium_eles(page, node_ids=None, obj_ids=None, single=True, ele_only= :param obj_ids: 元素的object id :param single: 是否获取但个元素 :param ele_only: 是否只要ele - :return: ChromiumElement对象或ChromiumFrame对象 + :return: ChromiumElement对象或ChromiumFrame对象,生成失败返回False """ 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) + node = page.driver.run('DOM.describeNode', nodeId=node_id) + if 'error' in node: + return False if node['node']['nodeName'] in ('#text', '#comment'): if ele_only: continue @@ -1254,11 +1288,14 @@ def make_chromium_eles(page, node_ids=None, obj_ids=None, single=True, ele_only= else: nodes.append(node['node']['nodeValue']) - obj_id = page.run_cdp('DOM.resolveNode', nodeId=node_id)['object']['objectId'] + obj_id = page.driver.run('DOM.resolveNode', nodeId=node_id) + if 'error' in obj_id: + return False + obj_id = obj_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) + ele = ChromiumFrame(page, ele, node) if single: return ele nodes.append(ele) @@ -1267,7 +1304,9 @@ def make_chromium_eles(page, node_ids=None, obj_ids=None, single=True, ele_only= for obj_id in obj_ids: if not obj_id: return False - node = page.run_cdp('DOM.describeNode', objectId=obj_id) + node = page.driver.run('DOM.describeNode', objectId=obj_id) + if 'error' in node: + return False if node['node']['nodeName'] in ('#text', '#comment'): if ele_only: continue @@ -1281,7 +1320,7 @@ def make_chromium_eles(page, node_ids=None, obj_ids=None, single=True, ele_only= backend_id=node['node']['backendNodeId']) if ele.tag in __FRAME_ELEMENT__: from .._pages.chromium_frame import ChromiumFrame - ele = ChromiumFrame(page, ele) + ele = ChromiumFrame(page, ele, node) if single: return ele nodes.append(ele) @@ -1336,7 +1375,7 @@ def run_js(page_or_ele, script, as_expr=False, timeout=None, args=None): :param page_or_ele: 页面对象或元素对象 :param script: js文本 :param as_expr: 是否作为表达式运行,为True时args无效 - :param timeout: 超时时间 + :param timeout: 超时时间(秒) :param args: 参数,按顺序在js文本中对应arguments[0]、arguments[1]... :return: js执行结果 """ @@ -1409,16 +1448,17 @@ def parse_js_result(page, ele, result): elif class_name == 'HTMLDocument': return result else: - return make_chromium_eles(page, obj_ids=(result['objectId'],)) + r = make_chromium_ele(page, obj_id=result['objectId']) + if r is False: + raise ElementLostError + return r elif sub_type == 'array': - r = page.run_cdp('Runtime.getProperties', objectId=result['objectId'], - ownProperties=True)['result'] + r = page.run_cdp('Runtime.getProperties', objectId=result['objectId'], ownProperties=True)['result'] return [parse_js_result(page, ele, result=i['value']) for i in r[:-1]] elif 'objectId' in result and result['className'].lower() == 'object': # dict - r = page.run_cdp('Runtime.getProperties', objectId=result['objectId'], - ownProperties=True)['result'] + r = page.run_cdp('Runtime.getProperties', objectId=result['objectId'], ownProperties=True)['result'] return {i['name']: parse_js_result(page, ele, result=i['value']) for i in r} else: diff --git a/DrissionPage/_elements/chromium_element.pyi b/DrissionPage/_elements/chromium_element.pyi index b381132..44b6c8d 100644 --- a/DrissionPage/_elements/chromium_element.pyi +++ b/DrissionPage/_elements/chromium_element.pyi @@ -99,19 +99,19 @@ class ChromiumElement(DrissionElement): def child(self, filter_loc: Union[tuple, str, int] = '', index: int = 1, - timeout: float = 0, + timeout: float = None, ele_only: bool = True) -> Union[ChromiumElement, str, NoneElement]: ... def prev(self, filter_loc: Union[tuple, str, int] = '', index: int = 1, - timeout: float = 0, + timeout: float = None, ele_only: bool = True) -> Union[ChromiumElement, str, NoneElement]: ... def next(self, filter_loc: Union[tuple, str, int] = '', index: int = 1, - timeout: float = 0, + timeout: float = None, ele_only: bool = True) -> Union[ChromiumElement, str, NoneElement]: ... def before(self, @@ -128,17 +128,17 @@ class ChromiumElement(DrissionElement): def children(self, filter_loc: Union[tuple, str] = '', - timeout: float = 0, + timeout: float = None, ele_only: bool = True) -> List[Union[ChromiumElement, str]]: ... def prevs(self, filter_loc: Union[tuple, str] = '', - timeout: float = 0, + timeout: float = None, ele_only: bool = True) -> List[Union[ChromiumElement, str]]: ... def nexts(self, filter_loc: Union[tuple, str] = '', - timeout: float = 0, + timeout: float = None, ele_only: bool = True) -> List[Union[ChromiumElement, str]]: ... def befores(self, @@ -315,12 +315,12 @@ def find_by_css(ele: ChromiumElement, selector: str, single: bool, timeout: float) -> Union[ChromiumElement, List[ChromiumElement], NoneElement]: ... -def make_chromium_ele(page: Union[ChromiumPage, WebPage, ChromiumTab, ChromiumFrame], +def make_chromium_ele(page: Union[ChromiumBase, ChromiumPage, WebPage, ChromiumTab, ChromiumFrame], node_id: int = ..., obj_id: str = ...) -> Union[ChromiumElement, ChromiumFrame, str]: ... -def make_chromium_eles(page: Union[ChromiumPage, WebPage, ChromiumTab, ChromiumFrame], +def make_chromium_eles(page: Union[ChromiumBase, ChromiumPage, WebPage, ChromiumTab, ChromiumFrame], node_ids: Union[tuple, list] = None, obj_ids: Union[tuple, list] = None, single: bool = True, diff --git a/DrissionPage/_functions/browser.py b/DrissionPage/_functions/browser.py index f5f4cca..33f0df6 100644 --- a/DrissionPage/_functions/browser.py +++ b/DrissionPage/_functions/browser.py @@ -194,7 +194,7 @@ def test_connect(ip, port, timeout=30): """测试浏览器是否可用 :param ip: 浏览器ip :param port: 浏览器端口 - :param timeout: 超时时间 + :param timeout: 超时时间(秒) :return: None """ end_time = perf_counter() + timeout diff --git a/DrissionPage/_functions/keys.py b/DrissionPage/_functions/keys.py index 7e4a300..8c0a243 100644 --- a/DrissionPage/_functions/keys.py +++ b/DrissionPage/_functions/keys.py @@ -419,7 +419,7 @@ def send_key(page, modifier, key): 'unmodifiedText': text, 'location': description['location'], 'isKeypad': description['location'] == 3, - '_ignore': AlertExistsError, '_timeout': 1} + '_ignore': AlertExistsError} page.run_cdp('Input.dispatchKeyEvent', **data) data['type'] = 'keyUp' diff --git a/DrissionPage/_functions/tools.py b/DrissionPage/_functions/tools.py index 6ab1229..4db38b5 100644 --- a/DrissionPage/_functions/tools.py +++ b/DrissionPage/_functions/tools.py @@ -192,7 +192,7 @@ def wait_until(page, condition, timeout=10, poll=0.1, raise_err=True): """等待返回值不为False或空,直到超时 :param page: DrissionPage对象 :param condition: 等待条件,返回值不为False则停止等待 - :param timeout: 超时时间 + :param timeout: 超时时间(秒) :param poll: 轮询间隔 :param raise_err: 是否抛出异常 :return: DP Element or bool @@ -267,7 +267,7 @@ def raise_error(result, ignore=None): 'No node with given id found', 'Node with given id does not belong to the document', 'No node found for given backend id'): r = ElementLostError() - elif error in ('tab closed', 'No target with given id found'): + elif error in ('page closed', 'No target with given id found'): r = PageClosedError() elif error == 'timeout': r = TimeoutError(f'超时。\n错误:{result["error"]}\nmethod:{result["method"]}\nargs:{result["args"]}\n' diff --git a/DrissionPage/_pages/chromium_base.py b/DrissionPage/_pages/chromium_base.py index 12eb386..eb21aee 100644 --- a/DrissionPage/_pages/chromium_base.py +++ b/DrissionPage/_pages/chromium_base.py @@ -38,7 +38,7 @@ class ChromiumBase(BasePage): """ :param address: 浏览器 ip:port :param tab_id: 要控制的标签页id,不指定默认为激活的 - :param timeout: 超时时间 + :param timeout: 超时时间(秒) """ super().__init__() self._is_loading = None @@ -147,7 +147,7 @@ class ChromiumBase(BasePage): def _get_document(self, timeout=10): """获取页面文档 - :param timeout: 超时时间 + :param timeout: 超时时间(秒) :return: 是否获取成功 """ if self._debug: @@ -282,7 +282,7 @@ class ChromiumBase(BasePage): """在内部查找元素 例:ele = page('@id=ele_id') :param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串 - :param timeout: 超时时间 + :param timeout: 超时时间(秒) :return: ChromiumElement对象 """ return self.ele(loc_or_str, timeout) @@ -468,7 +468,7 @@ class ChromiumBase(BasePage): :param script: js文本 :param args: 参数,按顺序在js文本中对应arguments[0]、arguments[1]... :param as_expr: 是否作为表达式运行,为True时args无效 - :param timeout: js超时时间,为None则使用页面timeouts.script设置 + :param timeout: js超时时间(秒),为None则使用页面timeouts.script设置 :return: 运行的结果 """ return run_js(self, script, as_expr, self.timeouts.script if timeout is None else timeout, args) @@ -478,7 +478,7 @@ class ChromiumBase(BasePage): :param script: js文本 :param args: 参数,按顺序在js文本中对应arguments[0]、arguments[1]... :param as_expr: 是否作为表达式运行,为True时args无效 - :param timeout: js超时时间,为None则使用页面timeouts.script设置 + :param timeout: js超时时间(秒),为None则使用页面timeouts.script设置 :return: 运行的结果 """ self.wait.load_complete() @@ -489,7 +489,7 @@ class ChromiumBase(BasePage): :param script: js文本 :param args: 参数,按顺序在js文本中对应arguments[0]、arguments[1]... :param as_expr: 是否作为表达式运行,为True时args无效 - :param timeout: js超时时间,为None则使用页面timeouts.script设置 + :param timeout: js超时时间(秒),为None则使用页面timeouts.script设置 :return: None """ from threading import Thread @@ -502,7 +502,7 @@ class ChromiumBase(BasePage): :param show_errmsg: 是否显示和抛出异常 :param retry: 重试次数 :param interval: 重试间隔(秒) - :param timeout: 连接超时时间 + :param timeout: 连接超时时间(秒) :return: 目标url是否可用 """ retry, interval = self._before_connect(url, retry, interval) @@ -531,7 +531,7 @@ class ChromiumBase(BasePage): def ele(self, loc_or_ele, timeout=None): """获取第一个符合条件的元素对象 :param loc_or_ele: 定位符或元素对象 - :param timeout: 查找超时时间 + :param timeout: 查找超时时间(秒) :return: ChromiumElement对象 """ return self._ele(loc_or_ele, timeout=timeout, method='ele()') @@ -539,7 +539,7 @@ class ChromiumBase(BasePage): def eles(self, loc_or_str, timeout=None): """获取所有符合条件的元素对象 :param loc_or_str: 定位符或元素对象 - :param timeout: 查找超时时间 + :param timeout: 查找超时时间(秒) :return: ChromiumElement对象组成的列表 """ return self._ele(loc_or_str, timeout=timeout, single=False) @@ -568,7 +568,7 @@ class ChromiumBase(BasePage): def _find_elements(self, loc_or_ele, timeout=None, single=True, relative=False, raise_err=None): """执行元素查找 :param loc_or_ele: 定位符或元素对象 - :param timeout: 查找超时时间 + :param timeout: 查找超时时间(秒) :param single: 是否只返回第一个 :param relative: WebPage用的表示是否相对定位的参数 :param raise_err: 找不到元素是是否抛出异常,为None时根据全局设置 @@ -581,56 +581,39 @@ class ChromiumBase(BasePage): else: raise ValueError('loc_or_str参数只能是tuple、str、ChromiumElement类型。') - ok = False - nodeIds = None - + self.wait.load_complete() 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, _timeout=timeout, - includeUserAgentShadowDOM=True) - count = search_result['resultCount'] - search_ids.append(search_result['searchId']) - except ContextLostError: - search_result = None - count = 0 + timeout = .5 if timeout <= 0 else timeout + result = self.driver.run('DOM.performSearch', query=loc, _timeout=timeout, includeUserAgentShadowDOM=True) + if not result or __ERROR__ in result: + num = 0 + else: + num = result['resultCount'] + search_ids.append(result['searchId']) while True: - if count > 0: - count = 1 if single else count - try: - nodeIds = self.run_cdp_loaded('DOM.getSearchResults', searchId=search_result['searchId'], - fromIndex=0, toIndex=count) - if nodeIds['nodeIds'][0] != 0: - ok = True - - except Exception: - pass - - if ok: - r = make_chromium_eles(self, node_ids=nodeIds['nodeIds'], single=single) - if r is not False: - break - else: - ok = False - - try: - timeout = end_time - perf_counter() - if timeout <= 0: - timeout = .5 - search_result = self.run_cdp_loaded('DOM.performSearch', query=loc, _timeout=timeout, - includeUserAgentShadowDOM=True) - count = search_result['resultCount'] - search_ids.append(search_result['searchId']) - except ContextLostError: - pass + if num > 0: + num = 1 if single else num + nIds = self._driver.run('DOM.getSearchResults', searchId=result['searchId'], fromIndex=0, toIndex=num) + if __ERROR__ not in nIds: + if nIds['nodeIds'][0] != 0: + r = make_chromium_eles(self, node_ids=nIds['nodeIds'], single=single) + if r is not False: + break if perf_counter() >= end_time: return NoneElement(self) if single else [] sleep(.1) + timeout = end_time - perf_counter() + timeout = .5 if timeout <= 0 else timeout + result = self.driver.run('DOM.performSearch', query=loc, _timeout=timeout, includeUserAgentShadowDOM=True) + if not result or __ERROR__ not in result: + num = result['resultCount'] + search_ids.append(result['searchId']) for _id in search_ids: self._driver.run('DOM.discardSearchResults', searchId=_id) @@ -712,7 +695,7 @@ class ChromiumBase(BasePage): def get_frame(self, loc_ind_ele, timeout=None): """获取页面中一个frame对象,可传入定位符、iframe序号、ChromiumFrame对象,序号从1开始 :param loc_ind_ele: 定位符、iframe序号、ChromiumFrame对象 - :param timeout: 查找元素超时时间 + :param timeout: 查找元素超时时间(秒) :return: ChromiumFrame对象 """ if isinstance(loc_ind_ele, str): @@ -752,7 +735,7 @@ class ChromiumBase(BasePage): def get_frames(self, loc=None, timeout=None): """获取所有符合条件的frame对象 :param loc: 定位符,为None时返回所有 - :param timeout: 查找超时时间 + :param timeout: 查找超时时间(秒) :return: ChromiumFrame对象组成的列表 """ loc = loc or 'xpath://*[name()="iframe" or name()="frame"]' @@ -839,6 +822,11 @@ class ChromiumBase(BasePage): if cookies: self.run_cdp_loaded('Network.clearBrowserCookies') + def disconnect(self): + """断开与页面的连接,不关闭页面""" + if self._driver: + self.driver.stop() + def handle_alert(self, accept=True, send=None, timeout=None, next_one=False): r = self._handle_alert(accept=accept, send=send, timeout=timeout, next_one=next_one) while self._has_alert: @@ -849,7 +837,7 @@ class ChromiumBase(BasePage): """处理提示框,可以自动等待提示框出现 :param accept: True表示确认,False表示取消,其它值不会按按钮但依然返回文本值 :param send: 处理prompt提示框时可输入文本 - :param timeout: 等待提示框出现的超时时间,为None则使用self.timeout属性的值 + :param timeout: 等待提示框出现的超时时间(秒),为None则使用self.timeout属性的值 :param next_one: 是否处理下一个出现的提示框,为True时timeout参数无效 :return: 提示框内容文本,未等到提示框则返回False """ @@ -901,12 +889,9 @@ class ChromiumBase(BasePage): def _wait_loaded(self, timeout=None): """等待页面加载完成,超时触发停止加载 - :param timeout: 超时时间 + :param timeout: 超时时间(秒) :return: 是否成功,超时返回False """ - if self._load_mode == 'none': - return True - timeout = timeout if timeout is not None else self.timeouts.page_load end_time = perf_counter() + timeout while perf_counter() < end_time: @@ -948,10 +933,11 @@ class ChromiumBase(BasePage): :param times: 重试次数 :param interval: 重试间隔(秒) :param show_errmsg: 是否抛出异常 - :param timeout: 连接超时时间 + :param timeout: 连接超时时间(秒) :return: 是否成功,返回None表示不确定 """ err = None + self._is_loading = True timeout = timeout if timeout is not None else self.timeouts.page_load for t in range(times + 1): err = None diff --git a/DrissionPage/_pages/chromium_base.pyi b/DrissionPage/_pages/chromium_base.pyi index 98c98fb..5781211 100644 --- a/DrissionPage/_pages/chromium_base.pyi +++ b/DrissionPage/_pages/chromium_base.pyi @@ -224,6 +224,8 @@ class ChromiumBase(BasePage): def clear_cache(self, session_storage: bool = True, local_storage: bool = True, cache: bool = True, cookies: bool = True) -> None: ... + def disconnect(self) -> None: ... + def handle_alert(self, accept: bool = True, send: str = None, timeout: float = None, next_one: bool = False) -> Union[str, False]: ... diff --git a/DrissionPage/_pages/chromium_frame.py b/DrissionPage/_pages/chromium_frame.py index 873f331..f2d8820 100644 --- a/DrissionPage/_pages/chromium_frame.py +++ b/DrissionPage/_pages/chromium_frame.py @@ -19,10 +19,11 @@ from ..errors import ContextLostError, ElementLostError, GetDocumentError, PageC class ChromiumFrame(ChromiumBase): - def __init__(self, page, ele): + def __init__(self, page, ele, info=None): """ :param page: frame所在的页面对象 :param ele: frame所在元素 + :param info: frame所在元素信息 """ page_type = str(type(page)) if 'ChromiumPage' in page_type or 'WebPage' in page_type: @@ -41,7 +42,7 @@ class ChromiumFrame(ChromiumBase): self._states = None self._reloading = False - node = page.run_cdp('DOM.describeNode', backendNodeId=ele._backend_id)['node'] + node = info['node'] if not info else page.run_cdp('DOM.describeNode', backendNodeId=ele._backend_id)['node'] self._frame_id = node['frameId'] if self._is_inner_frame(): self._is_diff_domain = False @@ -65,7 +66,7 @@ class ChromiumFrame(ChromiumBase): """在内部查找元素 例:ele2 = ele1('@id=ele_id') :param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串 - :param timeout: 超时时间 + :param timeout: 超时时间(秒) :return: ChromiumElement对象或属性、文本 """ return self.ele(loc_or_str, timeout) @@ -159,7 +160,7 @@ class ChromiumFrame(ChromiumBase): def _get_document(self, timeout=10): """刷新cdp使用的document数据 - :param timeout: 超时时间 + :param timeout: 超时时间(秒) :return: 是否获取成功 """ if self._is_reading: @@ -392,7 +393,7 @@ class ChromiumFrame(ChromiumBase): :param script: js文本 :param args: 参数,按顺序在js文本中对应arguments[0]、arguments[1]... :param as_expr: 是否作为表达式运行,为True时args无效 - :param timeout: js超时时间,为None则使用页面timeouts.script设置 + :param timeout: js超时时间(秒),为None则使用页面timeouts.script设置 :return: 运行的结果 """ if script.startswith('this.scrollIntoView'): @@ -412,7 +413,7 @@ class ChromiumFrame(ChromiumBase): """返回当前元素前面一个符合条件的同级元素,可用查询语法筛选,可指定返回筛选结果的第几个 :param filter_loc: 用于筛选的查询语法 :param index: 前面第几个查询结果,1开始 - :param timeout: 查找节点的超时时间 + :param timeout: 查找节点的超时时间(秒) :param ele_only: 是否只获取元素,为False时把文本、注释节点也纳入 :return: 同级元素或节点 """ @@ -422,7 +423,7 @@ class ChromiumFrame(ChromiumBase): """返回当前元素后面一个符合条件的同级元素,可用查询语法筛选,可指定返回筛选结果的第几个 :param filter_loc: 用于筛选的查询语法 :param index: 后面第几个查询结果,1开始 - :param timeout: 查找节点的超时时间 + :param timeout: 查找节点的超时时间(秒) :param ele_only: 是否只获取元素,为False时把文本、注释节点也纳入 :return: 同级元素或节点 """ @@ -433,7 +434,7 @@ class ChromiumFrame(ChromiumBase): 查找范围不限同级元素,而是整个DOM文档 :param filter_loc: 用于筛选的查询语法 :param index: 前面第几个查询结果,1开始 - :param timeout: 查找节点的超时时间 + :param timeout: 查找节点的超时时间(秒) :param ele_only: 是否只获取元素,为False时把文本、注释节点也纳入 :return: 本元素前面的某个元素或节点 """ @@ -444,7 +445,7 @@ class ChromiumFrame(ChromiumBase): 查找范围不限同级元素,而是整个DOM文档 :param filter_loc: 用于筛选的查询语法 :param index: 后面第几个查询结果,1开始 - :param timeout: 查找节点的超时时间 + :param timeout: 查找节点的超时时间(秒) :param ele_only: 是否只获取元素,为False时把文本、注释节点也纳入 :return: 本元素后面的某个元素或节点 """ @@ -453,7 +454,7 @@ class ChromiumFrame(ChromiumBase): def prevs(self, filter_loc='', timeout=0, ele_only=True): """返回当前元素前面符合条件的同级元素或节点组成的列表,可用查询语法筛选 :param filter_loc: 用于筛选的查询语法 - :param timeout: 查找节点的超时时间 + :param timeout: 查找节点的超时时间(秒) :param ele_only: 是否只获取元素,为False时把文本、注释节点也纳入 :return: 同级元素或节点文本组成的列表 """ @@ -462,7 +463,7 @@ class ChromiumFrame(ChromiumBase): def nexts(self, filter_loc='', timeout=0, ele_only=True): """返回当前元素后面符合条件的同级元素或节点组成的列表,可用查询语法筛选 :param filter_loc: 用于筛选的查询语法 - :param timeout: 查找节点的超时时间 + :param timeout: 查找节点的超时时间(秒) :param ele_only: 是否只获取元素,为False时把文本、注释节点也纳入 :return: 同级元素或节点文本组成的列表 """ diff --git a/DrissionPage/_pages/chromium_frame.pyi b/DrissionPage/_pages/chromium_frame.pyi index 8dc1380..f2cf074 100644 --- a/DrissionPage/_pages/chromium_frame.pyi +++ b/DrissionPage/_pages/chromium_frame.pyi @@ -24,7 +24,8 @@ class ChromiumFrame(ChromiumBase): def __init__(self, page: Union[ChromiumPage, WebPage, ChromiumTab, ChromiumFrame], - ele: ChromiumElement): + ele: ChromiumElement, + info: dict = None): self._page: ChromiumPage = ... self._target_page: ChromiumBase = ... self.tab: ChromiumTab = ... diff --git a/DrissionPage/_pages/chromium_page.py b/DrissionPage/_pages/chromium_page.py index 2c53511..73777de 100644 --- a/DrissionPage/_pages/chromium_page.py +++ b/DrissionPage/_pages/chromium_page.py @@ -25,7 +25,7 @@ class ChromiumPage(ChromiumBase): """ :param addr_or_opts: 浏览器地址:端口、ChromiumOptions对象或端口数字(int) :param tab_id: 要控制的标签页id,不指定默认为激活的 - :param timeout: 超时时间 + :param timeout: 超时时间(秒) """ addr_or_opts = addr_or_opts or addr_driver_opts self._page = self @@ -242,7 +242,7 @@ class ChromiumPage(ChromiumBase): def quit(self, timeout=5, force=True): """关闭浏览器 - :param timeout: 等待浏览器关闭超时时间 + :param timeout: 等待浏览器关闭超时时间(秒) :param force: 关闭超时是否强制终止进程 :return: None """ diff --git a/DrissionPage/_pages/chromium_tab.py b/DrissionPage/_pages/chromium_tab.py index 983ee4a..dacf435 100644 --- a/DrissionPage/_pages/chromium_tab.py +++ b/DrissionPage/_pages/chromium_tab.py @@ -79,7 +79,7 @@ class WebPageTab(SessionPage, ChromiumTab, BasePage): """在内部查找元素 例:ele = page('@id=ele_id') :param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串 - :param timeout: 超时时间 + :param timeout: 超时时间(秒) :return: 子元素对象 """ if self._mode == 'd': @@ -221,7 +221,7 @@ class WebPageTab(SessionPage, ChromiumTab, BasePage): def ele(self, loc_or_ele, timeout=None): """返回第一个符合条件的元素、属性或节点文本 :param loc_or_ele: 元素的定位信息,可以是元素对象,loc元组,或查询字符串 - :param timeout: 查找元素超时时间,默认与页面等待时间一致 + :param timeout: 查找元素超时时间(秒),默认与页面等待时间一致 :return: 元素对象或属性、文本节点文本 """ if self._mode == 's': @@ -232,7 +232,7 @@ class WebPageTab(SessionPage, ChromiumTab, BasePage): def eles(self, loc_or_str, timeout=None): """返回页面中所有符合条件的元素、属性或节点文本 :param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串 - :param timeout: 查找元素超时时间,默认与页面等待时间一致 + :param timeout: 查找元素超时时间(秒),默认与页面等待时间一致 :return: 元素对象或属性、文本组成的列表 """ if self._mode == 's': @@ -345,7 +345,7 @@ class WebPageTab(SessionPage, ChromiumTab, BasePage): def _find_elements(self, loc_or_ele, timeout=None, single=True, relative=False, raise_err=None): """返回页面中符合条件的元素、属性或节点文本,默认返回第一个 :param loc_or_ele: 元素的定位信息,可以是元素对象,loc元组,或查询字符串 - :param timeout: 查找元素超时时间,d模式专用 + :param timeout: 查找元素超时时间(秒),d模式专用 :param single: True则返回第一个,False则返回全部 :param relative: WebPage用的表示是否相对定位的参数 :param raise_err: 找不到元素是是否抛出异常,为None时根据全局设置 diff --git a/DrissionPage/_pages/session_page.py b/DrissionPage/_pages/session_page.py index 9dfcbf5..0170a1c 100644 --- a/DrissionPage/_pages/session_page.py +++ b/DrissionPage/_pages/session_page.py @@ -25,7 +25,7 @@ class SessionPage(BasePage): def __init__(self, session_or_options=None, timeout=None): """ :param session_or_options: Session对象或SessionOptions对象 - :param timeout: 连接超时时间,为None时从ini文件读取 + :param timeout: 连接超时时间(秒),为None时从ini文件读取 """ super(SessionPage, SessionPage).__init__(self) self._headers = None diff --git a/DrissionPage/_pages/web_page.py b/DrissionPage/_pages/web_page.py index a62d30c..22940e2 100644 --- a/DrissionPage/_pages/web_page.py +++ b/DrissionPage/_pages/web_page.py @@ -18,7 +18,7 @@ class WebPage(SessionPage, ChromiumPage, BasePage): def __init__(self, mode='d', timeout=None, chromium_options=None, session_or_options=None, driver_or_options=None): """初始化函数 :param mode: 'd' 或 's',即driver模式和session模式 - :param timeout: 超时时间,d模式时为寻找元素时间,s模式时为连接时间,默认10秒 + :param timeout: 超时时间(秒),d模式时为寻找元素时间,s模式时为连接时间,默认10秒 :param chromium_options: Driver对象,只使用s模式时应传入False :param session_or_options: Session对象或SessionOptions对象,只使用d模式时应传入False """ @@ -40,7 +40,7 @@ class WebPage(SessionPage, ChromiumPage, BasePage): """在内部查找元素 例:ele = page('@id=ele_id') :param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串 - :param timeout: 超时时间 + :param timeout: 超时时间(秒) :return: 子元素对象 """ if self._mode == 'd': @@ -182,7 +182,7 @@ class WebPage(SessionPage, ChromiumPage, BasePage): def ele(self, loc_or_ele, timeout=None): """返回第一个符合条件的元素、属性或节点文本 :param loc_or_ele: 元素的定位信息,可以是元素对象,loc元组,或查询字符串 - :param timeout: 查找元素超时时间,默认与页面等待时间一致 + :param timeout: 查找元素超时时间(秒),默认与页面等待时间一致 :return: 元素对象或属性、文本节点文本 """ if self._mode == 's': @@ -193,7 +193,7 @@ class WebPage(SessionPage, ChromiumPage, BasePage): def eles(self, loc_or_str, timeout=None): """返回页面中所有符合条件的元素、属性或节点文本 :param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串 - :param timeout: 查找元素超时时间,默认与页面等待时间一致 + :param timeout: 查找元素超时时间(秒),默认与页面等待时间一致 :return: 元素对象或属性、文本组成的列表 """ if self._mode == 's': diff --git a/DrissionPage/_units/actions.py b/DrissionPage/_units/actions.py index 4f4b282..d8a90c3 100644 --- a/DrissionPage/_units/actions.py +++ b/DrissionPage/_units/actions.py @@ -252,7 +252,6 @@ class Actions: return self data = self._get_key_data(key, 'keyDown') - data['_timeout'] = 1 data['_ignore'] = AlertExistsError self.page.run_cdp('Input.dispatchKeyEvent', **data) return self @@ -268,7 +267,6 @@ class Actions: return self data = self._get_key_data(key, 'keyUp') - data['_timeout'] = 1 data['_ignore'] = AlertExistsError self.page.run_cdp('Input.dispatchKeyEvent', **data) return self diff --git a/DrissionPage/_units/clicker.py b/DrissionPage/_units/clicker.py index 9a85215..6565e50 100644 --- a/DrissionPage/_units/clicker.py +++ b/DrissionPage/_units/clicker.py @@ -21,7 +21,7 @@ class Clicker(object): """点击元素 如果遇到遮挡,可选择是否用js点击 :param by_js: 是否用js点击,为None时先用模拟点击,遇到遮挡改用js,为True时直接用js点击,为False时只用模拟点击 - :param timeout: 模拟点击的超时时间,等待元素可见、可用、进入视口 + :param timeout: 模拟点击的超时时间(秒),等待元素可见、可用、进入视口 :param wait_stop: 是否等待元素运动结束再执行点击 :return: 是否点击成功 """ @@ -30,7 +30,7 @@ class Clicker(object): def left(self, by_js=False, timeout=1.5, wait_stop=True): """点击元素,可选择是否用js点击 :param by_js: 是否用js点击,为None时先用模拟点击,遇到遮挡改用js,为True时直接用js点击,为False时只用模拟点击 - :param timeout: 模拟点击的超时时间,等待元素可见、可用、进入视口 + :param timeout: 模拟点击的超时时间(秒),等待元素可见、可用、进入视口 :param wait_stop: 是否等待元素运动结束再执行点击 :return: 是否点击成功 """