From 83ea129d8c59dcf687a0eb593f55208eec713ffb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E5=8D=8E=E5=87=AF?= <13959713+haiyang0726@user.noreply.gitee.com> Date: Fri, 12 Jan 2024 01:46:55 +0000 Subject: [PATCH 01/21] =?UTF-8?q?=E5=A2=9E=E5=8A=A0ele.tree()=20=E6=96=B9?= =?UTF-8?q?=E6=B3=95=EF=BC=8C=E7=94=A8=E9=80=94=EF=BC=9A=E6=89=93=E5=8D=B0?= =?UTF-8?q?=E5=BD=93=E5=89=8D=E5=85=83=E7=B4=A0=E7=9A=84=E5=AD=90=E5=85=83?= =?UTF-8?q?=E7=B4=A0=E7=BB=93=E6=9E=84=E6=A0=91=EF=BC=8C=E9=BB=98=E8=AE=A4?= =?UTF-8?q?=E5=B1=95=E5=BC=80=E5=B1=82=E6=95=B0=E6=98=AF5=E5=B1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 刘华凯 <13959713+haiyang0726@user.noreply.gitee.com> --- DrissionPage/_elements/chromium_element.py | 25 ++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/DrissionPage/_elements/chromium_element.py b/DrissionPage/_elements/chromium_element.py index 642121d..1eb34e9 100644 --- a/DrissionPage/_elements/chromium_element.py +++ b/DrissionPage/_elements/chromium_element.py @@ -9,6 +9,7 @@ from os.path import basename, sep from pathlib import Path from re import search from time import perf_counter, sleep +from colorama import Fore, init from DataRecorder.tools import get_usable_path @@ -122,6 +123,30 @@ class ChromiumElement(DrissionElement): def text(self): """返回元素内所有文本,文本已格式化""" return get_ele_txt(make_session_ele(self.html)) + + def tree(self): + """打印当前元素的子元素结构树,默认展开层数是5层""" + init() + self.__tree(ele=self) + + def __tree(self,ele, layer=5, last_one=False, body=''): + list_ele = ele.children(timeout=0.1) + length = len(list_ele) + body_unit = ' ' if last_one else '│ ' + tail = '├───' + new_body = body + body_unit + + if length > 0 and layer >= 1: + new_last_one = False + for i in range(length): + if i == length - 1: + tail = '└───' + new_last_one = True + e = list_ele[i] + + print(f'{Fore.BLUE}{new_body}{tail}{Fore.CYAN}{i}<{e.tag}> {Fore.RESET}{e.attrs}') + + self.__tree(e, layer - 1, new_last_one, new_body) @property def raw_text(self): From 82f9174176fd95bce76f9e1c6166781c86e28d74 Mon Sep 17 00:00:00 2001 From: haiyang <13959713+haiyang0726@user.noreply.gitee.com> Date: Fri, 12 Jan 2024 03:45:58 +0000 Subject: [PATCH 02/21] =?UTF-8?q?=E5=8A=A0=E4=B8=8Atry=E8=AF=AD=E6=B3=95?= =?UTF-8?q?=EF=BC=8C=E5=A2=9E=E5=8A=A0tree()=20=E6=96=B9=E6=B3=95=E7=9A=84?= =?UTF-8?q?=E5=81=A5=E5=A3=AE=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: haiyang <13959713+haiyang0726@user.noreply.gitee.com> --- DrissionPage/_elements/chromium_element.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/DrissionPage/_elements/chromium_element.py b/DrissionPage/_elements/chromium_element.py index 1eb34e9..9f54e66 100644 --- a/DrissionPage/_elements/chromium_element.py +++ b/DrissionPage/_elements/chromium_element.py @@ -130,7 +130,10 @@ class ChromiumElement(DrissionElement): self.__tree(ele=self) def __tree(self,ele, layer=5, last_one=False, body=''): - list_ele = ele.children(timeout=0.1) + try: + list_ele = ele.children(timeout=0.1) + except: + list_ele = [] length = len(list_ele) body_unit = ' ' if last_one else '│ ' tail = '├───' From cbec20fc97658ad42b354cb4be6e2f88346de069 Mon Sep 17 00:00:00 2001 From: g1879 Date: Tue, 30 Jan 2024 17:05:02 +0800 Subject: [PATCH 03/21] =?UTF-8?q?=E6=88=AA=E5=9B=BE=E5=B7=A6=E4=B8=8A?= =?UTF-8?q?=E5=92=8C=E5=8F=B3=E4=B8=8B=E5=8F=82=E6=95=B0=E5=8F=AF=E5=8F=AA?= =?UTF-8?q?=E6=8E=A5=E6=94=B6=E5=85=B6=E4=B8=AD=E4=B8=80=E4=B8=AA=EF=BC=9B?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E5=AF=B9=E8=B1=A1save()=E5=8F=AF=E7=94=9F?= =?UTF-8?q?=E6=88=90=E4=B8=8D=E5=AD=98=E5=9C=A8=E7=9A=84=E8=B7=AF=E5=BE=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DrissionPage/_configs/chromium_options.py | 4 ++-- DrissionPage/_configs/options_manage.py | 3 +++ DrissionPage/_configs/options_manage.pyi | 4 ++-- DrissionPage/_configs/session_options.py | 4 ++-- DrissionPage/_pages/chromium_base.py | 6 +++++- 5 files changed, 14 insertions(+), 7 deletions(-) diff --git a/DrissionPage/_configs/chromium_options.py b/DrissionPage/_configs/chromium_options.py index d483967..0a5a9e7 100644 --- a/DrissionPage/_configs/chromium_options.py +++ b/DrissionPage/_configs/chromium_options.py @@ -532,9 +532,9 @@ class ChromiumOptions(object): path = path / 'config.ini' if path.is_dir() else path if path.exists(): - om = OptionsManager(str(path)) + om = OptionsManager(path) else: - om = OptionsManager(self.ini_path or str(Path(__file__).parent / 'configs.ini')) + om = OptionsManager(self.ini_path or (Path(__file__).parent / 'configs.ini')) # 设置chromium_options attrs = ('address', 'browser_path', 'arguments', 'extensions', 'user', 'load_mode', diff --git a/DrissionPage/_configs/options_manage.py b/DrissionPage/_configs/options_manage.py index e6867e3..f9c85f4 100644 --- a/DrissionPage/_configs/options_manage.py +++ b/DrissionPage/_configs/options_manage.py @@ -29,6 +29,8 @@ class OptionsManager(object): self.ini_path = default_configs elif path == 'default': self.ini_path = default_configs + elif isinstance(path, Path): + self.ini_path = path else: self.ini_path = Path(path) @@ -147,6 +149,7 @@ class OptionsManager(object): path = Path(path).absolute() path = path / 'config.ini' if path.is_dir() else path + path.parent.mkdir(exist_ok=True, parents=True) path = str(path) self._conf.write(open(path, 'w', encoding='utf-8')) diff --git a/DrissionPage/_configs/options_manage.pyi b/DrissionPage/_configs/options_manage.pyi index 80e438d..f2b0483 100644 --- a/DrissionPage/_configs/options_manage.pyi +++ b/DrissionPage/_configs/options_manage.pyi @@ -7,7 +7,7 @@ """ from configparser import RawConfigParser from pathlib import Path -from typing import Any, Optional +from typing import Any, Optional, Union class OptionsManager(object): @@ -15,7 +15,7 @@ class OptionsManager(object): file_exists: bool = ... _conf: RawConfigParser = ... - def __init__(self, path: str = None): ... + def __init__(self, path: Union[Path, str] = None): ... def __getattr__(self, item) -> dict: ... diff --git a/DrissionPage/_configs/session_options.py b/DrissionPage/_configs/session_options.py index 6373441..a3318ed 100644 --- a/DrissionPage/_configs/session_options.py +++ b/DrissionPage/_configs/session_options.py @@ -377,9 +377,9 @@ class SessionOptions(object): path = path / 'config.ini' if path.is_dir() else path if path.exists(): - om = OptionsManager(str(path)) + om = OptionsManager(path) else: - om = OptionsManager(self.ini_path or str(Path(__file__).parent / 'configs.ini')) + om = OptionsManager(self.ini_path or (Path(__file__).parent / 'configs.ini')) options = session_options_to_dict(self) diff --git a/DrissionPage/_pages/chromium_base.py b/DrissionPage/_pages/chromium_base.py index 92cb9a7..45225f1 100644 --- a/DrissionPage/_pages/chromium_base.py +++ b/DrissionPage/_pages/chromium_base.py @@ -1064,7 +1064,11 @@ class ChromiumBase(BasePage): vp = {'x': 0, 'y': 0, 'width': width, 'height': height, 'scale': 1} args = {'format': pic_type, 'captureBeyondViewport': True, 'clip': vp} else: - if left_top and right_bottom: + if left_top or right_bottom: + if not left_top: + left_top = (0, 0) + if not right_bottom: + right_bottom = self.rect.size x, y = left_top w = right_bottom[0] - x h = right_bottom[1] - y From a3855aba59459b8d6103f2d8ab1e8eb51a97f1cf Mon Sep 17 00:00:00 2001 From: g1879 Date: Wed, 31 Jan 2024 22:24:57 +0800 Subject: [PATCH 04/21] =?UTF-8?q?4.0.4.4=E4=BF=AE=E5=A4=8Dhandle=5Falert()?= =?UTF-8?q?=E5=92=8Ccli=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/_pages/chromium_base.py | 11 +++++++++++ DrissionPage/_pages/chromium_base.pyi | 2 +- setup.py | 2 +- 4 files changed, 14 insertions(+), 3 deletions(-) diff --git a/DrissionPage/__init__.py b/DrissionPage/__init__.py index c5d6e7a..92ba8b8 100644 --- a/DrissionPage/__init__.py +++ b/DrissionPage/__init__.py @@ -14,4 +14,4 @@ from ._configs.chromium_options import ChromiumOptions from ._configs.session_options import SessionOptions __all__ = ['ChromiumPage', 'ChromiumOptions', 'SessionOptions', 'SessionPage', 'WebPage', '__version__'] -__version__ = '4.0.4.3' +__version__ = '4.0.4.4' diff --git a/DrissionPage/_pages/chromium_base.py b/DrissionPage/_pages/chromium_base.py index 45225f1..c054c6a 100644 --- a/DrissionPage/_pages/chromium_base.py +++ b/DrissionPage/_pages/chromium_base.py @@ -878,7 +878,16 @@ class ChromiumBase(BasePage): self._driver = self.browser._get_driver(t_id, self) def handle_alert(self, accept=True, send=None, timeout=None, next_one=False): + """处理提示框,可以自动等待提示框出现 + :param accept: True表示确认,False表示取消,为None不会按按钮但依然返回文本值 + :param send: 处理prompt提示框时可输入文本 + :param timeout: 等待提示框出现的超时时间(秒),为None则使用self.timeout属性的值 + :param next_one: 是否处理下一个出现的提示框,为True时timeout参数无效 + :return: 提示框内容文本,未等到提示框则返回False + """ r = self._handle_alert(accept=accept, send=send, timeout=timeout, next_one=next_one) + if not isinstance(accept, bool): + return r while self._has_alert: sleep(.1) return r @@ -905,6 +914,8 @@ class ChromiumBase(BasePage): return False res_text = self._alert.text + if not isinstance(accept, bool): + return res_text d = {'accept': accept, '_timeout': 0} if self._alert.type == 'prompt' and send is not None: d['promptText'] = send diff --git a/DrissionPage/_pages/chromium_base.pyi b/DrissionPage/_pages/chromium_base.pyi index d5e6e5e..5f64082 100644 --- a/DrissionPage/_pages/chromium_base.pyi +++ b/DrissionPage/_pages/chromium_base.pyi @@ -255,7 +255,7 @@ class ChromiumBase(BasePage): def reconnect(self, wait: float = 0) -> None: ... - def handle_alert(self, accept: bool = True, send: str = None, timeout: float = None, + def handle_alert(self, accept: Optional[bool] = True, send: str = None, timeout: float = None, next_one: bool = False) -> Union[str, False]: ... def _handle_alert(self, accept: bool = True, send: str = None, timeout: float = None, diff --git a/setup.py b/setup.py index bef1de3..2ad5dcb 100644 --- a/setup.py +++ b/setup.py @@ -38,7 +38,7 @@ setup( python_requires='>=3.6', entry_points={ 'console_scripts': [ - 'dp = DrissionPage.functions.cli:main', + 'dp = DrissionPage._functions.cli:main', ], }, ) From 03378136c4ce1be8c342058ac7dc7431bc1b1c09 Mon Sep 17 00:00:00 2001 From: g1879 Date: Sun, 4 Feb 2024 14:18:56 +0800 Subject: [PATCH 05/21] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=B8=80=E4=B8=AAjs?= =?UTF-8?q?=E7=BB=93=E6=9E=9C=E8=A7=A3=E6=9E=90=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DrissionPage/_elements/chromium_element.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/DrissionPage/_elements/chromium_element.py b/DrissionPage/_elements/chromium_element.py index 7442ace..5f6c62a 100644 --- a/DrissionPage/_elements/chromium_element.py +++ b/DrissionPage/_elements/chromium_element.py @@ -1447,7 +1447,10 @@ def run_js(page_or_ele, script, as_expr, timeout, args=None): try: return parse_js_result(page, page_or_ele, res.get('result'), end_time) except Exception: - return res + from DrissionPage import __version__ + raise RuntimeError(f'\njs结果解析错误\n内容:{res.get("result")}\n版本:{__version__}\n' + f'出现这个错误可能意味着程序有bug,请把错误信息和重现方法告知作者,谢谢。\n' + f'报告网站:https://gitee.com/g1879/DrissionPage/issues') def parse_js_result(page, ele, result, end_time): @@ -1475,11 +1478,7 @@ def parse_js_result(page, ele, result, end_time): elif sub_type == 'array': r = page.run_cdp('Runtime.getProperties', objectId=result['objectId'], ownProperties=True)['result'] - return [parse_js_result(page, ele, result=i['value'], end_time=end_time) 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'] - return {i['name']: parse_js_result(page, ele, result=i['value'], end_time=end_time) for i in r} + return [parse_js_result(page, ele, result=i['value'], end_time=end_time) for i in r if i['name'].isdigit()] elif 'objectId' in result: timeout = end_time - perf_counter() From 043b33f16ecf72ee8db69b4266f7ed83ef765fee Mon Sep 17 00:00:00 2001 From: g1879 Date: Sun, 4 Feb 2024 16:41:10 +0800 Subject: [PATCH 06/21] =?UTF-8?q?=E5=A2=9E=E5=8A=A0click.to=5Fdownload()?= =?UTF-8?q?=EF=BC=9B=E4=BF=AE=E5=A4=8Dbody=E6=B3=A8=E9=87=8A=E6=8A=A5?= =?UTF-8?q?=E9=94=99=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DrissionPage/_units/clicker.py | 21 +++++++++++++++++++++ DrissionPage/_units/clicker.pyi | 20 ++++++++++++++++---- DrissionPage/_units/listener.pyi | 8 ++++---- 3 files changed, 41 insertions(+), 8 deletions(-) diff --git a/DrissionPage/_units/clicker.py b/DrissionPage/_units/clicker.py index 93be96e..3be5e33 100644 --- a/DrissionPage/_units/clicker.py +++ b/DrissionPage/_units/clicker.py @@ -140,6 +140,27 @@ class Clicker(object): """ self.at(count=times) + def to_download(self, save_path=None, rename=None, suffix=None, new_tab=False): + """点击触发下载 + :param save_path: 保存路径,为None保存在原来设置的,如未设置保存到当前路径 + :param rename: 重命名文件名 + :param suffix: 指定文件后缀 + :param new_tab: 是否在新tab触发下载 + :return: DownloadMission对象 + """ + if save_path: + self._ele.page.set.download_path(save_path) + elif not self._ele.page._page._browser._dl_mgr._running: + self._ele.page.set.download_path('.') + + if rename or suffix: + self._ele.page.set.download_file_name(rename, suffix) + + tab = self._ele.page._page if new_tab else self._ele.page + + self._ele.click() + return tab.wait.download_begin() + def _click(self, client_x, client_y, button='left', count=1): """实施点击 :param client_x: 视口中的x坐标 diff --git a/DrissionPage/_units/clicker.pyi b/DrissionPage/_units/clicker.pyi index 5eae4a1..3a26baf 100644 --- a/DrissionPage/_units/clicker.pyi +++ b/DrissionPage/_units/clicker.pyi @@ -5,8 +5,10 @@ @Copyright: (c) 2024 by g1879, Inc. All Rights Reserved. @License : BSD 3-Clause. """ -from typing import Optional +from pathlib import Path +from typing import Optional, Union +from .downloader import DownloadMission from .._elements.chromium_element import ChromiumElement @@ -14,16 +16,26 @@ class Clicker(object): def __init__(self, ele: ChromiumElement): self._ele: ChromiumElement = ... - def __call__(self, by_js: Optional[bool] = False, timeout: float = 1.5, wait_stop: bool = True) -> bool: ... + def __call__(self, by_js: Optional[bool, str] = False, timeout: float = 1.5, wait_stop: bool = True) -> bool: ... - def left(self, by_js: Optional[bool] = False, timeout: float = 1.5, wait_stop: bool = True) -> bool: ... + def left(self, by_js: Optional[bool, str] = False, timeout: float = 1.5, wait_stop: bool = True) -> bool: ... def right(self) -> None: ... def middle(self) -> None: ... - def at(self, offset_x: float = None, offset_y: float = None, button: str = 'left', count: int = 1) -> None: ... + def at(self, + offset_x: float = None, + offset_y: float = None, + button: str = 'left', + count: int = 1) -> None: ... def multi(self, times: int = 2) -> None: ... + def to_download(self, + save_path: Union[str, Path] = None, + rename: str = None, + suffix: str = None, + new_tab: bool = False) -> DownloadMission: ... + def _click(self, client_x: float, client_y: float, button: str = 'left', count: int = 1) -> None: ... diff --git a/DrissionPage/_units/listener.pyi b/DrissionPage/_units/listener.pyi index a80d3c2..fd3bd54 100644 --- a/DrissionPage/_units/listener.pyi +++ b/DrissionPage/_units/listener.pyi @@ -23,7 +23,7 @@ class Listener(object): self._page: ChromiumBase = ... self._address: str = ... self._target_id: str = ... - self._targets: Union[str, dict] = ... + self._targets: Optional[str, dict] = ... self._method: set = ... self._res_type: set = ... self._caught: Queue = ... @@ -172,7 +172,7 @@ class Request(object): def headers(self) -> dict: ... @property - def postData(self) -> Union[str, dict]: ... + def postData(self) -> Optional[str, dict]: ... @property def extra_info(self) -> Optional[RequestExtraInfo]: ... @@ -208,7 +208,7 @@ class Response(object): self._response: dict = ... self._raw_body: str = ... self._is_base64_body: bool = ... - self._body: Union[str, dict] = ... + self._body: Optional[str, dict] = ... self._headers: dict = ... @property @@ -221,7 +221,7 @@ class Response(object): def raw_body(self) -> str: ... @property - def body(self) -> Union[str, dict]: ... + def body(self) -> Optional[str, dict]: ... class ExtraInfo(object): From 001c479e4ce93b9a08bb3eb586400fd613be4469 Mon Sep 17 00:00:00 2001 From: g1879 Date: Sun, 4 Feb 2024 17:51:36 +0800 Subject: [PATCH 07/21] =?UTF-8?q?=E4=BC=98=E5=8C=96=E7=B1=BB=E5=9E=8B?= =?UTF-8?q?=E6=B3=A8=E8=A7=A3=EF=BC=9B=E5=8A=A0=E5=85=A5tree()=EF=BC=8C?= =?UTF-8?q?=E6=9C=AA=E5=AE=8C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DrissionPage/__init__.py | 2 +- DrissionPage/_elements/chromium_element.py | 28 ---------------------- DrissionPage/_functions/web.py | 27 +++++++++++++++++++-- DrissionPage/_functions/web.pyi | 3 +++ DrissionPage/_pages/chromium_base.pyi | 4 ++-- DrissionPage/_units/clicker.pyi | 6 ++--- DrissionPage/_units/downloader.pyi | 6 ++--- DrissionPage/_units/listener.pyi | 22 ++++++++--------- DrissionPage/_units/setter.pyi | 6 ++--- 9 files changed, 51 insertions(+), 53 deletions(-) diff --git a/DrissionPage/__init__.py b/DrissionPage/__init__.py index 92ba8b8..e7678ae 100644 --- a/DrissionPage/__init__.py +++ b/DrissionPage/__init__.py @@ -14,4 +14,4 @@ from ._configs.chromium_options import ChromiumOptions from ._configs.session_options import SessionOptions __all__ = ['ChromiumPage', 'ChromiumOptions', 'SessionOptions', 'SessionPage', 'WebPage', '__version__'] -__version__ = '4.0.4.4' +__version__ = '4.0.4.5' diff --git a/DrissionPage/_elements/chromium_element.py b/DrissionPage/_elements/chromium_element.py index 628e846..5f6c62a 100644 --- a/DrissionPage/_elements/chromium_element.py +++ b/DrissionPage/_elements/chromium_element.py @@ -10,7 +10,6 @@ from os.path import basename, sep from pathlib import Path from re import search from time import perf_counter, sleep -from colorama import Fore, init from DataRecorder.tools import get_usable_path @@ -124,33 +123,6 @@ class ChromiumElement(DrissionElement): def text(self): """返回元素内所有文本,文本已格式化""" return get_ele_txt(make_session_ele(self.html)) - - def tree(self): - """打印当前元素的子元素结构树,默认展开层数是5层""" - init() - self.__tree(ele=self) - - def __tree(self,ele, layer=5, last_one=False, body=''): - try: - list_ele = ele.children(timeout=0.1) - except: - list_ele = [] - length = len(list_ele) - body_unit = ' ' if last_one else '│ ' - tail = '├───' - new_body = body + body_unit - - if length > 0 and layer >= 1: - new_last_one = False - for i in range(length): - if i == length - 1: - tail = '└───' - new_last_one = True - e = list_ele[i] - - print(f'{Fore.BLUE}{new_body}{tail}{Fore.CYAN}{i}<{e.tag}> {Fore.RESET}{e.attrs}') - - self.__tree(e, layer - 1, new_last_one, new_body) @property def raw_text(self): diff --git a/DrissionPage/_functions/web.py b/DrissionPage/_functions/web.py index fcc5baa..30387bf 100644 --- a/DrissionPage/_functions/web.py +++ b/DrissionPage/_functions/web.py @@ -319,12 +319,12 @@ def is_cookie_in_driver(page, cookie): :return: bool """ if 'domain' in cookie: - for c in page.get_cookies(all_domains=True): + for c in page.cookies(all_domains=True): if cookie['name'] == c['name'] and cookie['value'] == c['value'] and cookie['domain'] == c.get('domain', None): return True else: - for c in page.get_cookies(all_domains=True): + for c in page.cookies(all_domains=True): if cookie['name'] == c['name'] and cookie['value'] == c['value']: return True return False @@ -363,3 +363,26 @@ def get_blob(page, url, as_bytes=True): return b64decode(result.split(',', 1)[-1]) else: return result + + +def tree(ele_or_page, layer=5, last_one=False, body=''): + try: + list_ele = ele_or_page.s_ele().children(timeout=0.1) + except: + list_ele = [] + length = len(list_ele) + body_unit = ' ' if last_one else '│ ' + tail = '├───' + new_body = body + body_unit + + if length > 0 and layer >= 1: + new_last_one = False + for i in range(length): + if i == length - 1: + tail = '└───' + new_last_one = True + e = list_ele[i] + + print(f'{new_body}{tail}{i}<{e.tag}> {e.attrs}') + + tree(e, layer - 1, new_last_one, new_body) \ No newline at end of file diff --git a/DrissionPage/_functions/web.pyi b/DrissionPage/_functions/web.pyi index 7b5daef..c592613 100644 --- a/DrissionPage/_functions/web.pyi +++ b/DrissionPage/_functions/web.pyi @@ -50,3 +50,6 @@ def is_cookie_in_driver(page: ChromiumBase, cookie: dict) -> bool: ... def get_blob(page: ChromiumBase, url: str, as_bytes: bool = True) -> bytes: ... + + +def tree(ele_or_page, layer=5, last_one=False, body='') -> None: ... diff --git a/DrissionPage/_pages/chromium_base.pyi b/DrissionPage/_pages/chromium_base.pyi index 5f64082..3ff2ab1 100644 --- a/DrissionPage/_pages/chromium_base.pyi +++ b/DrissionPage/_pages/chromium_base.pyi @@ -216,8 +216,8 @@ class ChromiumBase(BasePage): def add_ele(self, outerHTML: str, - insert_to: Optional[ChromiumElement, str, Tuple[str, str]] = None, - before: Optional[ChromiumElement, str, Tuple[str, str]] = None) -> ChromiumElement: ... + insert_to: Union[ChromiumElement, str, Tuple[str, str], None] = None, + before: Union[ChromiumElement, str, Tuple[str, str], None] = None) -> ChromiumElement: ... def get_frame(self, loc_ind_ele: Union[str, int, tuple, ChromiumFrame], timeout: float = None) -> ChromiumFrame: ... diff --git a/DrissionPage/_units/clicker.pyi b/DrissionPage/_units/clicker.pyi index 3a26baf..73cc689 100644 --- a/DrissionPage/_units/clicker.pyi +++ b/DrissionPage/_units/clicker.pyi @@ -6,7 +6,7 @@ @License : BSD 3-Clause. """ from pathlib import Path -from typing import Optional, Union +from typing import Union from .downloader import DownloadMission from .._elements.chromium_element import ChromiumElement @@ -16,9 +16,9 @@ class Clicker(object): def __init__(self, ele: ChromiumElement): self._ele: ChromiumElement = ... - def __call__(self, by_js: Optional[bool, str] = False, timeout: float = 1.5, wait_stop: bool = True) -> bool: ... + def __call__(self, by_js: Union[bool, str, None] = False, timeout: float = 1.5, wait_stop: bool = True) -> bool: ... - def left(self, by_js: Optional[bool, str] = False, timeout: float = 1.5, wait_stop: bool = True) -> bool: ... + def left(self, by_js: Union[bool, str, None] = False, timeout: float = 1.5, wait_stop: bool = True) -> bool: ... def right(self) -> None: ... diff --git a/DrissionPage/_units/downloader.pyi b/DrissionPage/_units/downloader.pyi index d4570a4..45537f1 100644 --- a/DrissionPage/_units/downloader.pyi +++ b/DrissionPage/_units/downloader.pyi @@ -32,9 +32,9 @@ class DownloadManager(object): def set_file_exists(self, tab_id: str, mode: Literal['rename', 'skip', 'overwrite']) -> None: ... - def set_flag(self, tab_id: str, flag: Optional[bool, DownloadMission]) -> None: ... + def set_flag(self, tab_id: str, flag: Union[bool, DownloadMission, None]) -> None: ... - def get_flag(self, tab_id: str) -> Optional[bool, DownloadMission]: ... + def get_flag(self, tab_id: str) -> Union[bool, DownloadMission, None]: ... def get_tab_missions(self, tab_id: str) -> list: ... @@ -54,7 +54,7 @@ class DownloadManager(object): class TabDownloadSettings(object): TABS: dict = ... tab_id: str = ... - waiting_flag: Optional[bool, dict] = ... + waiting_flag: Union[bool, dict, None] = ... rename: Optional[str] = ... suffix: Optional[str] = ... path: Optional[str] = ... diff --git a/DrissionPage/_units/listener.pyi b/DrissionPage/_units/listener.pyi index fd3bd54..a8fc984 100644 --- a/DrissionPage/_units/listener.pyi +++ b/DrissionPage/_units/listener.pyi @@ -6,7 +6,7 @@ @License : BSD 3-Clause. """ from queue import Queue -from typing import Union, Dict, List, Iterable, Optional, Literal +from typing import Union, Dict, List, Iterable, Optional, Literal, Any from requests.structures import CaseInsensitiveDict @@ -23,7 +23,7 @@ class Listener(object): self._page: ChromiumBase = ... self._address: str = ... self._target_id: str = ... - self._targets: Optional[str, dict] = ... + self._targets: Union[str, dict, None] = ... self._method: set = ... self._res_type: set = ... self._caught: Queue = ... @@ -39,16 +39,16 @@ class Listener(object): def targets(self) -> Optional[set]: ... def set_targets(self, - targets: Optional[str, list, tuple, set, bool] = True, + targets: Union[str, list, tuple, set, bool, None] = True, is_regex: Optional[bool] = False, - method: Optional[str, list, tuple, set, bool] = ('GET', 'POST'), - res_type: Optional[__RES_TYPE__, list, tuple, set, bool] = True) -> None: ... + method: Union[str, list, tuple, set, bool, None] = ('GET', 'POST'), + res_type: Union[__RES_TYPE__, list, tuple, set, bool, None] = True) -> None: ... def start(self, - targets: Optional[str, list, tuple, set, bool] = None, + targets: Union[str, list, tuple, set, bool, None] = None, is_regex: Optional[bool] = None, - method: Optional[str, list, tuple, set, bool] = None, - res_type: Optional[__RES_TYPE__, list, tuple, set, bool] = None) -> None: ... + method: Union[str, list, tuple, set, bool, None] = None, + res_type: Union[__RES_TYPE__, list, tuple, set, bool, None] = None) -> None: ... def stop(self) -> None: ... @@ -172,7 +172,7 @@ class Request(object): def headers(self) -> dict: ... @property - def postData(self) -> Optional[str, dict]: ... + def postData(self) -> Any: ... @property def extra_info(self) -> Optional[RequestExtraInfo]: ... @@ -208,7 +208,7 @@ class Response(object): self._response: dict = ... self._raw_body: str = ... self._is_base64_body: bool = ... - self._body: Optional[str, dict] = ... + self._body: Union[str, dict, None] = ... self._headers: dict = ... @property @@ -221,7 +221,7 @@ class Response(object): def raw_body(self) -> str: ... @property - def body(self) -> Optional[str, dict]: ... + def body(self) -> Any: ... class ExtraInfo(object): diff --git a/DrissionPage/_units/setter.pyi b/DrissionPage/_units/setter.pyi index 901e084..542f733 100644 --- a/DrissionPage/_units/setter.pyi +++ b/DrissionPage/_units/setter.pyi @@ -6,7 +6,7 @@ @License : BSD 3-Clause. """ from pathlib import Path -from typing import Union, Tuple, Literal, Any, Optional +from typing import Union, Tuple, Literal, Any from requests.adapters import HTTPAdapter from requests.auth import HTTPBasicAuth @@ -64,7 +64,7 @@ class ChromiumBaseSetter(BasePageSetter): def upload_files(self, files: Union[str, list, tuple]) -> None: ... - def blocked_urls(self, urls: Optional[list, tuple, str]) -> None: ... + def blocked_urls(self, urls: Union[list, tuple, str, None]) -> None: ... class TabSetter(ChromiumBaseSetter): @@ -109,7 +109,7 @@ class SessionPageSetter(BasePageSetter): def timeout(self, second: float) -> None: ... - def encoding(self, encoding: Optional[str, None], set_all: bool = True) -> None: ... + def encoding(self, encoding: Union[str, None], set_all: bool = True) -> None: ... def headers(self, headers: dict) -> None: ... From 6451eab8a4a06913e04c78ce380fbd95aa5cbefa Mon Sep 17 00:00:00 2001 From: g1879 Date: Sun, 4 Feb 2024 23:04:37 +0800 Subject: [PATCH 08/21] =?UTF-8?q?=E5=A2=9E=E5=8A=A0click.to=5Fupload()?= =?UTF-8?q?=EF=BC=9B=E5=8F=96=E6=B6=88upload()?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DrissionPage/_pages/chromium_base.py | 22 +++++++++++----------- DrissionPage/_pages/chromium_base.pyi | 5 ----- DrissionPage/_units/clicker.py | 17 ++++++++++++++--- DrissionPage/_units/clicker.pyi | 5 ++++- 4 files changed, 29 insertions(+), 20 deletions(-) diff --git a/DrissionPage/_pages/chromium_base.py b/DrissionPage/_pages/chromium_base.py index c054c6a..1851972 100644 --- a/DrissionPage/_pages/chromium_base.py +++ b/DrissionPage/_pages/chromium_base.py @@ -747,17 +747,6 @@ class ChromiumBase(BasePage): frames = self._ele(locator, timeout=timeout, index=None, raise_err=False) return [i for i in frames if i._type == 'ChromiumFrame'] - def upload(self, loc_or_ele, file_paths, by_js=False): - """触发上传文件选择框并自动填入指定路径 - :param loc_or_ele: 被点击后会触发文件选择框的元素或它的定位符 - :param file_paths: 文件路径,如果上传框支持多文件,可传入列表或字符串,字符串时多个文件用回车分隔 - :param by_js: 是否用js方式点击 - :return: None - """ - self.set.upload_files(file_paths) - self.ele(loc_or_ele).click(by_js=by_js) - self.wait.upload_paths_inputted() - def session_storage(self, item=None): """返回sessionStorage信息,不设置item则获取全部 :param item: 要获取的项,不设置则返回全部 @@ -1147,6 +1136,17 @@ class ChromiumBase(BasePage): def get_cookies(self, as_dict=False, all_domains=False, all_info=False): return self.cookies(as_dict=as_dict, all_domains=all_domains, all_info=all_info) + def upload(self, loc_or_ele, file_paths, by_js=False): + """触发上传文件选择框并自动填入指定路径 + :param loc_or_ele: 被点击后会触发文件选择框的元素或它的定位符 + :param file_paths: 文件路径,如果上传框支持多文件,可传入列表或字符串,字符串时多个文件用回车分隔 + :param by_js: 是否用js方式点击 + :return: None + """ + self.set.upload_files(file_paths) + self.ele(loc_or_ele).click(by_js=by_js) + self.wait.upload_paths_inputted() + class Timeout(object): """用于保存d模式timeout信息的类""" diff --git a/DrissionPage/_pages/chromium_base.pyi b/DrissionPage/_pages/chromium_base.pyi index 3ff2ab1..91e4354 100644 --- a/DrissionPage/_pages/chromium_base.pyi +++ b/DrissionPage/_pages/chromium_base.pyi @@ -223,11 +223,6 @@ class ChromiumBase(BasePage): def get_frames(self, locator: Union[str, tuple] = None, timeout: float = None) -> List[ChromiumFrame]: ... - def upload(self, - loc_or_ele: Union[str, Tuple[str, str], ChromiumElement], - file_paths: Union[str, list, tuple], - by_js: bool = False) -> None: ... - def run_cdp(self, cmd: str, **cmd_args) -> dict: ... def run_cdp_loaded(self, cmd: str, **cmd_args) -> dict: ... diff --git a/DrissionPage/_units/clicker.py b/DrissionPage/_units/clicker.py index 3be5e33..a8b5513 100644 --- a/DrissionPage/_units/clicker.py +++ b/DrissionPage/_units/clicker.py @@ -140,12 +140,13 @@ class Clicker(object): """ self.at(count=times) - def to_download(self, save_path=None, rename=None, suffix=None, new_tab=False): + def to_download(self, save_path=None, rename=None, suffix=None, new_tab=False, by_js=False): """点击触发下载 :param save_path: 保存路径,为None保存在原来设置的,如未设置保存到当前路径 :param rename: 重命名文件名 :param suffix: 指定文件后缀 - :param new_tab: 是否在新tab触发下载 + :param new_tab: 该下载是否在新tab中触发 + :param by_js: 是否用js方式点击,逻辑与click()一致 :return: DownloadMission对象 """ if save_path: @@ -158,9 +159,19 @@ class Clicker(object): tab = self._ele.page._page if new_tab else self._ele.page - self._ele.click() + self._ele.click(by_js=by_js) return tab.wait.download_begin() + def to_upload(self, file_paths, by_js=False): + """触发上传文件选择框并自动填入指定路径 + :param file_paths: 文件路径,如果上传框支持多文件,可传入列表或字符串,字符串时多个文件用回车分隔 + :param by_js: 是否用js方式点击,逻辑与click()一致 + :return: None + """ + self._ele.page.set.upload_files(file_paths) + self._ele.click(by_js=by_js) + self._ele.page.wait.upload_paths_inputted() + def _click(self, client_x, client_y, button='left', count=1): """实施点击 :param client_x: 视口中的x坐标 diff --git a/DrissionPage/_units/clicker.pyi b/DrissionPage/_units/clicker.pyi index 73cc689..7aa346d 100644 --- a/DrissionPage/_units/clicker.pyi +++ b/DrissionPage/_units/clicker.pyi @@ -36,6 +36,9 @@ class Clicker(object): save_path: Union[str, Path] = None, rename: str = None, suffix: str = None, - new_tab: bool = False) -> DownloadMission: ... + new_tab: bool = False, + by_js: bool = False) -> DownloadMission: ... + + def to_upload(self, file_paths: Union[str, list, tuple], by_js: bool = False) -> None: ... def _click(self, client_x: float, client_y: float, button: str = 'left', count: int = 1) -> None: ... From 7bb1e64a79cc73a870fb07bcff514b9181f65a57 Mon Sep 17 00:00:00 2001 From: g1879 Date: Mon, 5 Feb 2024 15:47:55 +0800 Subject: [PATCH 09/21] =?UTF-8?q?4.0.4.5=E5=AE=8C=E5=96=84tree()?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DrissionPage/_base/base.py | 2 +- DrissionPage/_base/base.pyi | 7 ++++-- DrissionPage/_functions/web.py | 44 +++++++++++++++++++-------------- DrissionPage/_functions/web.pyi | 4 +-- DrissionPage/common.py | 4 +-- 5 files changed, 36 insertions(+), 25 deletions(-) diff --git a/DrissionPage/_base/base.py b/DrissionPage/_base/base.py index 600a3f1..21e3292 100644 --- a/DrissionPage/_base/base.py +++ b/DrissionPage/_base/base.py @@ -36,7 +36,7 @@ class BaseParser(object): def html(self): return '' - def s_ele(self, locator): + def s_ele(self, locator=None): pass def s_eles(self, locator): diff --git a/DrissionPage/_base/base.pyi b/DrissionPage/_base/base.pyi index 2fabbe6..235d8f6 100644 --- a/DrissionPage/_base/base.pyi +++ b/DrissionPage/_base/base.pyi @@ -11,6 +11,7 @@ from typing import Union, Tuple, List, Any, Optional from DownloadKit import DownloadKit from .._elements.none_element import NoneElement +from .._elements.session_element import SessionElement class BaseParser(object): @@ -29,9 +30,11 @@ class BaseParser(object): @property def html(self) -> str: ... - def s_ele(self, locator: Union[Tuple[str, str], str, BaseElement], index: int = 1): ... + def s_ele(self, + locator: Union[Tuple[str, str], str, BaseElement, None] = None, + index: int = 1) -> SessionElement: ... - def s_eles(self, locator: Union[Tuple[str, str], str]): ... + def s_eles(self, locator: Union[Tuple[str, str], str]) -> List[SessionElement]: ... def _ele(self, locator: Union[Tuple[str, str], str], diff --git a/DrissionPage/_functions/web.py b/DrissionPage/_functions/web.py index 30387bf..1f22d09 100644 --- a/DrissionPage/_functions/web.py +++ b/DrissionPage/_functions/web.py @@ -365,24 +365,32 @@ def get_blob(page, url, as_bytes=True): return result -def tree(ele_or_page, layer=5, last_one=False, body=''): - try: - list_ele = ele_or_page.s_ele().children(timeout=0.1) - except: - list_ele = [] - length = len(list_ele) - body_unit = ' ' if last_one else '│ ' - tail = '├───' - new_body = body + body_unit +def tree(ele_or_page): + """把页面或元素对象DOM结构打印出来 + :param ele_or_page: 页面或元素对象 + :return: None + """ + def _tree(obj, last_one=True, body=''): + list_ele = obj.children() + length = len(list_ele) + body_unit = ' ' if last_one else '│ ' + tail = '├───' + new_body = body + body_unit - if length > 0 and layer >= 1: - new_last_one = False - for i in range(length): - if i == length - 1: - tail = '└───' - new_last_one = True - e = list_ele[i] + if length > 0: + new_last_one = False + for i in range(length): + if i == length - 1: + tail = '└───' + new_last_one = True + e = list_ele[i] - print(f'{new_body}{tail}{i}<{e.tag}> {e.attrs}') + attrs = ' '.join([f"{k}='{v}'" for k, v in e.attrs.items()]) + print(f'{new_body}{tail}<{e.tag} {attrs}>'.replace('\n', ' ')) - tree(e, layer - 1, new_last_one, new_body) \ No newline at end of file + _tree(e, new_last_one, new_body) + + ele = ele_or_page.s_ele() + attrs = ' '.join([f"{k}='{v}'" for k, v in ele.attrs.items()]) + print(f'<{ele.tag} {attrs}>'.replace('\n', ' ')) + _tree(ele) diff --git a/DrissionPage/_functions/web.pyi b/DrissionPage/_functions/web.pyi index c592613..aaebf3c 100644 --- a/DrissionPage/_functions/web.pyi +++ b/DrissionPage/_functions/web.pyi @@ -11,7 +11,7 @@ from typing import Union from requests import Session from requests.cookies import RequestsCookieJar -from .._base.base import BasePage, DrissionElement +from .._base.base import DrissionElement, BaseParser from .._elements.chromium_element import ChromiumElement from .._pages.chromium_base import ChromiumBase @@ -52,4 +52,4 @@ def is_cookie_in_driver(page: ChromiumBase, cookie: dict) -> bool: ... def get_blob(page: ChromiumBase, url: str, as_bytes: bool = True) -> bytes: ... -def tree(ele_or_page, layer=5, last_one=False, body='') -> None: ... +def tree(ele_or_page:BaseParser) -> None: ... diff --git a/DrissionPage/common.py b/DrissionPage/common.py index 5c1e2b8..2c09a2e 100644 --- a/DrissionPage/common.py +++ b/DrissionPage/common.py @@ -10,7 +10,7 @@ from ._functions.by import By from ._functions.keys import Keys from ._functions.settings import Settings from ._functions.tools import wait_until, configs_to_here -from ._functions.web import get_blob +from ._functions.web import get_blob, tree from ._units.actions import Actions -__all__ = ['make_session_ele', 'Actions', 'Keys', 'By', 'Settings', 'wait_until', 'configs_to_here', 'get_blob'] +__all__ = ['make_session_ele', 'Actions', 'Keys', 'By', 'Settings', 'wait_until', 'configs_to_here', 'get_blob', 'tree'] From 193049428dc01bd700d4dbd3a926a768d6b0d890 Mon Sep 17 00:00:00 2001 From: g1879 Date: Tue, 6 Feb 2024 16:36:44 +0800 Subject: [PATCH 10/21] =?UTF-8?q?=E4=BF=AE=E6=94=B9quit()=E9=80=BB?= =?UTF-8?q?=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DrissionPage/_base/browser.py | 2 +- DrissionPage/_functions/browser.py | 2 +- DrissionPage/_functions/tools.py | 15 +++++++++++++-- DrissionPage/_functions/tools.pyi | 2 +- 4 files changed, 16 insertions(+), 5 deletions(-) diff --git a/DrissionPage/_base/browser.py b/DrissionPage/_base/browser.py index 89fd915..ce41cff 100644 --- a/DrissionPage/_base/browser.py +++ b/DrissionPage/_base/browser.py @@ -210,7 +210,7 @@ class Browser(object): ip, port = self.address.split(':') if ip not in ('127.0.0.1', 'localhost'): return - stop_process_on_port(port) + stop_process_on_port(port, self.process_id) return if self.process_id: diff --git a/DrissionPage/_functions/browser.py b/DrissionPage/_functions/browser.py index 4f79269..2fbc5c7 100644 --- a/DrissionPage/_functions/browser.py +++ b/DrissionPage/_functions/browser.py @@ -157,7 +157,7 @@ def set_prefs(opt): def set_flags(opt): - """处理启动配置中的prefs项,目前只能对已存在文件夹配置 + """处理启动配置中的flags项 :param opt: ChromiumOptions :return: None """ diff --git a/DrissionPage/_functions/tools.py b/DrissionPage/_functions/tools.py index ee4ba25..dfd2340 100644 --- a/DrissionPage/_functions/tools.py +++ b/DrissionPage/_functions/tools.py @@ -12,7 +12,7 @@ from tempfile import gettempdir, TemporaryDirectory from threading import Lock from time import perf_counter -from psutil import process_iter, AccessDenied, NoSuchProcess, ZombieProcess +from psutil import process_iter, AccessDenied, NoSuchProcess, ZombieProcess, Process from .._configs.options_manage import OptionsManager from ..errors import (ContextLostError, ElementLostError, CDPError, PageDisconnectedError, NoRectError, @@ -182,9 +182,10 @@ def wait_until(function, kwargs=None, timeout=10): raise TimeoutError -def stop_process_on_port(port): +def stop_process_on_port(port, pid=None): """强制关闭某个端口内的进程 :param port: 端口号 + :param pid: 进程号 :return: None """ for proc in process_iter(['pid', 'connections']): @@ -201,6 +202,16 @@ def stop_process_on_port(port): except Exception as e: print(f"{proc.pid} {port}: {e}") + if pid: + for p in Process(pid).children(): + try: + p.terminate() + except: + pass + try: + Process(pid).terminate() + except: + pass def configs_to_here(save_name=None): """把默认ini文件复制到当前目录 diff --git a/DrissionPage/_functions/tools.pyi b/DrissionPage/_functions/tools.pyi index f6e13f5..f6b55c0 100644 --- a/DrissionPage/_functions/tools.pyi +++ b/DrissionPage/_functions/tools.pyi @@ -42,7 +42,7 @@ def get_hwnds_from_pid(pid: Union[str, int], title: str) -> list: ... def wait_until(function: callable, kwargs: dict = None, timeout: float = 10): ... -def stop_process_on_port(port: Union[int, str]) -> None: ... +def stop_process_on_port(port: Union[int, str], pid: int = None) -> None: ... def configs_to_here(file_name: Union[Path, str] = None) -> None: ... From 45f8c38a75081eece7809a7568b8479293e3685e Mon Sep 17 00:00:00 2001 From: g1879 Date: Tue, 6 Feb 2024 16:56:29 +0800 Subject: [PATCH 11/21] =?UTF-8?q?=E4=BF=AE=E6=94=B9quit()=E9=80=BB?= =?UTF-8?q?=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DrissionPage/_functions/tools.py | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/DrissionPage/_functions/tools.py b/DrissionPage/_functions/tools.py index dfd2340..49b0bca 100644 --- a/DrissionPage/_functions/tools.py +++ b/DrissionPage/_functions/tools.py @@ -188,6 +188,14 @@ def stop_process_on_port(port, pid=None): :param pid: 进程号 :return: None """ + if pid: + try: + for p in Process(pid).children(): + p.terminate() + Process(pid).terminate() + except: + pass + for proc in process_iter(['pid', 'connections']): try: connections = proc.connections() @@ -202,16 +210,6 @@ def stop_process_on_port(port, pid=None): except Exception as e: print(f"{proc.pid} {port}: {e}") - if pid: - for p in Process(pid).children(): - try: - p.terminate() - except: - pass - try: - Process(pid).terminate() - except: - pass def configs_to_here(save_name=None): """把默认ini文件复制到当前目录 From cfb0a6b8f654bd0ddc367c44513fb6d2bac08f1f Mon Sep 17 00:00:00 2001 From: g1879 Date: Wed, 7 Feb 2024 08:32:20 +0000 Subject: [PATCH 12/21] update README.md. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index db38f30..49cbe38 100644 --- a/README.md +++ b/README.md @@ -123,4 +123,4 @@ python 版本:3.6 及以上 如果本项目对您有所帮助,不妨请作者我喝杯咖啡 :) -![](https://gitee.com/g1879/DrissionPageDocs/raw/master/docs/imgs/code.jpg) +![](https://g1879.gitee.io/drissionpagedocs/assets/images/code-cf68de50a2f331a2aa0d39c9aebbbe2c.jpg) From 70243a1fd1ade16ec47050ab5e54b5eee131381a Mon Sep 17 00:00:00 2001 From: g1879 Date: Wed, 7 Feb 2024 20:28:13 +0800 Subject: [PATCH 13/21] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=EF=BC=9Bclick.for=5Fne?= =?UTF-8?q?w=5Ftab()=EF=BC=9Bset.upload=5Ffiles()=E5=92=8Cclick.to=5Fuploa?= =?UTF-8?q?d()=E6=94=AF=E6=8C=81Path=E7=B1=BB=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DrissionPage/_units/clicker.py | 15 +++++++++++++-- DrissionPage/_units/clicker.pyi | 5 ++++- DrissionPage/_units/setter.py | 2 ++ DrissionPage/_units/setter.pyi | 2 +- 4 files changed, 20 insertions(+), 4 deletions(-) diff --git a/DrissionPage/_units/clicker.py b/DrissionPage/_units/clicker.py index a8b5513..ce1a394 100644 --- a/DrissionPage/_units/clicker.py +++ b/DrissionPage/_units/clicker.py @@ -159,7 +159,7 @@ class Clicker(object): tab = self._ele.page._page if new_tab else self._ele.page - self._ele.click(by_js=by_js) + self.left(by_js=by_js) return tab.wait.download_begin() def to_upload(self, file_paths, by_js=False): @@ -169,9 +169,20 @@ class Clicker(object): :return: None """ self._ele.page.set.upload_files(file_paths) - self._ele.click(by_js=by_js) + self.left(by_js=by_js) self._ele.page.wait.upload_paths_inputted() + def for_new_tab(self, by_js=False): + """点击后等待新tab出现并返回其对象 + :param by_js: 是否使用js点击,逻辑与click()一致 + :return: 新标签页对象,如果没有等到新标签页出现则抛出异常 + """ + self.left(by_js=by_js) + tid = self._ele.page._page.wait.new_tab() + if not tid: + raise RuntimeError('没有出现新标签页。') + return self._ele.page._page.get_tab(tid) + def _click(self, client_x, client_y, button='left', count=1): """实施点击 :param client_x: 视口中的x坐标 diff --git a/DrissionPage/_units/clicker.pyi b/DrissionPage/_units/clicker.pyi index 7aa346d..ac80568 100644 --- a/DrissionPage/_units/clicker.pyi +++ b/DrissionPage/_units/clicker.pyi @@ -10,6 +10,7 @@ from typing import Union from .downloader import DownloadMission from .._elements.chromium_element import ChromiumElement +from .._pages.chromium_tab import WebPageTab, ChromiumTab class Clicker(object): @@ -39,6 +40,8 @@ class Clicker(object): new_tab: bool = False, by_js: bool = False) -> DownloadMission: ... - def to_upload(self, file_paths: Union[str, list, tuple], by_js: bool = False) -> None: ... + def to_upload(self, file_paths: Union[str, Path, list, tuple], by_js: bool = False) -> None: ... + + def for_new_tab(self, by_js:bool=False)->Union[ChromiumTab, WebPageTab]:... def _click(self, client_x: float, client_y: float, button: str = 'left', count: int = 1) -> None: ... diff --git a/DrissionPage/_units/setter.py b/DrissionPage/_units/setter.py index dd0780c..22a3423 100644 --- a/DrissionPage/_units/setter.py +++ b/DrissionPage/_units/setter.py @@ -130,6 +130,8 @@ class ChromiumBaseSetter(BasePageSetter): if isinstance(files, str): files = files.split('\n') + elif isinstance(files, Path): + files = (files, ) self._page._upload_list = [str(Path(i).absolute()) for i in files] def headers(self, headers: dict) -> None: diff --git a/DrissionPage/_units/setter.pyi b/DrissionPage/_units/setter.pyi index 542f733..8695d61 100644 --- a/DrissionPage/_units/setter.pyi +++ b/DrissionPage/_units/setter.pyi @@ -62,7 +62,7 @@ class ChromiumBaseSetter(BasePageSetter): def auto_handle_alert(self, on_off: bool = True, accept: bool = True) -> None: ... - def upload_files(self, files: Union[str, list, tuple]) -> None: ... + def upload_files(self, files: Union[str, Path, list, tuple]) -> None: ... def blocked_urls(self, urls: Union[list, tuple, str, None]) -> None: ... From 038d837dda52f5c698a10e893d0b9480a1d29f45 Mon Sep 17 00:00:00 2001 From: g1879 Date: Thu, 8 Feb 2024 10:13:14 +0800 Subject: [PATCH 14/21] =?UTF-8?q?click.to=5Fdownload()=E5=A2=9E=E5=8A=A0ti?= =?UTF-8?q?meout=E5=8F=82=E6=95=B0=EF=BC=9B=E5=AE=8C=E5=96=84=E6=89=BEchro?= =?UTF-8?q?me=E8=B7=AF=E5=BE=84=E9=80=BB=E8=BE=91=EF=BC=9B=E8=B0=83?= =?UTF-8?q?=E6=95=B4quit()=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DrissionPage/_functions/browser.py | 20 +++++++++++++------- DrissionPage/_functions/tools.py | 7 ++++++- DrissionPage/_units/clicker.py | 5 +++-- DrissionPage/_units/clicker.pyi | 3 ++- 4 files changed, 24 insertions(+), 11 deletions(-) diff --git a/DrissionPage/_functions/browser.py b/DrissionPage/_functions/browser.py index 2fbc5c7..5075945 100644 --- a/DrissionPage/_functions/browser.py +++ b/DrissionPage/_functions/browser.py @@ -316,14 +316,20 @@ def get_chrome_path(ini_path): return None # -----------从注册表中获取-------------- - import winreg + from winreg import OpenKey, EnumValue, CloseKey, HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE, KEY_READ try: - key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, - r'SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\chrome.exe', - reserved=0, access=winreg.KEY_READ) - k = winreg.EnumValue(key, 0) - winreg.CloseKey(key) - + key = OpenKey(HKEY_CURRENT_USER, + r'SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\chrome.exe', + reserved=0, access=KEY_READ) + k = EnumValue(key, 0) + CloseKey(key) + if k[1]: + return k[1] + key = OpenKey(HKEY_LOCAL_MACHINE, + r'SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\chrome.exe', + reserved=0, access=KEY_READ) + k = EnumValue(key, 0) + CloseKey(key) return k[1] except FileNotFoundError: diff --git a/DrissionPage/_functions/tools.py b/DrissionPage/_functions/tools.py index 49b0bca..c2ec84e 100644 --- a/DrissionPage/_functions/tools.py +++ b/DrissionPage/_functions/tools.py @@ -204,7 +204,12 @@ def stop_process_on_port(port, pid=None): for conn in connections: if conn.laddr.port == int(port): try: - proc.terminate() + # proc.terminate() + parent = proc.parent() + if parent is None: + proc.kill() + elif parent.name() == 'explorer.exe': + proc.terminate() except (NoSuchProcess, AccessDenied, ZombieProcess): pass except Exception as e: diff --git a/DrissionPage/_units/clicker.py b/DrissionPage/_units/clicker.py index ce1a394..d8a9b1e 100644 --- a/DrissionPage/_units/clicker.py +++ b/DrissionPage/_units/clicker.py @@ -140,13 +140,14 @@ class Clicker(object): """ self.at(count=times) - def to_download(self, save_path=None, rename=None, suffix=None, new_tab=False, by_js=False): + def to_download(self, save_path=None, rename=None, suffix=None, new_tab=False, by_js=False, timeout=None): """点击触发下载 :param save_path: 保存路径,为None保存在原来设置的,如未设置保存到当前路径 :param rename: 重命名文件名 :param suffix: 指定文件后缀 :param new_tab: 该下载是否在新tab中触发 :param by_js: 是否用js方式点击,逻辑与click()一致 + :param timeout: 等待下载触发的超时时间,为None则使用页面对象设置 :return: DownloadMission对象 """ if save_path: @@ -160,7 +161,7 @@ class Clicker(object): tab = self._ele.page._page if new_tab else self._ele.page self.left(by_js=by_js) - return tab.wait.download_begin() + return tab.wait.download_begin(timeout=timeout) def to_upload(self, file_paths, by_js=False): """触发上传文件选择框并自动填入指定路径 diff --git a/DrissionPage/_units/clicker.pyi b/DrissionPage/_units/clicker.pyi index ac80568..e6ca30f 100644 --- a/DrissionPage/_units/clicker.pyi +++ b/DrissionPage/_units/clicker.pyi @@ -38,7 +38,8 @@ class Clicker(object): rename: str = None, suffix: str = None, new_tab: bool = False, - by_js: bool = False) -> DownloadMission: ... + by_js: bool = False, + timeout:float=None) -> DownloadMission: ... def to_upload(self, file_paths: Union[str, Path, list, tuple], by_js: bool = False) -> None: ... From 0e57938e23a2cbe398a4fc0505518c3ecbe214ea Mon Sep 17 00:00:00 2001 From: g1879 Date: Fri, 9 Feb 2024 21:46:34 +0800 Subject: [PATCH 15/21] =?UTF-8?q?=E8=B0=83=E6=95=B4quit()=E9=80=BB?= =?UTF-8?q?=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DrissionPage/_base/browser.py | 49 ++++++++++++++++--------------- DrissionPage/_functions/tools.py | 36 ----------------------- DrissionPage/_functions/tools.pyi | 3 -- 3 files changed, 26 insertions(+), 62 deletions(-) diff --git a/DrissionPage/_base/browser.py b/DrissionPage/_base/browser.py index ce41cff..86caffe 100644 --- a/DrissionPage/_base/browser.py +++ b/DrissionPage/_base/browser.py @@ -7,12 +7,12 @@ """ from pathlib import Path from shutil import rmtree -from time import sleep, perf_counter +from time import perf_counter, sleep from websocket import WebSocketBadStatusException from .driver import BrowserDriver, Driver -from .._functions.tools import stop_process_on_port, raise_error +from .._functions.tools import raise_error from .._units.downloader import DownloadManager from ..errors import PageDisconnectedError @@ -196,38 +196,41 @@ class Browser(object): :param force: 是否立刻强制终止进程 :return: None """ + pids = [pid['id'] for pid in self.run_cdp('SystemInfo.getProcessInfo')['processInfo']] for tab in self._all_drivers.values(): for driver in tab: driver.stop() - try: - self.run_cdp('Browser.close') - except PageDisconnectedError: - self.driver.stop() - return - self.driver.stop() if force: - ip, port = self.address.split(':') - if ip not in ('127.0.0.1', 'localhost'): - return - stop_process_on_port(port, self.process_id) - return + from psutil import Process + for pid in pids: + Process(pid).kill() + else: + try: + self.run_cdp('Browser.close') + self.driver.stop() + except PageDisconnectedError: + self.driver.stop() - if self.process_id: - from os import popen - from platform import system - txt = f'tasklist | findstr {self.process_id}' if system().lower() == 'windows' \ - else f'ps -ef | grep {self.process_id}' - end_time = perf_counter() + timeout - while perf_counter() < end_time: + from os import popen + from platform import system + end_time = perf_counter() + timeout + while perf_counter() < end_time: + ok = True + for pid in pids: + txt = f'tasklist | findstr {pid}' if system().lower() == 'windows' else f'ps -ef | grep {pid}' p = popen(txt) - sleep(.1) + sleep(.05) try: - if f' {self.process_id} ' not in p.read(): - return + if f' {pid} ' in p.read(): + ok = False + break except TypeError: pass + if ok: + break + def _on_disconnect(self): self.page._on_disconnect() Browser.BROWSERS.pop(self.id, None) diff --git a/DrissionPage/_functions/tools.py b/DrissionPage/_functions/tools.py index c2ec84e..38640cf 100644 --- a/DrissionPage/_functions/tools.py +++ b/DrissionPage/_functions/tools.py @@ -12,8 +12,6 @@ from tempfile import gettempdir, TemporaryDirectory from threading import Lock from time import perf_counter -from psutil import process_iter, AccessDenied, NoSuchProcess, ZombieProcess, Process - from .._configs.options_manage import OptionsManager from ..errors import (ContextLostError, ElementLostError, CDPError, PageDisconnectedError, NoRectError, AlertExistsError, WrongURLError, StorageError, CookieFormatError, JavaScriptError) @@ -182,40 +180,6 @@ def wait_until(function, kwargs=None, timeout=10): raise TimeoutError -def stop_process_on_port(port, pid=None): - """强制关闭某个端口内的进程 - :param port: 端口号 - :param pid: 进程号 - :return: None - """ - if pid: - try: - for p in Process(pid).children(): - p.terminate() - Process(pid).terminate() - except: - pass - - for proc in process_iter(['pid', 'connections']): - try: - connections = proc.connections() - except (AccessDenied, NoSuchProcess): - continue - for conn in connections: - if conn.laddr.port == int(port): - try: - # proc.terminate() - parent = proc.parent() - if parent is None: - proc.kill() - elif parent.name() == 'explorer.exe': - proc.terminate() - except (NoSuchProcess, AccessDenied, ZombieProcess): - pass - except Exception as e: - print(f"{proc.pid} {port}: {e}") - - def configs_to_here(save_name=None): """把默认ini文件复制到当前目录 :param save_name: 指定文件名,为None则命名为'dp_configs.ini' diff --git a/DrissionPage/_functions/tools.pyi b/DrissionPage/_functions/tools.pyi index f6b55c0..86d3527 100644 --- a/DrissionPage/_functions/tools.pyi +++ b/DrissionPage/_functions/tools.pyi @@ -42,9 +42,6 @@ def get_hwnds_from_pid(pid: Union[str, int], title: str) -> list: ... def wait_until(function: callable, kwargs: dict = None, timeout: float = 10): ... -def stop_process_on_port(port: Union[int, str], pid: int = None) -> None: ... - - def configs_to_here(file_name: Union[Path, str] = None) -> None: ... From 597a9883c8b616c0ecb81279080d3ff5743b1132 Mon Sep 17 00:00:00 2001 From: g1879 Date: Tue, 20 Feb 2024 17:53:29 +0800 Subject: [PATCH 16/21] =?UTF-8?q?=E5=85=83=E7=B4=A0=E5=AF=B9=E8=B1=A1page?= =?UTF-8?q?=E5=B1=9E=E6=80=A7=E6=94=B9=E4=B8=BAowner?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DrissionPage/_base/base.py | 9 +- DrissionPage/_base/base.pyi | 13 +- DrissionPage/_base/browser.py | 49 ++++--- DrissionPage/_elements/chromium_element.py | 149 ++++++++++---------- DrissionPage/_elements/chromium_element.pyi | 12 +- DrissionPage/_elements/session_element.py | 24 ++-- DrissionPage/_elements/session_element.pyi | 3 +- DrissionPage/_functions/browser.py | 20 ++- DrissionPage/_functions/tools.py | 31 ---- DrissionPage/_functions/tools.pyi | 3 - DrissionPage/_functions/web.py | 8 +- DrissionPage/_pages/chromium_base.pyi | 4 +- DrissionPage/_pages/chromium_frame.py | 33 +++-- DrissionPage/_pages/chromium_frame.pyi | 11 +- DrissionPage/_pages/chromium_page.py | 1 + DrissionPage/_pages/chromium_page.pyi | 1 + DrissionPage/_pages/chromium_tab.py | 3 +- DrissionPage/_pages/session_page.py | 1 + DrissionPage/_pages/session_page.pyi | 7 +- DrissionPage/_units/actions.py | 28 ++-- DrissionPage/_units/actions.pyi | 4 +- DrissionPage/_units/clicker.py | 52 ++++--- DrissionPage/_units/clicker.pyi | 8 +- DrissionPage/_units/rect.py | 24 ++-- DrissionPage/_units/scroller.py | 6 +- DrissionPage/_units/selector.py | 4 +- DrissionPage/_units/setter.py | 4 +- DrissionPage/_units/setter.pyi | 2 +- DrissionPage/_units/states.py | 8 +- 29 files changed, 273 insertions(+), 249 deletions(-) diff --git a/DrissionPage/_base/base.py b/DrissionPage/_base/base.py index 21e3292..d67ccb6 100644 --- a/DrissionPage/_base/base.py +++ b/DrissionPage/_base/base.py @@ -52,8 +52,9 @@ class BaseParser(object): class BaseElement(BaseParser): """各元素类的基类""" - def __init__(self, page=None): - self.page = page + def __init__(self, owner=None): + self.owner = owner + self.page = owner._page self._type = 'BaseElement' # ----------------以下属性或方法由后代实现---------------- @@ -175,7 +176,7 @@ class DrissionElement(BaseElement): raise ElementNotFoundError(None, 'child()', {'locator': locator, 'index': index, 'ele_only': ele_only}) else: - return NoneElement(self.page, 'child()', {'locator': locator, 'index': index, 'ele_only': ele_only}) + return NoneElement(self.owner, 'child()', {'locator': locator, 'index': index, 'ele_only': ele_only}) def prev(self, locator='', index=1, timeout=None, ele_only=True): """返回前面的一个兄弟元素,可用查询语法筛选,可指定返回筛选结果的第几个 @@ -293,7 +294,7 @@ class DrissionElement(BaseElement): if Settings.raise_when_ele_not_found: raise ElementNotFoundError(None, func, {'locator': locator, 'index': index, 'ele_only': ele_only}) else: - return NoneElement(self.page, func, {'locator': locator, 'index': index, 'ele_only': ele_only}) + return NoneElement(self.owner, func, {'locator': locator, 'index': index, 'ele_only': ele_only}) def _get_relatives(self, index=None, locator='', direction='following', brother=True, timeout=.5, ele_only=True): """按要求返回兄弟元素或节点组成的列表 diff --git a/DrissionPage/_base/base.pyi b/DrissionPage/_base/base.pyi index 235d8f6..ea4d22d 100644 --- a/DrissionPage/_base/base.pyi +++ b/DrissionPage/_base/base.pyi @@ -12,6 +12,9 @@ from DownloadKit import DownloadKit from .._elements.none_element import NoneElement from .._elements.session_element import SessionElement +from .._pages.chromium_page import ChromiumPage +from .._pages.session_page import SessionPage +from .._pages.web_page import WebPage class BaseParser(object): @@ -53,8 +56,9 @@ class BaseParser(object): class BaseElement(BaseParser): - def __init__(self, page: BasePage = None): - self.page: BasePage = ... + def __init__(self, owner: BasePage = None): + self.owner: BasePage = ... + self.page: Union[ChromiumPage, SessionPage, WebPage] = ... # ----------------以下属性或方法由后代实现---------------- @property @@ -81,9 +85,7 @@ class BaseElement(BaseParser): class DrissionElement(BaseElement): - def __init__(self, - page: BasePage = ...): - self.page: BasePage = ... + def __init__(self, owner: BasePage = None): ... @property def link(self) -> str: ... @@ -202,6 +204,7 @@ class BasePage(BaseParser): self._DownloadKit: DownloadKit = ... self._none_ele_return_value: bool = ... self._none_ele_value: Any = ... + self._page: Union[ChromiumPage, SessionPage, WebPage]=... @property def title(self) -> Union[str, None]: ... diff --git a/DrissionPage/_base/browser.py b/DrissionPage/_base/browser.py index ce41cff..86caffe 100644 --- a/DrissionPage/_base/browser.py +++ b/DrissionPage/_base/browser.py @@ -7,12 +7,12 @@ """ from pathlib import Path from shutil import rmtree -from time import sleep, perf_counter +from time import perf_counter, sleep from websocket import WebSocketBadStatusException from .driver import BrowserDriver, Driver -from .._functions.tools import stop_process_on_port, raise_error +from .._functions.tools import raise_error from .._units.downloader import DownloadManager from ..errors import PageDisconnectedError @@ -196,38 +196,41 @@ class Browser(object): :param force: 是否立刻强制终止进程 :return: None """ + pids = [pid['id'] for pid in self.run_cdp('SystemInfo.getProcessInfo')['processInfo']] for tab in self._all_drivers.values(): for driver in tab: driver.stop() - try: - self.run_cdp('Browser.close') - except PageDisconnectedError: - self.driver.stop() - return - self.driver.stop() if force: - ip, port = self.address.split(':') - if ip not in ('127.0.0.1', 'localhost'): - return - stop_process_on_port(port, self.process_id) - return + from psutil import Process + for pid in pids: + Process(pid).kill() + else: + try: + self.run_cdp('Browser.close') + self.driver.stop() + except PageDisconnectedError: + self.driver.stop() - if self.process_id: - from os import popen - from platform import system - txt = f'tasklist | findstr {self.process_id}' if system().lower() == 'windows' \ - else f'ps -ef | grep {self.process_id}' - end_time = perf_counter() + timeout - while perf_counter() < end_time: + from os import popen + from platform import system + end_time = perf_counter() + timeout + while perf_counter() < end_time: + ok = True + for pid in pids: + txt = f'tasklist | findstr {pid}' if system().lower() == 'windows' else f'ps -ef | grep {pid}' p = popen(txt) - sleep(.1) + sleep(.05) try: - if f' {self.process_id} ' not in p.read(): - return + if f' {pid} ' in p.read(): + ok = False + break except TypeError: pass + if ok: + break + def _on_disconnect(self): self.page._on_disconnect() Browser.BROWSERS.pop(self.id, None) diff --git a/DrissionPage/_elements/chromium_element.py b/DrissionPage/_elements/chromium_element.py index 5f6c62a..4d4e872 100644 --- a/DrissionPage/_elements/chromium_element.py +++ b/DrissionPage/_elements/chromium_element.py @@ -36,14 +36,15 @@ __FRAME_ELEMENT__ = ('iframe', 'frame') class ChromiumElement(DrissionElement): """控制浏览器元素的对象""" - def __init__(self, page, node_id=None, obj_id=None, backend_id=None): + def __init__(self, owner, node_id=None, obj_id=None, backend_id=None): """node_id、obj_id和backend_id必须至少传入一个 - :param page: 元素所在页面对象 + :param owner: 元素所在页面对象 :param node_id: cdp中的node id :param obj_id: js中的object id :param backend_id: backend id """ - super().__init__(page) + super().__init__(owner) + self.tab = self.owner.tab self._select = None self._scroll = None self._rect = None @@ -96,14 +97,14 @@ class ChromiumElement(DrissionElement): def tag(self): """返回元素tag""" if self._tag is None: - self._tag = self.page.run_cdp('DOM.describeNode', - backendNodeId=self._backend_id)['node']['localName'].lower() + self._tag = self.owner.run_cdp('DOM.describeNode', + backendNodeId=self._backend_id)['node']['localName'].lower() return self._tag @property def html(self): """返回元素outerHTML文本""" - return self.page.run_cdp('DOM.getOuterHTML', backendNodeId=self._backend_id)['outerHTML'] + return self.owner.run_cdp('DOM.getOuterHTML', backendNodeId=self._backend_id)['outerHTML'] @property def inner_html(self): @@ -114,7 +115,7 @@ class ChromiumElement(DrissionElement): def attrs(self): """返回元素所有attribute属性""" try: - attrs = self.page.run_cdp('DOM.getAttributes', nodeId=self._node_id)['attributes'] + attrs = self.owner.run_cdp('DOM.getAttributes', nodeId=self._node_id)['attributes'] return {attrs[i]: attrs[i + 1] for i in range(0, len(attrs), 2)} except CDPError: # 文档根元素不能调用此方法 return {} @@ -161,7 +162,7 @@ class ChromiumElement(DrissionElement): @property def shadow_root(self): """返回当前元素的shadow_root元素对象""" - info = self.page.run_cdp('DOM.describeNode', backendNodeId=self._backend_id)['node'] + info = self.owner.run_cdp('DOM.describeNode', backendNodeId=self._backend_id)['node'] if not info.get('shadowRoots', None): return None @@ -190,7 +191,7 @@ class ChromiumElement(DrissionElement): def wait(self): """返回用于等待的对象""" if self._wait is None: - self._wait = ElementWaiter(self.page, self) + self._wait = ElementWaiter(self.owner, self) return self._wait @property @@ -412,7 +413,7 @@ class ChromiumElement(DrissionElement): :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) + return run_js(self, script, as_expr, self.owner.timeouts.script if timeout is None else timeout, args) def run_async_js(self, script, *args, as_expr=False): """以异步方式对本元素执行javascript代码 @@ -494,7 +495,7 @@ class ChromiumElement(DrissionElement): :param base64_to_bytes: 为True时,如果是base64数据,转换为bytes格式 :return: 资源内容 """ - timeout = self.page.timeout if timeout is None else timeout + timeout = self.owner.timeout if timeout is None else timeout if self.tag == 'img': # 等待图片加载完成 js = ('return this.complete && typeof this.naturalWidth != "undefined" ' '&& this.naturalWidth > 0 && typeof this.naturalHeight != "undefined" ' @@ -517,7 +518,7 @@ class ChromiumElement(DrissionElement): end_time = perf_counter() + timeout while perf_counter() < end_time: if is_blob: - result = get_blob(self.page, src, base64_to_bytes) + result = get_blob(self.owner, src, base64_to_bytes) if result: break @@ -526,11 +527,11 @@ class ChromiumElement(DrissionElement): if not src: continue - node = self.page.run_cdp('DOM.describeNode', backendNodeId=self._backend_id)['node'] - frame = node.get('frameId', None) or self.page._frame_id + node = self.owner.run_cdp('DOM.describeNode', backendNodeId=self._backend_id)['node'] + frame = node.get('frameId', None) or self.owner._frame_id try: - result = self.page.run_cdp('Page.getResourceContent', frameId=frame, url=src) + result = self.owner.run_cdp('Page.getResourceContent', frameId=frame, url=src) break except CDPError: sleep(.1) @@ -585,7 +586,7 @@ class ChromiumElement(DrissionElement): if self.tag == 'img': # 等待图片加载完成 js = ('return this.complete && typeof this.naturalWidth != "undefined" && this.naturalWidth > 0 ' '&& typeof this.naturalHeight != "undefined" && this.naturalHeight > 0') - end_time = perf_counter() + self.page.timeout + end_time = perf_counter() + self.owner.timeout while not self.run_js(js) and perf_counter() < end_time: sleep(.1) if scroll_to_center: @@ -598,8 +599,8 @@ class ChromiumElement(DrissionElement): if not name: name = f'{self.tag}.jpg' - return self.page._get_screenshot(path, name, as_bytes=as_bytes, as_base64=as_base64, full_page=False, - left_top=left_top, right_bottom=right_bottom, ele=self) + return self.owner._get_screenshot(path, name, as_bytes=as_bytes, as_base64=as_base64, full_page=False, + left_top=left_top, right_bottom=right_bottom, ele=self) def input(self, vals, clear=True, by_js=False): """输入文本或组合键,也可用于输入文件路径到input元素(路径间用\n间隔) @@ -625,7 +626,7 @@ class ChromiumElement(DrissionElement): else: self._input_focus() - input_text_or_keys(self.page, vals) + input_text_or_keys(self.owner, vals) def clear(self, by_js=False): """清空元素文本 @@ -643,14 +644,14 @@ class ChromiumElement(DrissionElement): def _input_focus(self): """输入前使元素获取焦点""" try: - self.page.run_cdp('DOM.focus', backendNodeId=self._backend_id) + self.owner.run_cdp('DOM.focus', backendNodeId=self._backend_id) except Exception: self.click(by_js=None) def focus(self): """使元素获取焦点""" try: - self.page.run_cdp('DOM.focus', backendNodeId=self._backend_id) + self.owner.run_cdp('DOM.focus', backendNodeId=self._backend_id) except Exception: self.run_js('this.focus();') @@ -660,9 +661,9 @@ class ChromiumElement(DrissionElement): :param offset_y: 相对元素左上角坐标的y轴偏移量 :return: None """ - self.page.scroll.to_see(self) + self.owner.scroll.to_see(self) x, y = offset_scroll(self, offset_x, offset_y) - self.page.run_cdp('Input.dispatchMouseEvent', type='mouseMoved', x=x, y=y, _ignore=AlertExistsError) + self.owner.run_cdp('Input.dispatchMouseEvent', type='mouseMoved', x=x, y=y, _ignore=AlertExistsError) def drag(self, offset_x=0, offset_y=0, duration=.5): """拖拽当前元素到相对位置 @@ -687,7 +688,7 @@ class ChromiumElement(DrissionElement): elif not isinstance(ele_or_loc, (list, tuple)): raise TypeError('需要ChromiumElement对象或坐标。') - self.page.actions.hold(self).move_to(ele_or_loc, duration=duration).release() + self.owner.actions.hold(self).move_to(ele_or_loc, duration=duration).release() def _get_obj_id(self, node_id=None, backend_id=None): """根据传入node id或backend id获取js中的object id @@ -696,9 +697,9 @@ class ChromiumElement(DrissionElement): :return: js中的object id """ if node_id: - return self.page.run_cdp('DOM.resolveNode', nodeId=node_id)['object']['objectId'] + return self.owner.run_cdp('DOM.resolveNode', nodeId=node_id)['object']['objectId'] else: - return self.page.run_cdp('DOM.resolveNode', backendNodeId=backend_id)['object']['objectId'] + return self.owner.run_cdp('DOM.resolveNode', backendNodeId=backend_id)['object']['objectId'] def _get_node_id(self, obj_id=None, backend_id=None): """根据传入object id或backend id获取cdp中的node id @@ -707,9 +708,9 @@ class ChromiumElement(DrissionElement): :return: cdp中的node id """ if obj_id: - return self.page.run_cdp('DOM.requestNode', objectId=obj_id)['nodeId'] + return self.owner.run_cdp('DOM.requestNode', objectId=obj_id)['nodeId'] else: - n = self.page.run_cdp('DOM.describeNode', backendNodeId=backend_id)['node'] + n = self.owner.run_cdp('DOM.describeNode', backendNodeId=backend_id)['node'] self._tag = n['localName'] return n['nodeId'] @@ -718,7 +719,7 @@ class ChromiumElement(DrissionElement): :param node_id: :return: backend id """ - n = self.page.run_cdp('DOM.describeNode', nodeId=node_id)['node'] + n = self.owner.run_cdp('DOM.describeNode', nodeId=node_id)['node'] self._tag = n['localName'] return n['backendNodeId'] @@ -770,7 +771,7 @@ class ChromiumElement(DrissionElement): if isinstance(files, str): files = files.split('\n') files = [str(Path(i).absolute()) for i in files] - self.page.run_cdp('DOM.setFileInputFiles', files=files, backendNodeId=self._backend_id) + self.owner.run_cdp('DOM.setFileInputFiles', files=files, backendNodeId=self._backend_id) class ShadowRoot(BaseElement): @@ -782,7 +783,8 @@ class ShadowRoot(BaseElement): :param obj_id: js中的object id :param backend_id: cdp中的backend id """ - super().__init__(parent_ele.page) + super().__init__(parent_ele.owner) + self.tab = self.owner.tab self.parent_ele = parent_ele if backend_id: self._backend_id = backend_id @@ -841,7 +843,7 @@ class ShadowRoot(BaseElement): :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) + return run_js(self, script, as_expr, self.owner.timeouts.script if timeout is None else timeout, args) def run_async_js(self, script, *args, as_expr=False, timeout=None): """以异步方式执行js代码 @@ -853,7 +855,7 @@ class ShadowRoot(BaseElement): """ from threading import Thread Thread(target=run_js, args=(self, script, as_expr, - self.page.timeouts.script if timeout is None else timeout, args)).start() + self.owner.timeouts.script if timeout is None else timeout, args)).start() def parent(self, level_or_loc=1, index=1): """返回上面某一级父元素,可指定层数或用查询语法定位 @@ -899,7 +901,7 @@ class ShadowRoot(BaseElement): if Settings.raise_when_ele_not_found: raise ElementNotFoundError(None, 'child()', {'locator': locator, 'index': index}) else: - return NoneElement(self.page, 'child()', {'locator': locator, 'index': index}) + return NoneElement(self.owner, 'child()', {'locator': locator, 'index': index}) def next(self, locator='', index=1): """返回当前元素后面一个符合条件的同级元素,可用查询语法筛选,可指定返回筛选结果的第几个 @@ -920,7 +922,7 @@ class ShadowRoot(BaseElement): if Settings.raise_when_ele_not_found: raise ElementNotFoundError(None, 'next()', {'locator': locator, 'index': index}) else: - return NoneElement(self.page, 'next()', {'locator': locator, 'index': index}) + return NoneElement(self.owner, 'next()', {'locator': locator, 'index': index}) def before(self, locator='', index=1): """返回文档中当前元素前面符合条件的一个元素,可用查询语法筛选,可指定返回筛选结果的第几个 @@ -942,7 +944,7 @@ class ShadowRoot(BaseElement): if Settings.raise_when_ele_not_found: raise ElementNotFoundError(None, 'before()', {'locator': locator, 'index': index}) else: - return NoneElement(self.page, 'before()', {'locator': locator, 'index': index}) + return NoneElement(self.owner, 'before()', {'locator': locator, 'index': index}) def after(self, locator='', index=1): """返回文档中此当前元素后面符合条件的一个元素,可用查询语法筛选,可指定返回筛选结果的第几个 @@ -957,7 +959,7 @@ class ShadowRoot(BaseElement): if Settings.raise_when_ele_not_found: raise ElementNotFoundError(None, 'after()', {'locator': locator, 'index': index}) else: - return NoneElement(self.page, 'after()', {'locator': locator, 'index': index}) + return NoneElement(self.owner, 'after()', {'locator': locator, 'index': index}) def children(self, locator=''): """返回当前元素符合条件的直接子元素或节点组成的列表,可用查询语法筛选 @@ -1065,14 +1067,15 @@ class ShadowRoot(BaseElement): def do_find(): if loc[0] == 'css selector': if index == 1: - nod_id = self.page.run_cdp('DOM.querySelector', nodeId=self._node_id, selector=loc[1])['nodeId'] + nod_id = self.owner.run_cdp('DOM.querySelector', nodeId=self._node_id, selector=loc[1])['nodeId'] if nod_id: - r = make_chromium_eles(self.page, _ids=nod_id, is_obj_id=False) + r = make_chromium_eles(self.owner, _ids=nod_id, is_obj_id=False) 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'] - r = make_chromium_eles(self.page, _ids=nod_ids, index=index, is_obj_id=False) + nod_ids = self.owner.run_cdp('DOM.querySelectorAll', + nodeId=self._node_id, selector=loc[1])['nodeId'] + r = make_chromium_eles(self.owner, _ids=nod_ids, index=index, is_obj_id=False) return None if r is False else r else: @@ -1083,21 +1086,21 @@ class ShadowRoot(BaseElement): css = [i.css_path[61:] for i in eles] if index is not None: try: - node_id = self.page.run_cdp('DOM.querySelector', nodeId=self._node_id, - selector=css[index - 1])['nodeId'] + node_id = self.owner.run_cdp('DOM.querySelector', nodeId=self._node_id, + selector=css[index - 1])['nodeId'] except IndexError: return None - r = make_chromium_eles(self.page, _ids=node_id, is_obj_id=False) + r = make_chromium_eles(self.owner, _ids=node_id, is_obj_id=False) return None if r is False else r else: - node_ids = [self.page.run_cdp('DOM.querySelector', nodeId=self._node_id, selector=i)['nodeId'] + node_ids = [self.owner.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, _ids=node_ids, index=index, is_obj_id=False) + r = make_chromium_eles(self.owner, _ids=node_ids, index=index, is_obj_id=False) return None if r is False else r - timeout = timeout if timeout is not None else self.page.timeout + timeout = timeout if timeout is not None else self.owner.timeout end_time = perf_counter() + timeout result = do_find() while result is None and perf_counter() <= end_time: @@ -1106,19 +1109,19 @@ class ShadowRoot(BaseElement): if result: return result - return NoneElement(self.page) if index is not None else [] + return NoneElement(self.owner) if index is not None else [] def _get_node_id(self, obj_id): """返回元素node id""" - return self.page.run_cdp('DOM.requestNode', objectId=obj_id)['nodeId'] + return self.owner.run_cdp('DOM.requestNode', objectId=obj_id)['nodeId'] def _get_obj_id(self, back_id): """返回元素object id""" - return self.page.run_cdp('DOM.resolveNode', backendNodeId=back_id)['object']['objectId'] + return self.owner.run_cdp('DOM.resolveNode', backendNodeId=back_id)['object']['objectId'] def _get_backend_id(self, node_id): """返回元素object id""" - r = self.page.run_cdp('DOM.describeNode', nodeId=node_id)['node'] + r = self.owner.run_cdp('DOM.describeNode', nodeId=node_id)['node'] self._tag = r['localName'].lower() return r['backendNodeId'] @@ -1145,7 +1148,7 @@ def find_in_chromium_ele(ele, locator, index=1, timeout=None, relative=True): loc_str = f'{ele.css_path}{loc[1]}' loc = loc[0], loc_str - timeout = timeout if timeout is not None else ele.page.timeout + timeout = timeout if timeout is not None else ele.owner.timeout # ---------------执行查找----------------- if loc[0] == 'xpath': @@ -1167,18 +1170,18 @@ def find_by_xpath(ele, xpath, index, timeout, relative=True): type_txt = '9' if index == 1 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) - ele.page.wait.doc_loaded() + ele.owner.wait.doc_loaded() def do_find(): - res = ele.page.run_cdp('Runtime.callFunctionOn', functionDeclaration=js, objectId=ele._obj_id, - returnByValue=False, awaitPromise=True, userGesture=True) + res = ele.owner.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) + res = ele.owner.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}') @@ -1187,14 +1190,14 @@ def find_by_xpath(ele, xpath, index, timeout, relative=True): return None if index == 1: - r = make_chromium_eles(ele.page, _ids=res['result']['objectId'], is_obj_id=True) + r = make_chromium_eles(ele.owner, _ids=res['result']['objectId'], is_obj_id=True) return None if r is False else r else: - res = ele.page.run_cdp('Runtime.getProperties', objectId=res['result']['objectId'], - ownProperties=True)['result'][:-1] + res = ele.owner.run_cdp('Runtime.getProperties', objectId=res['result']['objectId'], + ownProperties=True)['result'][:-1] if index is None: - r = [make_chromium_eles(ele.page, _ids=i['value']['objectId'], is_obj_id=True) + r = [make_chromium_eles(ele.owner, _ids=i['value']['objectId'], is_obj_id=True) if i['value']['type'] == 'object' else i['value']['value'] for i in res] return None if False in r else r @@ -1206,7 +1209,7 @@ def find_by_xpath(ele, xpath, index, timeout, relative=True): index1 = eles_count + index + 1 if index < 0 else index res = res[index1 - 1] if res['value']['type'] == 'object': - r = make_chromium_eles(ele.page, _ids=res['value']['objectId'], is_obj_id=True) + r = make_chromium_eles(ele.owner, _ids=res['value']['objectId'], is_obj_id=True) else: r = res['value']['value'] return None if r is False else r @@ -1219,7 +1222,7 @@ def find_by_xpath(ele, xpath, index, timeout, relative=True): if result: return result - return NoneElement(ele.page) if index is not None else [] + return NoneElement(ele.owner) if index is not None else [] def find_by_css(ele, selector, index, timeout): @@ -1235,11 +1238,11 @@ def find_by_css(ele, selector, index, timeout): node_txt = 'this.contentDocument' if ele.tag in ('iframe', 'frame', 'shadow-root') else 'this' js = f'function(){{return {node_txt}.querySelector{find_all}("{selector}");}}' - ele.page.wait.doc_loaded() + ele.owner.wait.doc_loaded() def do_find(): - res = ele.page.run_cdp('Runtime.callFunctionOn', functionDeclaration=js, objectId=ele._obj_id, - returnByValue=False, awaitPromise=True, userGesture=True) + res = ele.owner.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}') @@ -1247,14 +1250,14 @@ def find_by_css(ele, selector, index, timeout): return None if index == 1: - r = make_chromium_eles(ele.page, _ids=res['result']['objectId'], is_obj_id=True) + r = make_chromium_eles(ele.owner, _ids=res['result']['objectId'], is_obj_id=True) return None if r is False else r else: - obj_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, _ids=obj_ids, index=index, is_obj_id=True) + obj_ids = [i['value']['objectId'] for i in ele.owner.run_cdp('Runtime.getProperties', + objectId=res['result']['objectId'], + ownProperties=True)['result'][:-1]] + r = make_chromium_eles(ele.owner, _ids=obj_ids, index=index, is_obj_id=True) return None if r is False else r end_time = perf_counter() + timeout @@ -1265,7 +1268,7 @@ def find_by_css(ele, selector, index, timeout): if result: return result - return NoneElement(ele.page) if index is not None else [] + return NoneElement(ele.owner) if index is not None else [] def make_chromium_eles(page, _ids, index=1, is_obj_id=True): @@ -1393,7 +1396,7 @@ def run_js(page_or_ele, script, as_expr, timeout, args=None): """ if isinstance(page_or_ele, (ChromiumElement, ShadowRoot)): is_page = False - page = page_or_ele.page + page = page_or_ele.owner obj_id = page_or_ele._obj_id else: is_page = True diff --git a/DrissionPage/_elements/chromium_element.pyi b/DrissionPage/_elements/chromium_element.pyi index 69f7bb4..b1defa4 100644 --- a/DrissionPage/_elements/chromium_element.pyi +++ b/DrissionPage/_elements/chromium_element.pyi @@ -28,9 +28,12 @@ PIC_TYPE = Literal['jpg', 'jpeg', 'png', 'webp', True] class ChromiumElement(DrissionElement): - def __init__(self, page: ChromiumBase, node_id: int = None, obj_id: str = None, backend_id: int = None): + def __init__(self, owner: ChromiumBase, node_id: int = None, obj_id: str = None, backend_id: int = None): self._tag: str = ... + # self.page: Union[ChromiumPage, WebPage] = ... + self.owner: ChromiumBase = ... self.page: Union[ChromiumPage, WebPage] = ... + self.tab: Union[ChromiumPage, ChromiumTab] = ... self._node_id: int = ... self._obj_id: str = ... self._backend_id: int = ... @@ -226,7 +229,7 @@ class ChromiumElement(DrissionElement): def drag(self, offset_x: int = 0, offset_y: int = 0, duration: float = 0.5) -> None: ... - def drag_to(self, ele_or_loc: Union[tuple, ChromiumElement], duration: float = 0.5) -> None: ... + def drag_to(self, ele_or_loc: Union[Tuple[int, int], str, ChromiumElement], duration: float = 0.5) -> None: ... def _get_obj_id(self, node_id: int = None, backend_id: int = None) -> str: ... @@ -240,10 +243,13 @@ class ChromiumElement(DrissionElement): class ShadowRoot(BaseElement): def __init__(self, parent_ele: ChromiumElement, obj_id: str = None, backend_id: int = None): + # self.page: Union[ChromiumPage, WebPage] = ... + self.owner: ChromiumBase = ... + self.tab: Union[ChromiumPage, ChromiumTab] = ... self._obj_id: str = ... self._node_id: int = ... self._backend_id: int = ... - self.page: ChromiumPage = ... + # self.page: ChromiumPage = ... self.parent_ele: ChromiumElement = ... self._states: ShadowRootStates = ... diff --git a/DrissionPage/_elements/session_element.py b/DrissionPage/_elements/session_element.py index 208c8e4..40a265c 100644 --- a/DrissionPage/_elements/session_element.py +++ b/DrissionPage/_elements/session_element.py @@ -20,12 +20,12 @@ from .._functions.web import get_ele_txt, make_absolute_link class SessionElement(DrissionElement): """session模式的元素对象,包装了一个lxml的Element对象,并封装了常用功能""" - def __init__(self, ele, page=None): + def __init__(self, ele, owner=None): """初始化对象 :param ele: 被包装的HtmlElement元素 - :param page: 元素所在页面对象,如果是从 html 文本生成的元素,则为 None + :param owner: 元素所在页面对象,如果是从 html 文本生成的元素,则为 None """ - super().__init__(page) + super().__init__(owner) self._inner_ele = ele self._type = 'SessionElement' @@ -201,10 +201,10 @@ class SessionElement(DrissionElement): return link else: # 其它情况直接返回绝对url - return make_absolute_link(link, self.page.url) + return make_absolute_link(link, self.owner.url) elif name == 'src': - return make_absolute_link(self.inner_ele.get('src'), self.page.url) + return make_absolute_link(self.inner_ele.get('src'), self.owner.url) elif name == 'text': return self.text @@ -313,7 +313,7 @@ def make_session_ele(html_or_ele, loc=None, index=1): # SessionElement elif html_or_ele._type == 'SessionElement': - page = html_or_ele.page + page = html_or_ele.owner loc_str = loc[1] if loc[0] == 'xpath' and loc[1].lstrip().startswith('/'): @@ -323,8 +323,8 @@ def make_session_ele(html_or_ele, loc=None, index=1): # 若css以>开头,表示找元素的直接子元素,要用page以绝对路径才能找到 elif loc[0] == 'css selector' and loc[1].lstrip().startswith('>'): loc_str = f'{html_or_ele.css_path}{loc[1]}' - if html_or_ele.page: - html_or_ele = fromstring(html_or_ele.page.html) + if html_or_ele.owner: + html_or_ele = fromstring(html_or_ele.owner.html) else: # 接收html文本,无page的情况 html_or_ele = fromstring(html_or_ele('xpath:/ancestor::*').html) @@ -342,11 +342,11 @@ def make_session_ele(html_or_ele, loc=None, index=1): loc = loc[0], loc_str # 获取整个页面html再定位到当前元素,以实现查找上级元素 - page = html_or_ele.page + page = html_or_ele.owner xpath = html_or_ele.xpath # ChromiumElement,兼容传入的元素在iframe内的情况 - html = html_or_ele.page.run_cdp('DOM.getOuterHTML', objectId=html_or_ele._doc_id)['outerHTML'] \ - if html_or_ele._doc_id else html_or_ele.page.html + html = html_or_ele.owner.run_cdp('DOM.getOuterHTML', objectId=html_or_ele._doc_id)['outerHTML'] \ + if html_or_ele._doc_id else html_or_ele.owner.html html_or_ele = fromstring(html) html_or_ele = html_or_ele.xpath(xpath)[0] @@ -360,7 +360,7 @@ def make_session_ele(html_or_ele, loc=None, index=1): # ShadowRoot elif isinstance(html_or_ele, BaseElement): - page = html_or_ele.page + page = html_or_ele.owner html_or_ele = fromstring(html_or_ele.html) else: diff --git a/DrissionPage/_elements/session_element.pyi b/DrissionPage/_elements/session_element.pyi index 563beae..f6e4e00 100644 --- a/DrissionPage/_elements/session_element.pyi +++ b/DrissionPage/_elements/session_element.pyi @@ -18,8 +18,9 @@ from .._pages.session_page import SessionPage class SessionElement(DrissionElement): - def __init__(self, ele: HtmlElement, page: Union[SessionPage, None] = None): + def __init__(self, ele: HtmlElement, owner: Union[SessionPage, None] = None): self._inner_ele: HtmlElement = ... + self.owner: SessionPage = ... self.page: SessionPage = ... @property diff --git a/DrissionPage/_functions/browser.py b/DrissionPage/_functions/browser.py index 2fbc5c7..5075945 100644 --- a/DrissionPage/_functions/browser.py +++ b/DrissionPage/_functions/browser.py @@ -316,14 +316,20 @@ def get_chrome_path(ini_path): return None # -----------从注册表中获取-------------- - import winreg + from winreg import OpenKey, EnumValue, CloseKey, HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE, KEY_READ try: - key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, - r'SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\chrome.exe', - reserved=0, access=winreg.KEY_READ) - k = winreg.EnumValue(key, 0) - winreg.CloseKey(key) - + key = OpenKey(HKEY_CURRENT_USER, + r'SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\chrome.exe', + reserved=0, access=KEY_READ) + k = EnumValue(key, 0) + CloseKey(key) + if k[1]: + return k[1] + key = OpenKey(HKEY_LOCAL_MACHINE, + r'SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\chrome.exe', + reserved=0, access=KEY_READ) + k = EnumValue(key, 0) + CloseKey(key) return k[1] except FileNotFoundError: diff --git a/DrissionPage/_functions/tools.py b/DrissionPage/_functions/tools.py index 49b0bca..38640cf 100644 --- a/DrissionPage/_functions/tools.py +++ b/DrissionPage/_functions/tools.py @@ -12,8 +12,6 @@ from tempfile import gettempdir, TemporaryDirectory from threading import Lock from time import perf_counter -from psutil import process_iter, AccessDenied, NoSuchProcess, ZombieProcess, Process - from .._configs.options_manage import OptionsManager from ..errors import (ContextLostError, ElementLostError, CDPError, PageDisconnectedError, NoRectError, AlertExistsError, WrongURLError, StorageError, CookieFormatError, JavaScriptError) @@ -182,35 +180,6 @@ def wait_until(function, kwargs=None, timeout=10): raise TimeoutError -def stop_process_on_port(port, pid=None): - """强制关闭某个端口内的进程 - :param port: 端口号 - :param pid: 进程号 - :return: None - """ - if pid: - try: - for p in Process(pid).children(): - p.terminate() - Process(pid).terminate() - except: - pass - - for proc in process_iter(['pid', 'connections']): - try: - connections = proc.connections() - except (AccessDenied, NoSuchProcess): - continue - for conn in connections: - if conn.laddr.port == int(port): - try: - proc.terminate() - except (NoSuchProcess, AccessDenied, ZombieProcess): - pass - except Exception as e: - print(f"{proc.pid} {port}: {e}") - - def configs_to_here(save_name=None): """把默认ini文件复制到当前目录 :param save_name: 指定文件名,为None则命名为'dp_configs.ini' diff --git a/DrissionPage/_functions/tools.pyi b/DrissionPage/_functions/tools.pyi index f6b55c0..86d3527 100644 --- a/DrissionPage/_functions/tools.pyi +++ b/DrissionPage/_functions/tools.pyi @@ -42,9 +42,6 @@ def get_hwnds_from_pid(pid: Union[str, int], title: str) -> list: ... def wait_until(function: callable, kwargs: dict = None, timeout: float = 10): ... -def stop_process_on_port(port: Union[int, str], pid: int = None) -> None: ... - - def configs_to_here(file_name: Union[Path, str] = None) -> None: ... diff --git a/DrissionPage/_functions/web.py b/DrissionPage/_functions/web.py index 1f22d09..3ef7b1e 100644 --- a/DrissionPage/_functions/web.py +++ b/DrissionPage/_functions/web.py @@ -119,10 +119,10 @@ def offset_scroll(ele, offset_x, offset_y): cp_x, cp_y = ele.rect.click_point lx = loc_x + offset_x if offset_x else cp_x ly = loc_y + offset_y if offset_y else cp_y - if not location_in_viewport(ele.page, lx, ly): - clientWidth = ele.page.run_js('return document.body.clientWidth;') - clientHeight = ele.page.run_js('return document.body.clientHeight;') - ele.page.scroll.to_location(lx - clientWidth // 2, ly - clientHeight // 2) + if not location_in_viewport(ele.owner, lx, ly): + clientWidth = ele.owner.run_js('return document.body.clientWidth;') + clientHeight = ele.owner.run_js('return document.body.clientHeight;') + ele.owner.scroll.to_location(lx - clientWidth // 2, ly - clientHeight // 2) cl_x, cl_y = ele.rect.viewport_location ccp_x, ccp_y = ele.rect.viewport_click_point cx = cl_x + offset_x if offset_x else ccp_x diff --git a/DrissionPage/_pages/chromium_base.pyi b/DrissionPage/_pages/chromium_base.pyi index 91e4354..59be1c2 100644 --- a/DrissionPage/_pages/chromium_base.pyi +++ b/DrissionPage/_pages/chromium_base.pyi @@ -8,7 +8,8 @@ from pathlib import Path from typing import Union, Tuple, List, Any, Optional, Literal -from .chromium_tab import ChromiumTab +from .chromium_tab import ChromiumTab, WebPageTab +from .web_page import WebPage from .._base.base import BasePage from .._base.browser import Browser from .._base.driver import Driver @@ -35,6 +36,7 @@ class ChromiumBase(BasePage): timeout: float = None): self._browser: Browser = ... self._page: ChromiumPage = ... + self.tab: Union[ChromiumPage, ChromiumTab] = ... self.address: str = ... self._driver: Driver = ... self._frame_id: str = ... diff --git a/DrissionPage/_pages/chromium_frame.py b/DrissionPage/_pages/chromium_frame.py index 5544fe4..fef99d0 100644 --- a/DrissionPage/_pages/chromium_frame.py +++ b/DrissionPage/_pages/chromium_frame.py @@ -21,38 +21,38 @@ from ..errors import ContextLostError, ElementLostError, PageDisconnectedError, class ChromiumFrame(ChromiumBase): - def __init__(self, page, ele, info=None): + def __init__(self, owner, ele, info=None): """ - :param page: frame所在的页面对象 + :param owner: frame所在的页面对象 :param ele: frame所在元素 :param info: frame所在元素信息 """ - if page._type in ('ChromiumPage', 'WebPage'): - self._page = self._target_page = self.tab = page - self._browser = page.browser + if owner._type in ('ChromiumPage', 'WebPage'): + self._page = self._target_page = self.tab = owner + self._browser = owner.browser else: # Tab、Frame - self._page = page.page + self._page = owner.page self._browser = self._page.browser - self._target_page = page - self.tab = page.tab if page._type == 'ChromiumFrame' else page + self._target_page = owner + self.tab = owner.tab if owner._type == 'ChromiumFrame' else owner - self.address = page.address - self._tab_id = page.tab_id + self.address = owner.address + self._tab_id = owner.tab_id self._backend_id = ele._backend_id self._frame_ele = ele self._states = None self._reloading = False - node = info['node'] if not info else page.run_cdp('DOM.describeNode', backendNodeId=ele._backend_id)['node'] + node = info['node'] if not info else owner.run_cdp('DOM.describeNode', backendNodeId=ele._backend_id)['node'] self._frame_id = node['frameId'] if self._is_inner_frame(): self._is_diff_domain = False self.doc_ele = ChromiumElement(self._target_page, backend_id=node['contentDocument']['backendNodeId']) - super().__init__(page.address, page.tab_id, page.timeout) + super().__init__(owner.address, owner.tab_id, owner.timeout) else: self._is_diff_domain = True delattr(self, '_frame_id') - super().__init__(page.address, node['frameId'], page.timeout) + super().__init__(owner.address, node['frameId'], owner.timeout) obj_id = super().run_js('document;', as_expr=True)['objectId'] self.doc_ele = ChromiumElement(self, obj_id=obj_id) @@ -176,6 +176,7 @@ class ChromiumFrame(ChromiumBase): return True except: + raise return False finally: @@ -251,8 +252,14 @@ class ChromiumFrame(ChromiumBase): @property def page(self): + """返回所属Page对象""" return self._page + @property + def owner(self): + """返回所属页面对象""" + return self.frame_ele.owner + @property def frame_ele(self): """返回总页面上的frame元素""" diff --git a/DrissionPage/_pages/chromium_frame.pyi b/DrissionPage/_pages/chromium_frame.pyi index a98db83..7a4c772 100644 --- a/DrissionPage/_pages/chromium_frame.pyi +++ b/DrissionPage/_pages/chromium_frame.pyi @@ -10,7 +10,7 @@ from typing import Union, Tuple, List, Any, Optional from .chromium_base import ChromiumBase from .chromium_page import ChromiumPage -from .chromium_tab import ChromiumTab +from .chromium_tab import ChromiumTab, WebPageTab from .web_page import WebPage from .._elements.chromium_element import ChromiumElement from .._units.listener import FrameListener @@ -24,12 +24,12 @@ from .._units.waiter import FrameWaiter class ChromiumFrame(ChromiumBase): def __init__(self, - page: Union[ChromiumPage, WebPage, ChromiumTab, ChromiumFrame], + owner: Union[ChromiumPage, WebPage, ChromiumTab, ChromiumFrame], ele: ChromiumElement, info: dict = None): - self._page: ChromiumPage = ... self._target_page: ChromiumBase = ... - self.tab: ChromiumTab = ... + self._page: ChromiumPage = ... + self.tab: Union[ChromiumPage, ChromiumTab] = ... self._tab_id: str = ... self._set: ChromiumFrameSetter = ... self._frame_ele: ChromiumElement = ... @@ -68,6 +68,9 @@ class ChromiumFrame(ChromiumBase): @property def page(self) -> Union[ChromiumPage, WebPage]: ... + @property + def owner(self) -> ChromiumBase: ... + @property def frame_ele(self) -> ChromiumElement: ... diff --git a/DrissionPage/_pages/chromium_page.py b/DrissionPage/_pages/chromium_page.py index a57f531..c6b10fc 100644 --- a/DrissionPage/_pages/chromium_page.py +++ b/DrissionPage/_pages/chromium_page.py @@ -59,6 +59,7 @@ class ChromiumPage(ChromiumBase): self._created = True self._page = self + self.tab = self self._run_browser() super().__init__(self.address, tab_id) self._type = 'ChromiumPage' diff --git a/DrissionPage/_pages/chromium_page.pyi b/DrissionPage/_pages/chromium_page.pyi index 8b74035..4a031ac 100644 --- a/DrissionPage/_pages/chromium_page.pyi +++ b/DrissionPage/_pages/chromium_page.pyi @@ -30,6 +30,7 @@ class ChromiumPage(ChromiumBase): addr_or_opts: Union[str, int, ChromiumOptions] = None, tab_id: str = None, timeout: float = None): + self.tab: ChromiumPage = ... self._chromium_options: ChromiumOptions = ... self._browser: Browser = ... self._browser_id: str = ... diff --git a/DrissionPage/_pages/chromium_tab.py b/DrissionPage/_pages/chromium_tab.py index 6baeb61..23de4be 100644 --- a/DrissionPage/_pages/chromium_tab.py +++ b/DrissionPage/_pages/chromium_tab.py @@ -46,6 +46,7 @@ class ChromiumTab(ChromiumBase): self._created = True self._page = page + self.tab = self self._browser = page.browser super().__init__(page.address, tab_id, page.timeout) self._rect = None @@ -398,4 +399,4 @@ class WebPageTab(SessionPage, ChromiumTab, BasePage): # --------即将废弃------- def get_cookies(self, as_dict=False, all_domains=False, all_info=False): - return self.cookies(as_dict=as_dict, all_domains=all_domains, all_info=all_info) \ No newline at end of file + return self.cookies(as_dict=as_dict, all_domains=all_domains, all_info=all_info) diff --git a/DrissionPage/_pages/session_page.py b/DrissionPage/_pages/session_page.py index 0079aed..1bc7c27 100644 --- a/DrissionPage/_pages/session_page.py +++ b/DrissionPage/_pages/session_page.py @@ -36,6 +36,7 @@ class SessionPage(BasePage): self._set = None self._encoding = None self._type = 'SessionPage' + self._page = self self._s_set_start_options(session_or_options) self._s_set_runtime_settings() self._create_session() diff --git a/DrissionPage/_pages/session_page.pyi b/DrissionPage/_pages/session_page.pyi index ca8b6b2..7a3ac69 100644 --- a/DrissionPage/_pages/session_page.pyi +++ b/DrissionPage/_pages/session_page.pyi @@ -32,6 +32,7 @@ class SessionPage(BasePage): self.retry_interval: float = ... self._set: SessionPageSetter = ... self._encoding: str = ... + self._page: SessionPage = ... def _s_set_start_options(self, session_or_options: Union[Session, SessionOptions]) -> None: ... @@ -113,9 +114,9 @@ class SessionPage(BasePage): -> Union[SessionElement, List[SessionElement]]: ... def cookies(self, - as_dict: bool = False, - all_domains: bool = False, - all_info: bool = False) -> Union[dict, list]: ... + as_dict: bool = False, + all_domains: bool = False, + all_info: bool = False) -> Union[dict, list]: ... # ----------------session独有属性和方法----------------------- @property diff --git a/DrissionPage/_units/actions.py b/DrissionPage/_units/actions.py index c66787c..f99ee70 100644 --- a/DrissionPage/_units/actions.py +++ b/DrissionPage/_units/actions.py @@ -15,12 +15,12 @@ from .._functions.web import location_in_viewport class Actions: """用于实现动作链的类""" - def __init__(self, page): + def __init__(self, owner): """ - :param page: ChromiumBase对象 + :param owner: ChromiumBase对象 """ - self.page = page - self._dr = page.driver + self.owner = owner + self._dr = owner.driver self.modifier = 0 # 修饰符,Alt=1, Ctrl=2, Meta/Command=4, Shift=8 self.curr_x = 0 # 视口坐标 self.curr_y = 0 @@ -40,23 +40,23 @@ class Actions: lx = ele_or_loc[0] + offset_x ly = ele_or_loc[1] + offset_y elif isinstance(ele_or_loc, str) or ele_or_loc._type == 'ChromiumElement': - ele_or_loc = self.page(ele_or_loc) - self.page.scroll.to_see(ele_or_loc) + ele_or_loc = self.owner(ele_or_loc) + self.owner.scroll.to_see(ele_or_loc) x, y = ele_or_loc.rect.location if offset_x or offset_y else ele_or_loc.rect.midpoint lx = x + offset_x ly = y + offset_y else: raise TypeError('ele_or_loc参数只能接受坐标(x, y)或ChromiumElement对象。') - if not location_in_viewport(self.page, lx, ly): + if not location_in_viewport(self.owner, lx, ly): # 把坐标滚动到页面中间 - clientWidth = self.page.run_js('return document.body.clientWidth;') - clientHeight = self.page.run_js('return document.body.clientHeight;') - self.page.scroll.to_location(lx - clientWidth // 2, ly - clientHeight // 2) + clientWidth = self.owner.run_js('return document.body.clientWidth;') + clientHeight = self.owner.run_js('return document.body.clientHeight;') + self.owner.scroll.to_location(lx - clientWidth // 2, ly - clientHeight // 2) # 这样设计为了应付那些不随滚动条滚动的元素 if is_loc: - cx, cy = location_to_client(self.page, lx, ly) + cx, cy = location_to_client(self.owner, lx, ly) else: x, y = ele_or_loc.rect.viewport_location if offset_x or offset_y \ else ele_or_loc.rect.viewport_midpoint @@ -255,7 +255,7 @@ class Actions: data = self._get_key_data(key, 'keyDown') data['_ignore'] = AlertExistsError - self.page.run_cdp('Input.dispatchKeyEvent', **data) + self.owner.run_cdp('Input.dispatchKeyEvent', **data) return self def key_up(self, key): @@ -270,7 +270,7 @@ class Actions: data = self._get_key_data(key, 'keyUp') data['_ignore'] = AlertExistsError - self.page.run_cdp('Input.dispatchKeyEvent', **data) + self.owner.run_cdp('Input.dispatchKeyEvent', **data) return self def type(self, keys): @@ -295,7 +295,7 @@ class Actions: :param text: 文本值或按键组合 :return: self """ - input_text_or_keys(self.page, text) + input_text_or_keys(self.owner, text) return self def wait(self, second): diff --git a/DrissionPage/_units/actions.pyi b/DrissionPage/_units/actions.pyi index 55f863c..c070799 100644 --- a/DrissionPage/_units/actions.pyi +++ b/DrissionPage/_units/actions.pyi @@ -44,8 +44,8 @@ KEYS = Literal['NULL', 'CANCEL', 'HELP', 'BACKSPACE', 'BACK_SPACE', 'meta', class Actions: - def __init__(self, page: ChromiumBase): - self.page: ChromiumBase = ... + def __init__(self, owner: ChromiumBase): + self.owner: ChromiumBase = ... self._dr: Driver = ... self.modifier: int = ... self.curr_x: int = ... diff --git a/DrissionPage/_units/clicker.py b/DrissionPage/_units/clicker.py index a8b5513..40113ed 100644 --- a/DrissionPage/_units/clicker.py +++ b/DrissionPage/_units/clicker.py @@ -45,7 +45,7 @@ class Clicker(object): if not by_js: # 模拟点击 can_click = False - timeout = self._ele.page.timeout if timeout is None else timeout + timeout = self._ele.owner.timeout if timeout is None else timeout rect = None if timeout == 0: try: @@ -85,8 +85,8 @@ class Clicker(object): x = rect[1][0] - (rect[1][0] - rect[0][0]) / 2 y = rect[0][0] + 3 try: - r = self._ele.page.run_cdp('DOM.getNodeForLocation', x=x, y=y, includeUserAgentShadowDOM=True, - ignorePointerEventsNone=True) + r = self._ele.owner.run_cdp('DOM.getNodeForLocation', x=x, y=y, includeUserAgentShadowDOM=True, + ignorePointerEventsNone=True) if r['backendNodeId'] != self._ele._backend_id: vx, vy = self._ele.rect.viewport_midpoint else: @@ -107,13 +107,13 @@ class Clicker(object): def right(self): """右键单击""" - self._ele.page.scroll.to_see(self._ele) + self._ele.owner.scroll.to_see(self._ele) x, y = self._ele.rect.viewport_click_point self._click(x, y, 'right') def middle(self): """中键单击""" - self._ele.page.scroll.to_see(self._ele) + self._ele.owner.scroll.to_see(self._ele) x, y = self._ele.rect.viewport_click_point self._click(x, y, 'middle') @@ -125,7 +125,7 @@ class Clicker(object): :param count: 点击次数 :return: None """ - self._ele.page.scroll.to_see(self._ele) + self._ele.owner.scroll.to_see(self._ele) if offset_x is None and offset_y is None: w, h = self._ele.rect.size offset_x = w // 2 @@ -140,27 +140,28 @@ class Clicker(object): """ self.at(count=times) - def to_download(self, save_path=None, rename=None, suffix=None, new_tab=False, by_js=False): + def to_download(self, save_path=None, rename=None, suffix=None, new_tab=False, by_js=False, timeout=None): """点击触发下载 :param save_path: 保存路径,为None保存在原来设置的,如未设置保存到当前路径 :param rename: 重命名文件名 :param suffix: 指定文件后缀 :param new_tab: 该下载是否在新tab中触发 :param by_js: 是否用js方式点击,逻辑与click()一致 + :param timeout: 等待下载触发的超时时间,为None则使用页面对象设置 :return: DownloadMission对象 """ if save_path: - self._ele.page.set.download_path(save_path) - elif not self._ele.page._page._browser._dl_mgr._running: + self._ele.owner.tab.set.download_path(save_path) + elif not self._ele.page._browser._dl_mgr._running: self._ele.page.set.download_path('.') if rename or suffix: - self._ele.page.set.download_file_name(rename, suffix) + self._ele.owner.tab.set.download_file_name(rename, suffix) - tab = self._ele.page._page if new_tab else self._ele.page + tab = self._ele.page if new_tab else self._ele.owner - self._ele.click(by_js=by_js) - return tab.wait.download_begin() + self.left(by_js=by_js) + return tab.wait.download_begin(timeout=timeout) def to_upload(self, file_paths, by_js=False): """触发上传文件选择框并自动填入指定路径 @@ -168,9 +169,20 @@ class Clicker(object): :param by_js: 是否用js方式点击,逻辑与click()一致 :return: None """ - self._ele.page.set.upload_files(file_paths) - self._ele.click(by_js=by_js) - self._ele.page.wait.upload_paths_inputted() + self._ele.owner.set.upload_files(file_paths) + self.left(by_js=by_js) + self._ele.owner.wait.upload_paths_inputted() + + def for_new_tab(self, by_js=False): + """点击后等待新tab出现并返回其对象 + :param by_js: 是否使用js点击,逻辑与click()一致 + :return: 新标签页对象,如果没有等到新标签页出现则抛出异常 + """ + self.left(by_js=by_js) + tid = self._ele.page.wait.new_tab() + if not tid: + raise RuntimeError('没有出现新标签页。') + return self._ele.page.get_tab(tid) def _click(self, client_x, client_y, button='left', count=1): """实施点击 @@ -180,11 +192,11 @@ class Clicker(object): :param count: 点击次数 :return: None """ - self._ele.page.run_cdp('Input.dispatchMouseEvent', type='mousePressed', x=client_x, - y=client_y, button=button, clickCount=count, _ignore=AlertExistsError) + self._ele.owner.run_cdp('Input.dispatchMouseEvent', type='mousePressed', x=client_x, + y=client_y, button=button, clickCount=count, _ignore=AlertExistsError) # sleep(.05) - self._ele.page.run_cdp('Input.dispatchMouseEvent', type='mouseReleased', x=client_x, - y=client_y, button=button, _ignore=AlertExistsError) + self._ele.owner.run_cdp('Input.dispatchMouseEvent', type='mouseReleased', x=client_x, + y=client_y, button=button, _ignore=AlertExistsError) # -------------即将废弃-------------- diff --git a/DrissionPage/_units/clicker.pyi b/DrissionPage/_units/clicker.pyi index 7aa346d..e6ca30f 100644 --- a/DrissionPage/_units/clicker.pyi +++ b/DrissionPage/_units/clicker.pyi @@ -10,6 +10,7 @@ from typing import Union from .downloader import DownloadMission from .._elements.chromium_element import ChromiumElement +from .._pages.chromium_tab import WebPageTab, ChromiumTab class Clicker(object): @@ -37,8 +38,11 @@ class Clicker(object): rename: str = None, suffix: str = None, new_tab: bool = False, - by_js: bool = False) -> DownloadMission: ... + by_js: bool = False, + timeout:float=None) -> DownloadMission: ... - def to_upload(self, file_paths: Union[str, list, tuple], by_js: bool = False) -> None: ... + def to_upload(self, file_paths: Union[str, Path, list, tuple], by_js: bool = False) -> None: ... + + def for_new_tab(self, by_js:bool=False)->Union[ChromiumTab, WebPageTab]:... def _click(self, client_x: float, client_y: float, button: str = 'left', count: int = 1) -> None: ... diff --git a/DrissionPage/_units/rect.py b/DrissionPage/_units/rect.py index 3011ec6..55baa6b 100644 --- a/DrissionPage/_units/rect.py +++ b/DrissionPage/_units/rect.py @@ -18,7 +18,7 @@ class ElementRect(object): def corners(self): """返回元素四个角坐标,顺序:坐上、右上、右下、左下,没有大小的元素抛出NoRectError""" vr = self._get_viewport_rect('border') - r = self._ele.page.run_cdp_loaded('Page.getLayoutMetrics')['visualViewport'] + r = self._ele.owner.run_cdp_loaded('Page.getLayoutMetrics')['visualViewport'] sx = r['pageX'] sy = r['pageY'] return [(vr[0] + sx, vr[1] + sy), (vr[2] + sx, vr[3] + sy), (vr[4] + sx, vr[5] + sy), (vr[6] + sx, vr[7] + sy)] @@ -32,8 +32,8 @@ class ElementRect(object): @property def size(self): """返回元素大小,格式(宽, 高)""" - border = self._ele.page.run_cdp('DOM.getBoxModel', backendNodeId=self._ele._backend_id, - nodeId=self._ele._node_id, objectId=self._ele._obj_id)['model']['border'] + border = self._ele.owner.run_cdp('DOM.getBoxModel', backendNodeId=self._ele._backend_id, + nodeId=self._ele._node_id, objectId=self._ele._obj_id)['model']['border'] return border[2] - border[0], border[5] - border[1] @property @@ -75,25 +75,25 @@ class ElementRect(object): @property def screen_location(self): """返回元素左上角在屏幕上坐标,左上角为(0, 0)""" - vx, vy = self._ele.page.rect.viewport_location + vx, vy = self._ele.owner.rect.viewport_location ex, ey = self.viewport_location - pr = self._ele.page.run_js('return window.devicePixelRatio;') + pr = self._ele.owner.run_js('return window.devicePixelRatio;') return (vx + ex) * pr, (ey + vy) * pr @property def screen_midpoint(self): """返回元素中点在屏幕上坐标,左上角为(0, 0)""" - vx, vy = self._ele.page.rect.viewport_location + vx, vy = self._ele.owner.rect.viewport_location ex, ey = self.viewport_midpoint - pr = self._ele.page.run_js('return window.devicePixelRatio;') + pr = self._ele.owner.run_js('return window.devicePixelRatio;') return (vx + ex) * pr, (ey + vy) * pr @property def screen_click_point(self): """返回元素中点在屏幕上坐标,左上角为(0, 0)""" - vx, vy = self._ele.page.rect.viewport_location + vx, vy = self._ele.owner.rect.viewport_location ex, ey = self.viewport_click_point - pr = self._ele.page.run_js('return window.devicePixelRatio;') + pr = self._ele.owner.run_js('return window.devicePixelRatio;') return (vx + ex) * pr, (ey + vy) * pr def _get_viewport_rect(self, quad): @@ -101,12 +101,12 @@ class ElementRect(object): :param quad: 方框类型,margin border padding :return: 四个角坐标 """ - return self._ele.page.run_cdp('DOM.getBoxModel', backendNodeId=self._ele._backend_id, - nodeId=self._ele._node_id, objectId=self._ele._obj_id)['model'][quad] + return self._ele.owner.run_cdp('DOM.getBoxModel', backendNodeId=self._ele._backend_id, + nodeId=self._ele._node_id, objectId=self._ele._obj_id)['model'][quad] def _get_page_coord(self, x, y): """根据视口坐标获取绝对坐标""" - r = self._ele.page.run_cdp_loaded('Page.getLayoutMetrics')['visualViewport'] + r = self._ele.owner.run_cdp_loaded('Page.getLayoutMetrics')['visualViewport'] sx = r['pageX'] sy = r['pageY'] return x + sx, y + sy diff --git a/DrissionPage/_units/scroller.py b/DrissionPage/_units/scroller.py index d59e98a..cacdfee 100644 --- a/DrissionPage/_units/scroller.py +++ b/DrissionPage/_units/scroller.py @@ -87,7 +87,7 @@ class Scroller(object): if not self._wait_complete: return - page = self._driver.page if self._driver._type == 'ChromiumElement' else self._driver + page = self._driver.owner if self._driver._type == 'ChromiumElement' else self._driver r = page.run_cdp('Page.getLayoutMetrics') x = r['layoutViewport']['pageX'] y = r['layoutViewport']['pageY'] @@ -112,11 +112,11 @@ class ElementScroller(Scroller): :param center: 是否尽量滚动到页面正中,为None时如果被遮挡,则滚动到页面正中 :return: None """ - self._driver.page.scroll.to_see(self._driver, center=center) + self._driver.owner.scroll.to_see(self._driver, center=center) def to_center(self): """元素尽量滚动到视口中间""" - self._driver.page.scroll.to_see(self._driver, center=True) + self._driver.owner.scroll.to_see(self._driver, center=True) class PageScroller(Scroller): diff --git a/DrissionPage/_units/selector.py b/DrissionPage/_units/selector.py index 3f9b08e..be3e29d 100644 --- a/DrissionPage/_units/selector.py +++ b/DrissionPage/_units/selector.py @@ -27,7 +27,7 @@ class SelectElement(object): :return: None """ para_type = 'index' if isinstance(text_or_index, int) else 'text' - timeout = timeout if timeout is not None else self._ele.page.timeout + timeout = timeout if timeout is not None else self._ele.owner.timeout return self._select(text_or_index, para_type, timeout=timeout) @property @@ -186,7 +186,7 @@ class SelectElement(object): raise TypeError('单选列表只能传入str格式。') mode = 'false' if cancel else 'true' - timeout = timeout if timeout is not None else self._ele.page.timeout + timeout = timeout if timeout is not None else self._ele.owner.timeout condition = set(condition) if isinstance(condition, (list, tuple)) else {condition} if para_type in ('text', 'value'): diff --git a/DrissionPage/_units/setter.py b/DrissionPage/_units/setter.py index dd0780c..245ac24 100644 --- a/DrissionPage/_units/setter.py +++ b/DrissionPage/_units/setter.py @@ -130,6 +130,8 @@ class ChromiumBaseSetter(BasePageSetter): if isinstance(files, str): files = files.split('\n') + elif isinstance(files, Path): + files = (files, ) self._page._upload_list = [str(Path(i).absolute()) for i in files] def headers(self, headers: dict) -> None: @@ -456,7 +458,7 @@ class ChromiumElementSetter(object): :param value: 属性值 :return: None """ - self._ele.page.run_cdp('DOM.setAttributeValue', nodeId=self._ele._node_id, name=name, value=str(value)) + self._ele.owner.run_cdp('DOM.setAttributeValue', nodeId=self._ele._node_id, name=name, value=str(value)) def property(self, name, value): """设置元素property属性 diff --git a/DrissionPage/_units/setter.pyi b/DrissionPage/_units/setter.pyi index 542f733..8695d61 100644 --- a/DrissionPage/_units/setter.pyi +++ b/DrissionPage/_units/setter.pyi @@ -62,7 +62,7 @@ class ChromiumBaseSetter(BasePageSetter): def auto_handle_alert(self, on_off: bool = True, accept: bool = True) -> None: ... - def upload_files(self, files: Union[str, list, tuple]) -> None: ... + def upload_files(self, files: Union[str, Path, list, tuple]) -> None: ... def blocked_urls(self, urls: Union[list, tuple, str, None]) -> None: ... diff --git a/DrissionPage/_units/states.py b/DrissionPage/_units/states.py index e6bc267..cda3d77 100644 --- a/DrissionPage/_units/states.py +++ b/DrissionPage/_units/states.py @@ -51,7 +51,7 @@ class ElementStates(object): def is_in_viewport(self): """返回元素是否出现在视口中,以元素click_point为判断""" x, y = self._ele.rect.click_point - return location_in_viewport(self._ele.page, x, y) if x else False + return location_in_viewport(self._ele.owner, x, y) if x else False @property def is_whole_in_viewport(self): @@ -59,14 +59,14 @@ class ElementStates(object): x1, y1 = self._ele.rect.location w, h = self._ele.rect.size x2, y2 = x1 + w, y1 + h - return location_in_viewport(self._ele.page, x1, y1) and location_in_viewport(self._ele.page, x2, y2) + return location_in_viewport(self._ele.owner, x1, y1) and location_in_viewport(self._ele.owner, x2, y2) @property def is_covered(self): """返回元素是否被覆盖,与是否在视口中无关,如被覆盖返回覆盖元素的backend id,否则返回False""" lx, ly = self._ele.rect.click_point try: - bid = self._ele.page.run_cdp('DOM.getNodeForLocation', x=int(lx), y=int(ly)).get('backendNodeId') + bid = self._ele.owner.run_cdp('DOM.getNodeForLocation', x=int(lx), y=int(ly)).get('backendNodeId') return bid if bid != self._ele._backend_id else False except CDPError: return False @@ -96,7 +96,7 @@ class ShadowRootStates(object): def is_alive(self): """返回元素是否仍在DOM中""" try: - self._ele.page.run_cdp('DOM.describeNode', backendNodeId=self._ele._backend_id) + self._ele.owner.run_cdp('DOM.describeNode', backendNodeId=self._ele._backend_id) return True except Exception: return False From e2393b1bf9f0d3397a6da8e233287ae2bef2ab2b Mon Sep 17 00:00:00 2001 From: g1879 Date: Wed, 21 Feb 2024 11:27:45 +0800 Subject: [PATCH 17/21] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=9C=89=E4=BA=9B?= =?UTF-8?q?=E4=B8=8B=E6=8B=89=E5=88=97=E8=A1=A8=E9=80=89=E6=8B=A9=E5=87=BA?= =?UTF-8?q?=E9=94=99=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DrissionPage/_units/selector.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DrissionPage/_units/selector.py b/DrissionPage/_units/selector.py index be3e29d..6682f64 100644 --- a/DrissionPage/_units/selector.py +++ b/DrissionPage/_units/selector.py @@ -264,4 +264,4 @@ class SelectElement(object): def _dispatch_change(self): """触发修改动作""" - self._ele.run_js('this.dispatchEvent(new Event("change", {bubbles: true}));') + self._ele.run_js('this.dispatchEvent(new CustomEvent("change", {bubbles: true}));') From a5b4951b5b2884cd6c8310e7ebeaca3021021060 Mon Sep 17 00:00:00 2001 From: g1879 Date: Mon, 26 Feb 2024 22:48:57 +0800 Subject: [PATCH 18/21] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E9=A1=B5=E9=9D=A2?= =?UTF-8?q?=E7=94=A8css=E6=9F=A5=E6=89=BE=E5=85=83=E7=B4=A0=E6=97=B6?= =?UTF-8?q?=E4=BC=9A=E6=89=BE=E5=88=B0=E6=96=87=E6=9C=AC=E7=9A=84=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DrissionPage/_base/base.py | 2 +- DrissionPage/_elements/chromium_element.py | 32 +++++++++++++++------ DrissionPage/_elements/chromium_element.pyi | 3 +- DrissionPage/_pages/chromium_base.py | 3 +- DrissionPage/_units/listener.py | 4 ++- 5 files changed, 31 insertions(+), 13 deletions(-) diff --git a/DrissionPage/_base/base.py b/DrissionPage/_base/base.py index d67ccb6..205baaa 100644 --- a/DrissionPage/_base/base.py +++ b/DrissionPage/_base/base.py @@ -54,7 +54,7 @@ class BaseElement(BaseParser): def __init__(self, owner=None): self.owner = owner - self.page = owner._page + self.page = owner._page if owner else None self._type = 'BaseElement' # ----------------以下属性或方法由后代实现---------------- diff --git a/DrissionPage/_elements/chromium_element.py b/DrissionPage/_elements/chromium_element.py index 4d4e872..8cb416b 100644 --- a/DrissionPage/_elements/chromium_element.py +++ b/DrissionPage/_elements/chromium_element.py @@ -1271,12 +1271,13 @@ def find_by_css(ele, selector, index, timeout): return NoneElement(ele.owner) if index is not None else [] -def make_chromium_eles(page, _ids, index=1, is_obj_id=True): +def make_chromium_eles(page, _ids, index=1, is_obj_id=True, ele_only=False): """根据node id或object id生成相应元素对象 :param page: ChromiumPage对象 :param _ids: 元素的id列表 :param index: 获取第几个,为None返回全部 :param is_obj_id: 传入的id是obj id还是node id + :param ele_only: 是否只返回ele,在页面查找元素时生效 :return: 浏览器元素对象或它们组成的列表,生成失败返回False """ if is_obj_id: @@ -1287,16 +1288,27 @@ def make_chromium_eles(page, _ids, index=1, is_obj_id=True): _ids = (_ids,) if index is not None: # 获取一个 - obj_id = _ids[index - 1] - return get_node_func(page, obj_id) + if ele_only: + for obj_id in _ids: + tmp = get_node_func(page, obj_id, ele_only) + if tmp is False: + return False + elif tmp is not None: + return tmp + return False + + else: + obj_id = _ids[index - 1] + return get_node_func(page, obj_id, ele_only) else: # 获取全部 nodes = [] for obj_id in _ids: - tmp = get_node_func(page, obj_id) + tmp = get_node_func(page, obj_id, ele_only) if tmp is False: return False - nodes.append(tmp) + elif tmp is not None: + nodes.append(tmp) return nodes @@ -1310,22 +1322,24 @@ def _get_node_info(page, id_type, _id): return node -def _get_node_by_obj_id(page, obj_id): +def _get_node_by_obj_id(page, obj_id, ele_only): + """根据obj id返回元素对象或文本,ele_only时如果是文本返回None,出错返回False""" node = _get_node_info(page, 'objectId', obj_id) if node is False: return False if node['node']['nodeName'] in ('#text', '#comment'): - return node['node']['nodeValue'] + return None if ele_only else node['node']['nodeValue'] else: return _make_ele(page, obj_id, node) -def _get_node_by_node_id(page, node_id): +def _get_node_by_node_id(page, node_id, ele_only): + """根据node id返回元素对象或文本,ele_only时如果是文本返回None,出错返回False""" node = _get_node_info(page, 'nodeId', node_id) if node is False: return False if node['node']['nodeName'] in ('#text', '#comment'): - return node['node']['nodeValue'] + return None if ele_only else node['node']['nodeValue'] else: obj_id = page.driver.run('DOM.resolveNode', nodeId=node_id) if 'error' in obj_id: diff --git a/DrissionPage/_elements/chromium_element.pyi b/DrissionPage/_elements/chromium_element.pyi index b1defa4..730ebb1 100644 --- a/DrissionPage/_elements/chromium_element.pyi +++ b/DrissionPage/_elements/chromium_element.pyi @@ -356,7 +356,8 @@ def find_by_css(ele: ChromiumElement, def make_chromium_eles(page: Union[ChromiumBase, ChromiumPage, WebPage, ChromiumTab, ChromiumFrame], _ids: Union[tuple, list, str, int], index: Optional[int] = 1, - is_obj_id: bool = True + is_obj_id: bool = True, + ele_only: bool = False ) -> Union[ChromiumElement, ChromiumFrame, List[Union[ChromiumElement, ChromiumFrame]]]: ... diff --git a/DrissionPage/_pages/chromium_base.py b/DrissionPage/_pages/chromium_base.py index 1851972..7ee9123 100644 --- a/DrissionPage/_pages/chromium_base.py +++ b/DrissionPage/_pages/chromium_base.py @@ -576,7 +576,8 @@ class ChromiumBase(BasePage): fromIndex=from_index, toIndex=end_index) if __ERROR__ not in nIds: if nIds['nodeIds'][0] != 0: - r = make_chromium_eles(self, _ids=nIds['nodeIds'], index=index_arg, is_obj_id=False) + r = make_chromium_eles(self, _ids=nIds['nodeIds'], index=index_arg, + is_obj_id=False, ele_only=True) if r is not False: break diff --git a/DrissionPage/_units/listener.py b/DrissionPage/_units/listener.py index 80ebe03..dd5de19 100644 --- a/DrissionPage/_units/listener.py +++ b/DrissionPage/_units/listener.py @@ -162,6 +162,8 @@ class Listener(object): :param gap: 每接收到多少个数据包返回一次数据 :return: 用于在接收到监听目标时触发动作的可迭代对象 """ + if not self.listening: + raise RuntimeError('监听未启动或已暂停。') caught = 0 end = perf_counter() + timeout if timeout else None while True: @@ -221,7 +223,7 @@ class Listener(object): :return: 返回是否等待成功 """ if not self.listening: - raise RuntimeError('监听未启动,用listen.start()启动。') + raise RuntimeError('监听未启动或已暂停。') if timeout is None: while (not targets_only and self._running_requests > 0) or (targets_only and self._running_targets > 0): sleep(.1) From 12b6882d4c74804e00c97b150fb6bc6f89cbd756 Mon Sep 17 00:00:00 2001 From: g1879 Date: Mon, 26 Feb 2024 23:04:41 +0800 Subject: [PATCH 19/21] =?UTF-8?q?reconnect()=E5=8C=85=E5=90=ABbrowser?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DrissionPage/_base/browser.py | 8 ++++++++ DrissionPage/_base/browser.pyi | 2 ++ DrissionPage/_pages/chromium_base.py | 1 + 3 files changed, 11 insertions(+) diff --git a/DrissionPage/_base/browser.py b/DrissionPage/_base/browser.py index 86caffe..018b84e 100644 --- a/DrissionPage/_base/browser.py +++ b/DrissionPage/_base/browser.py @@ -190,6 +190,14 @@ class Browser(object): """ return self.run_cdp('Browser.getWindowForTarget', targetId=tab_id or self.id)['bounds'] + def reconnect(self): + """断开重连""" + self._driver.stop() + self._driver = BrowserDriver(self.id, 'browser', self.address, self) + self.run_cdp('Target.setDiscoverTargets', discover=True) + self._driver.set_callback('Target.targetDestroyed', self._onTargetDestroyed) + self._driver.set_callback('Target.targetCreated', self._onTargetCreated) + def quit(self, timeout=5, force=False): """关闭浏览器 :param timeout: 等待浏览器关闭超时时间(秒) diff --git a/DrissionPage/_base/browser.pyi b/DrissionPage/_base/browser.pyi index af3b2c7..4f44b8c 100644 --- a/DrissionPage/_base/browser.pyi +++ b/DrissionPage/_base/browser.pyi @@ -56,6 +56,8 @@ class Browser(object): def get_window_bounds(self, tab_id: str = None) -> dict: ... + def reconnect(self) -> None: ... + def connect_to_page(self) -> None: ... def _onTargetCreated(self, **kwargs) -> None: ... diff --git a/DrissionPage/_pages/chromium_base.py b/DrissionPage/_pages/chromium_base.py index 7ee9123..9354e22 100644 --- a/DrissionPage/_pages/chromium_base.py +++ b/DrissionPage/_pages/chromium_base.py @@ -865,6 +865,7 @@ class ChromiumBase(BasePage): t_id = self._target_id self.disconnect() sleep(wait) + self.browser.reconnect() self._driver = self.browser._get_driver(t_id, self) def handle_alert(self, accept=True, send=None, timeout=None, next_one=False): From 8c1a549b21240820b9e543ba944aa6e5317566f4 Mon Sep 17 00:00:00 2001 From: g1879 Date: Wed, 28 Feb 2024 21:17:15 +0800 Subject: [PATCH 20/21] =?UTF-8?q?4.0.4.6=E4=BF=AE=E5=A4=8D=E7=94=A8css?= =?UTF-8?q?=E5=9C=A8=E5=85=83=E7=B4=A0=E4=B8=8B=E8=8E=B7=E5=8F=96=E5=A4=9A?= =?UTF-8?q?=E4=B8=AA=E5=AD=90=E5=85=83=E7=B4=A0=E6=97=B6=E6=95=B0=E9=87=8F?= =?UTF-8?q?=E4=B8=8D=E6=AD=A3=E7=A1=AE=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/_elements/chromium_element.py | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/DrissionPage/__init__.py b/DrissionPage/__init__.py index e7678ae..8faccd9 100644 --- a/DrissionPage/__init__.py +++ b/DrissionPage/__init__.py @@ -14,4 +14,4 @@ from ._configs.chromium_options import ChromiumOptions from ._configs.session_options import SessionOptions __all__ = ['ChromiumPage', 'ChromiumOptions', 'SessionOptions', 'SessionPage', 'WebPage', '__version__'] -__version__ = '4.0.4.5' +__version__ = '4.0.4.6' diff --git a/DrissionPage/_elements/chromium_element.py b/DrissionPage/_elements/chromium_element.py index 8cb416b..9c11a49 100644 --- a/DrissionPage/_elements/chromium_element.py +++ b/DrissionPage/_elements/chromium_element.py @@ -1256,7 +1256,7 @@ def find_by_css(ele, selector, index, timeout): else: obj_ids = [i['value']['objectId'] for i in ele.owner.run_cdp('Runtime.getProperties', objectId=res['result']['objectId'], - ownProperties=True)['result'][:-1]] + ownProperties=True)['result']] r = make_chromium_eles(ele.owner, _ids=obj_ids, index=index, is_obj_id=True) return None if r is False else r @@ -1291,9 +1291,7 @@ def make_chromium_eles(page, _ids, index=1, is_obj_id=True, ele_only=False): if ele_only: for obj_id in _ids: tmp = get_node_func(page, obj_id, ele_only) - if tmp is False: - return False - elif tmp is not None: + if tmp is not None: return tmp return False From 54bb9f138a9bef86bc485151de0dd4208d3a141a Mon Sep 17 00:00:00 2001 From: g1879 Date: Sat, 2 Mar 2024 22:43:51 +0800 Subject: [PATCH 21/21] =?UTF-8?q?4.0.4.7(+)=20wait()=E5=A2=9E=E5=8A=A0scop?= =?UTF-8?q?e=E5=8F=82=E6=95=B0=EF=BC=9B=20=E4=BF=AE=E5=A4=8D=E4=B8=8A?= =?UTF-8?q?=E4=B8=AA=E7=89=88=E6=9C=AC=E5=87=BA=E7=8E=B0=E7=9A=84=E6=89=BE?= =?UTF-8?q?=E6=B5=8F=E8=A7=88=E5=99=A8=E8=B7=AF=E5=BE=84=E9=97=AE=E9=A2=98?= =?UTF-8?q?=EF=BC=9B=20=E4=BF=AE=E5=A4=8D=E5=9C=A8ini=E8=AE=BE=E7=BD=AE?= =?UTF-8?q?=E4=B8=8B=E8=BD=BD=E8=B7=AF=E5=BE=84=E6=97=B6=E6=8A=A5=E9=94=99?= =?UTF-8?q?=E9=97=AE=E9=A2=98=EF=BC=9B=20=E4=BF=AE=E5=A4=8Drun=5Fasync=5Fj?= =?UTF-8?q?s()=E6=8A=A5=E9=94=99=E9=97=AE=E9=A2=98=EF=BC=9B=20=E4=BF=AE?= =?UTF-8?q?=E5=A4=8Dreconnect()=E6=8A=A5=E9=94=99=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/browser.py | 1 + DrissionPage/_base/driver.pyi | 7 ++++++- DrissionPage/_elements/chromium_element.py | 10 ++++------ DrissionPage/_functions/browser.py | 23 +++++++++++----------- DrissionPage/_units/downloader.py | 8 +------- DrissionPage/_units/waiter.py | 22 +++++++++++++++------ DrissionPage/_units/waiter.pyi | 4 ++-- 8 files changed, 43 insertions(+), 34 deletions(-) diff --git a/DrissionPage/__init__.py b/DrissionPage/__init__.py index 8faccd9..40056cf 100644 --- a/DrissionPage/__init__.py +++ b/DrissionPage/__init__.py @@ -14,4 +14,4 @@ from ._configs.chromium_options import ChromiumOptions from ._configs.session_options import SessionOptions __all__ = ['ChromiumPage', 'ChromiumOptions', 'SessionOptions', 'SessionPage', 'WebPage', '__version__'] -__version__ = '4.0.4.6' +__version__ = '4.0.4.7' diff --git a/DrissionPage/_base/browser.py b/DrissionPage/_base/browser.py index 018b84e..4ab366d 100644 --- a/DrissionPage/_base/browser.py +++ b/DrissionPage/_base/browser.py @@ -193,6 +193,7 @@ class Browser(object): def reconnect(self): """断开重连""" self._driver.stop() + BrowserDriver.BROWSERS.pop(self.id) self._driver = BrowserDriver(self.id, 'browser', self.address, self) self.run_cdp('Target.setDiscoverTargets', discover=True) self._driver.set_callback('Target.targetDestroyed', self._onTargetDestroyed) diff --git a/DrissionPage/_base/driver.pyi b/DrissionPage/_base/driver.pyi index a4810a2..329280b 100644 --- a/DrissionPage/_base/driver.pyi +++ b/DrissionPage/_base/driver.pyi @@ -71,6 +71,11 @@ class BrowserDriver(Driver): def __new__(cls, tab_id: str, tab_type: str, address: str, owner: Browser): ... - def __init__(self, tab_id: str, tab_type: str, address: str, owner: Browser): ... + def __init__(self, tab_id: str, tab_type: str, address: str, owner: Browser): + """ + + :rtype: object + """ + ... def get(self, url) -> Response: ... diff --git a/DrissionPage/_elements/chromium_element.py b/DrissionPage/_elements/chromium_element.py index 9c11a49..6ba1317 100644 --- a/DrissionPage/_elements/chromium_element.py +++ b/DrissionPage/_elements/chromium_element.py @@ -1447,12 +1447,10 @@ def run_js(page_or_ele, script, as_expr, timeout, args=None): except TimeoutError: raise TimeoutError(f'执行js超时(等待{timeout}秒)。') except ContextLostError: - if is_page: - raise ContextLostError('页面已被刷新,请尝试等待页面加载完成再执行操作。') - else: - raise ElementLostError('原来获取到的元素对象已不在页面内。') + raise ContextLostError('页面已被刷新,请尝试等待页面加载完成再执行操作。') if is_page else ElementLostError( + '原来获取到的元素对象已不在页面内。') - if res is None and page.states.has_alert: + if not res: # _timeout=0或js激活alert时 return None exceptionDetails = res.get('exceptionDetails') @@ -1463,7 +1461,7 @@ def run_js(page_or_ele, script, as_expr, timeout, args=None): return parse_js_result(page, page_or_ele, res.get('result'), end_time) except Exception: from DrissionPage import __version__ - raise RuntimeError(f'\njs结果解析错误\n内容:{res.get("result")}\n版本:{__version__}\n' + raise RuntimeError(f'\njs结果解析错误\n版本:{__version__}\n内容:{res}\njs:{script}\n' f'出现这个错误可能意味着程序有bug,请把错误信息和重现方法告知作者,谢谢。\n' f'报告网站:https://gitee.com/g1879/DrissionPage/issues') diff --git a/DrissionPage/_functions/browser.py b/DrissionPage/_functions/browser.py index 5075945..a707c61 100644 --- a/DrissionPage/_functions/browser.py +++ b/DrissionPage/_functions/browser.py @@ -317,23 +317,24 @@ def get_chrome_path(ini_path): # -----------从注册表中获取-------------- from winreg import OpenKey, EnumValue, CloseKey, HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE, KEY_READ + txt = r'SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\chrome.exe' try: - key = OpenKey(HKEY_CURRENT_USER, - r'SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\chrome.exe', - reserved=0, access=KEY_READ) + key = OpenKey(HKEY_CURRENT_USER, txt, reserved=0, access=KEY_READ) k = EnumValue(key, 0) CloseKey(key) if k[1]: return k[1] - key = OpenKey(HKEY_LOCAL_MACHINE, - r'SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\chrome.exe', - reserved=0, access=KEY_READ) - k = EnumValue(key, 0) - CloseKey(key) - return k[1] - except FileNotFoundError: - pass + except (FileNotFoundError, OSError): + try: + key = OpenKey(HKEY_LOCAL_MACHINE, txt, reserved=0, access=KEY_READ) + k = EnumValue(key, 0) + CloseKey(key) + if k[1]: + return k[1] + + except (FileNotFoundError, OSError): + pass # -----------从系统变量中获取-------------- for path in environ.get('PATH', '').split(';'): diff --git a/DrissionPage/_units/downloader.py b/DrissionPage/_units/downloader.py index 8f7f2b5..73dc956 100644 --- a/DrissionPage/_units/downloader.py +++ b/DrissionPage/_units/downloader.py @@ -31,13 +31,7 @@ class DownloadManager(object): self._flags = {} # {tab_id: [bool, DownloadMission]} if self._page.download_path: - self._browser.driver.set_callback('Browser.downloadProgress', self._onDownloadProgress) - self._browser.driver.set_callback('Browser.downloadWillBegin', self._onDownloadWillBegin) - r = self._browser.run_cdp('Browser.setDownloadBehavior', downloadPath=self._page.download_path, - behavior='allowAndName', eventsEnabled=True) - if 'error' in r: - print('浏览器版本太低无法使用下载管理功能。') - self._running = True + self.set_path(self._page, self._page.download_path) else: self._running = False diff --git a/DrissionPage/_units/waiter.py b/DrissionPage/_units/waiter.py index 5398a1b..6450dec 100644 --- a/DrissionPage/_units/waiter.py +++ b/DrissionPage/_units/waiter.py @@ -18,12 +18,17 @@ class BaseWaiter(object): """ self._driver = page_or_ele - def __call__(self, second): - """等待若干秒 + def __call__(self, second, scope=None): + """等待若干秒,如传入两个参数,等待时间为这两个数间的一个随机数 :param second: 秒数 + :param scope: 随机数范围 :return: None """ - sleep(second) + if scope is None: + sleep(second) + else: + from random import uniform + sleep(uniform(second, scope)) def ele_deleted(self, loc_or_ele, timeout=None, raise_err=None): """等待元素从DOM中删除 @@ -328,12 +333,17 @@ class ElementWaiter(object): self._page = page self._ele = ele - def __call__(self, second): - """等待若干秒 + def __call__(self, second, scope=None): + """等待若干秒,如传入两个参数,等待时间为这两个数间的一个随机数 :param second: 秒数 + :param scope: 随机数范围 :return: None """ - sleep(second) + if scope is None: + sleep(second) + else: + from random import uniform + sleep(uniform(second, scope)) def deleted(self, timeout=None, raise_err=None): """等待元素从dom删除 diff --git a/DrissionPage/_units/waiter.pyi b/DrissionPage/_units/waiter.pyi index 92ed44b..300c176 100644 --- a/DrissionPage/_units/waiter.pyi +++ b/DrissionPage/_units/waiter.pyi @@ -18,7 +18,7 @@ class BaseWaiter(object): def __init__(self, page: ChromiumBase): self._driver: ChromiumBase = ... - def __call__(self, second: float) -> None: ... + def __call__(self, second: float, scope: float = None) -> None: ... def ele_deleted(self, loc_or_ele: Union[str, tuple, ChromiumElement], @@ -78,7 +78,7 @@ class ElementWaiter(object): self._ele: ChromiumElement = ... self._page: ChromiumBase = ... - def __call__(self, second: float) -> None: ... + def __call__(self, second: float, scope: float = None) -> None: ... def deleted(self, timeout: float = None, raise_err: bool = None) -> bool: ...