From 45e64adc1eba3b27383d6960916e0691090ef33a Mon Sep 17 00:00:00 2001 From: g1879 Date: Thu, 15 Aug 2024 17:47:41 +0800 Subject: [PATCH] =?UTF-8?q?=E7=BB=A7=E7=BB=AD=E4=BF=AE=E6=94=B9=E5=A4=87?= =?UTF-8?q?=E6=B3=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DrissionPage/_elements/chromium_element.py | 6 +- DrissionPage/_elements/none_element.pyi | 3 +- DrissionPage/_functions/keys.py | 14 - DrissionPage/_functions/keys.pyi | 33 +- DrissionPage/_functions/locator.py | 27 -- DrissionPage/_functions/locator.pyi | 57 +++- DrissionPage/_functions/tools.py | 48 --- DrissionPage/_functions/tools.pyi | 78 ++++- DrissionPage/_functions/web.py | 65 ---- DrissionPage/_functions/web.pyi | 101 +++++- DrissionPage/_pages/chromium_frame.py | 166 +--------- DrissionPage/_pages/chromium_frame.pyi | 347 +++++++++++++++++---- DrissionPage/_units/listener.pyi | 2 +- 13 files changed, 520 insertions(+), 427 deletions(-) diff --git a/DrissionPage/_elements/chromium_element.py b/DrissionPage/_elements/chromium_element.py index 01fa730..c63fa67 100644 --- a/DrissionPage/_elements/chromium_element.py +++ b/DrissionPage/_elements/chromium_element.py @@ -34,7 +34,6 @@ __FRAME_ELEMENT__ = ('iframe', 'frame') class ChromiumElement(DrissionElement): - """控制浏览器元素的对象""" def __init__(self, owner, node_id=None, obj_id=None, backend_id=None): super().__init__(owner) @@ -423,7 +422,7 @@ class ChromiumElement(DrissionElement): def s_ele(self, locator=None, index=1, timeout=None): return (make_session_ele(self, locator, index=index, method='s_ele()') if self.ele(locator, index=index, timeout=timeout) - else NoneElement(self, method='s_ele()', args={'locator': locator, 'index': index})) + else NoneElement(self.owner, method='s_ele()', args={'locator': locator, 'index': index})) def s_eles(self, locator=None, timeout=None): return (make_session_ele(self, locator, index=None) @@ -686,7 +685,6 @@ class ChromiumElement(DrissionElement): class ShadowRoot(BaseElement): - """ShadowRoot是用于处理ShadowRoot的类,使用方法和ChromiumElement基本一致""" def __init__(self, parent_ele, obj_id=None, backend_id=None): super().__init__(parent_ele.owner) @@ -847,7 +845,7 @@ class ShadowRoot(BaseElement): def s_ele(self, locator=None, index=1, timeout=None): return (make_session_ele(self, locator, index=index, method='s_ele()') if self.ele(locator, index=index, timeout=timeout) - else NoneElement(self, method='s_ele()', args={'locator': locator, 'index': index})) + else NoneElement(self.owner, method='s_ele()', args={'locator': locator, 'index': index})) def s_eles(self, locator, timeout=None): return (make_session_ele(self, locator, index=None) diff --git a/DrissionPage/_elements/none_element.pyi b/DrissionPage/_elements/none_element.pyi index deeae48..8aed06a 100644 --- a/DrissionPage/_elements/none_element.pyi +++ b/DrissionPage/_elements/none_element.pyi @@ -11,7 +11,8 @@ from .._base.base import BasePage class NoneElement(object): - def __init__(self, page: BasePage = None, + def __init__(self, + page: BasePage = None, method: str = None, args: dict = None): """ diff --git a/DrissionPage/_functions/keys.py b/DrissionPage/_functions/keys.py index 2e9313a..210d94a 100644 --- a/DrissionPage/_functions/keys.py +++ b/DrissionPage/_functions/keys.py @@ -341,8 +341,6 @@ sys = system().lower() def keys_to_typing(value): - """把要输入的内容连成字符串,去掉其中 ctrl 等键。 - 返回的modifier表示是否有按下组合键""" typing = [] modifier = 0 for val in value: @@ -361,12 +359,6 @@ def keys_to_typing(value): def make_input_data(modifiers, key, key_up=False): - """ - :param modifiers: 功能键设置 - :param key: 按键字符 - :param key_up: 是否提起 - :return: None - """ data = keyDefinitions.get(key) if not data: return None @@ -414,7 +406,6 @@ def make_input_data(modifiers, key, key_up=False): def send_key(page, modifier, key): - """发送一个字,在键盘中的字符触发按键,其它直接发送文本""" data = make_input_data(modifier, key) if data: page._run_cdp('Input.dispatchKeyEvent', **data) @@ -426,11 +417,6 @@ def send_key(page, modifier, key): def input_text_or_keys(page, text_or_keys): - """输入文本,也可输入组合键,组合键用tuple形式输入 - :param page: ChromiumBase对象 - :param text_or_keys: 文本值或按键组合 - :return: self - """ if not isinstance(text_or_keys, (tuple, list)): text_or_keys = (str(text_or_keys),) modifier, text_or_keys = keys_to_typing(text_or_keys) diff --git a/DrissionPage/_functions/keys.pyi b/DrissionPage/_functions/keys.pyi index c9254d1..f41ec9a 100644 --- a/DrissionPage/_functions/keys.pyi +++ b/DrissionPage/_functions/keys.pyi @@ -85,13 +85,38 @@ keyDefinitions: dict = ... modifierBit: dict = ... -def keys_to_typing(value: Union[str, int, list, tuple]) -> Tuple[int, str]: ... +def keys_to_typing(value: Union[str, int, list, tuple]) -> Tuple[int, str]: + """把要输入的内容连成字符串,去掉其中 ctrl 等键。 + 返回的modifier表示是否有按下组合键""" + ... -def make_input_data(modifiers: int, key: str, key_up: bool = False) -> dict: ... +def make_input_data(modifiers: int, + key: str, + key_up: bool = False) -> dict: + """ + :param modifiers: 功能键设置 + :param key: 按键字符 + :param key_up: 是否提起 + :return: None + """ + ... -def send_key(page: ChromiumBase, modifier: int, key: str) -> None: ... +def send_key(page: ChromiumBase, modifier: int, key: str) -> None: + """发送一个字,在键盘中的字符触发按键,其它直接发送文本 + :param page: 动作所在页面 + :param modifier: 功能键信息 + :param key: 要是输入的按键 + :return: None + """ + ... -def input_text_or_keys(page: ChromiumBase, text_or_keys: Any) -> None: ... +def input_text_or_keys(page: ChromiumBase, text_or_keys: Any) -> None: + """输入文本,也可输入组合键,组合键用tuple形式输入 + :param page: ChromiumBase对象 + :param text_or_keys: 文本值或按键组合 + :return: self + """ + ... diff --git a/DrissionPage/_functions/locator.py b/DrissionPage/_functions/locator.py index e0aa381..65941a4 100644 --- a/DrissionPage/_functions/locator.py +++ b/DrissionPage/_functions/locator.py @@ -10,10 +10,6 @@ from .by import By def locator_to_tuple(loc): - """解析定位字符串生成dict格式数据 - :param loc: 待处理的字符串 - :return: 格式: {'and': bool, 'args': ['属性名称', '匹配方式', '属性值', 是否否定]} - """ loc = _preprocess(loc) # 多属性查找 @@ -84,18 +80,11 @@ def _get_arg(text) -> list: def is_loc(text): - """返回text是否定位符""" return text.startswith(('.', '#', '@', 't:', 't=', 'tag:', 'tag=', 'tx:', 'tx=', 'tx^', 'tx$', 'text:', 'text=', 'text^', 'text$', 'xpath:', 'xpath=', 'x:', 'x=', 'css:', 'css=', 'c:', 'c=')) def get_loc(loc, translate_css=False, css_mode=False): - """接收本库定位语法或selenium定位元组,转换为标准定位元组,可翻译css selector为xpath - :param loc: 本库定位语法或selenium定位元组 - :param translate_css: 是否翻译css selector为xpath,用于相对定位 - :param css_mode: 是否尽量用css selector方式 - :return: DrissionPage定位元组 - """ if isinstance(loc, tuple): loc = translate_css_loc(loc) if css_mode else translate_loc(loc) @@ -118,10 +107,6 @@ def get_loc(loc, translate_css=False, css_mode=False): def str_to_xpath_loc(loc): - """处理元素查找语句 - :param loc: 查找语法字符串 - :return: 匹配符元组 - """ loc_by = 'xpath' loc = _preprocess(loc) @@ -173,10 +158,6 @@ def str_to_xpath_loc(loc): def str_to_css_loc(loc): - """处理元素查找语句 - :param loc: 查找语法字符串 - :return: 匹配符元组 - """ loc_by = 'css selector' loc = _preprocess(loc) @@ -444,10 +425,6 @@ def _make_single_css_str(tag: str, text: str) -> tuple: def translate_loc(loc): - """把By类型的loc元组转换为css selector或xpath类型的 - :param loc: By类型的loc元组 - :return: css selector或xpath类型的loc元组 - """ if len(loc) != 2: raise ValueError('定位符长度必须为2。') @@ -486,10 +463,6 @@ def translate_loc(loc): def translate_css_loc(loc): - """把By类型的loc元组转换为css selector或xpath类型的 - :param loc: By类型的loc元组 - :return: css selector或xpath类型的loc元组 - """ if len(loc) != 2: raise ValueError('定位符长度必须为2。') diff --git a/DrissionPage/_functions/locator.pyi b/DrissionPage/_functions/locator.pyi index 991ff27..2b079da 100644 --- a/DrissionPage/_functions/locator.pyi +++ b/DrissionPage/_functions/locator.pyi @@ -8,25 +8,66 @@ from typing import Union -def locator_to_tuple(loc: str) -> dict: ... +def locator_to_tuple(loc: str) -> dict: + """解析定位字符串生成dict格式数据 + :param loc: 待处理的字符串 + :return: 格式: {'and': bool, 'args': ['属性名称', '匹配方式', '属性值', 是否否定]} + """ + ... -def is_loc(text: str) -> bool: ... +def is_loc(text: str) -> bool: + """返回text是否定位符""" + ... -def get_loc(loc: Union[tuple, str], translate_css: bool = False, css_mode: bool = False) -> tuple: ... +def get_loc(loc: Union[tuple, str], + translate_css: bool = False, + css_mode: bool = False) -> tuple: + """接收本库定位语法或selenium定位元组,转换为标准定位元组,可翻译css selector为xpath + :param loc: 本库定位语法或selenium定位元组 + :param translate_css: 是否翻译css selector为xpath,用于相对定位 + :param css_mode: 是否尽量用css selector方式 + :return: DrissionPage定位元组 + """ + ... -def str_to_xpath_loc(loc: str) -> tuple: ... +def str_to_xpath_loc(loc: str) -> tuple: + """处理元素查找语句 + :param loc: 查找语法字符串 + :return: 匹配符元组 + """ + ... -def str_to_css_loc(loc: str) -> tuple: ... +def str_to_css_loc(loc: str) -> tuple: + """处理元素查找语句 + :param loc: 查找语法字符串 + :return: 匹配符元组 + """ + ... -def translate_loc(loc: tuple) -> tuple: ... +def translate_loc(loc: tuple) -> tuple: + """把By类型的loc元组转换为css selector或xpath类型的 + :param loc: By类型的loc元组 + :return: css selector或xpath类型的loc元组 + """ + ... -def translate_css_loc(loc: tuple) -> tuple: ... +def translate_css_loc(loc: tuple) -> tuple: + """把By类型的loc元组转换为css selector或xpath类型的 + :param loc: By类型的loc元组 + :return: css selector或xpath类型的loc元组 + """ + ... -def css_trans(txt: str) -> str: ... +def css_trans(txt: str) -> str: + """css字符串中特殊字符转义 + :param txt: 要处理的文本 + :return: 处理后的文本 + """ + ... diff --git a/DrissionPage/_functions/tools.py b/DrissionPage/_functions/tools.py index ed97953..92183d3 100644 --- a/DrissionPage/_functions/tools.py +++ b/DrissionPage/_functions/tools.py @@ -24,9 +24,6 @@ class PortFinder(object): checked_paths = set() def __init__(self, path=None): - """ - :param path: 临时文件保存路径,为None时使用系统临时文件夹 - """ tmp = Path(path) if path else Path(gettempdir()) / 'DrissionPage' self.tmp_dir = tmp / 'autoPortData' self.tmp_dir.mkdir(parents=True, exist_ok=True) @@ -37,10 +34,6 @@ class PortFinder(object): PortFinder.checked_paths.add(str(self.tmp_dir.absolute())) def get_port(self, scope=None): - """查找一个可用端口 - :param scope: 指定端口范围,不含最后的数字,为None则使用[9600-59600) - :return: 可以使用的端口和用户文件夹路径组成的元组 - """ from random import randint with PortFinder.lock: if PortFinder.prev_time and perf_counter() - PortFinder.prev_time > 60: @@ -67,11 +60,6 @@ class PortFinder(object): def port_is_using(ip, port): - """检查端口是否被占用 - :param ip: 浏览器地址 - :param port: 浏览器端口 - :return: bool - """ from socket import socket, AF_INET, SOCK_STREAM s = socket(AF_INET, SOCK_STREAM) s.settimeout(.1) @@ -81,11 +69,6 @@ def port_is_using(ip, port): def clean_folder(folder_path, ignore=None): - """清空一个文件夹,除了ignore里的文件和文件夹 - :param folder_path: 要清空的文件夹路径 - :param ignore: 忽略列表 - :return: None - """ ignore = [] if not ignore else ignore p = Path(folder_path) @@ -98,11 +81,6 @@ def clean_folder(folder_path, ignore=None): def show_or_hide_browser(page, hide=True): - """执行显示或隐藏浏览器窗口 - :param page: ChromePage对象 - :param hide: 是否隐藏 - :return: None - """ if not page.browser.address.startswith(('127.0.0.1', 'localhost')): return @@ -125,11 +103,6 @@ def show_or_hide_browser(page, hide=True): def get_browser_progress_id(progress, address): - """获取浏览器进程id - :param progress: 已知的进程对象,没有时传入None - :param address: 浏览器管理地址,含端口 - :return: 进程id或None - """ if progress: return progress.pid @@ -148,11 +121,6 @@ def get_browser_progress_id(progress, address): def get_hwnds_from_pid(pid, title): - """通过PID查询句柄ID - :param pid: 进程id - :param title: 窗口标题 - :return: 进程句柄组成的列表 - """ try: from win32gui import IsWindow, GetWindowText, EnumWindows from win32process import GetWindowThreadProcessId @@ -172,12 +140,6 @@ def get_hwnds_from_pid(pid, title): def wait_until(function, kwargs=None, timeout=10): - """等待传入的方法返回值不为假 - :param function: 要执行的方法 - :param kwargs: 方法参数 - :param timeout: 超时时间(秒) - :return: 执行结果,超时抛出TimeoutError - """ if kwargs is None: kwargs = {} end_time = perf_counter() + timeout @@ -190,22 +152,12 @@ def wait_until(function, kwargs=None, timeout=10): def configs_to_here(save_name=None): - """把默认ini文件复制到当前目录 - :param save_name: 指定文件名,为None则命名为'dp_configs.ini' - :return: None - """ om = OptionsManager('default') save_name = f'{save_name}.ini' if save_name is not None else 'dp_configs.ini' om.save(save_name) def raise_error(result, ignore=None, user=False): - """抛出error对应报错 - :param result: 包含error的dict - :param ignore: 要忽略的错误 - :param user: 是否用户调用的 - :return: None - """ error = result['error'] if error in ('Cannot find context with specified id', 'Inspected target navigated or closed', 'No frame with given id found'): diff --git a/DrissionPage/_functions/tools.pyi b/DrissionPage/_functions/tools.pyi index 5e344f3..5946a9c 100644 --- a/DrissionPage/_functions/tools.pyi +++ b/DrissionPage/_functions/tools.pyi @@ -20,31 +20,89 @@ class PortFinder(object): tmp_dir: Path = ... checked_paths: set = ... - def __init__(self, path: Union[str, Path] = None): ... + def __init__(self, path: Union[str, Path] = None): + """ + :param path: 临时文件保存路径,为None时使用系统临时文件夹 + """ + ... @staticmethod - def get_port(scope: Tuple[int, int] = None) -> Tuple[int, str]: ... + def get_port(scope: Tuple[int, int] = None) -> Tuple[int, str]: + """查找一个可用端口 + :param scope: 指定端口范围,不含最后的数字,为None则使用[9600-59600) + :return: 可以使用的端口和用户文件夹路径组成的元组 + """ + ... -def port_is_using(ip: str, port: Union[str, int]) -> bool: ... +def port_is_using(ip: str, port: Union[str, int]) -> bool: + """检查端口是否被占用 + :param ip: 浏览器地址 + :param port: 浏览器端口 + :return: bool + """ + ... -def clean_folder(folder_path: Union[str, Path], ignore: Union[tuple, list] = None) -> None: ... +def clean_folder(folder_path: Union[str, Path], ignore: Union[tuple, list] = None) -> None: + """清空一个文件夹,除了ignore里的文件和文件夹 + :param folder_path: 要清空的文件夹路径 + :param ignore: 忽略列表 + :return: None + """ + ... -def show_or_hide_browser(page: ChromiumBase, hide: bool = True) -> None: ... +def show_or_hide_browser(page: ChromiumBase, hide: bool = True) -> None: + """执行显示或隐藏浏览器窗口 + :param page: ChromePage对象 + :param hide: 是否隐藏 + :return: None + """ + ... -def get_browser_progress_id(progress: Union[popen, None], address: str) -> Union[str, None]: ... +def get_browser_progress_id(progress: Union[popen, None], address: str) -> Union[str, None]: + """获取浏览器进程id + :param progress: 已知的进程对象,没有时传入None + :param address: 浏览器管理地址,含端口 + :return: 进程id或None + """ + ... -def get_hwnds_from_pid(pid: Union[str, int], title: str) -> list: ... +def get_hwnds_from_pid(pid: Union[str, int], title: str) -> list: + """通过PID查询句柄ID + :param pid: 进程id + :param title: 窗口标题 + :return: 进程句柄组成的列表 + """ + ... -def wait_until(function: callable, kwargs: dict = None, timeout: float = 10): ... +def wait_until(function: callable, kwargs: dict = None, timeout: float = 10): + """等待传入的方法返回值不为假 + :param function: 要执行的方法 + :param kwargs: 方法参数 + :param timeout: 超时时间(秒) + :return: 执行结果,超时抛出TimeoutError + """ + ... -def configs_to_here(file_name: Union[Path, str] = None) -> None: ... +def configs_to_here(file_name: Union[Path, str] = None) -> None: + """把默认ini文件复制到当前目录 + :param save_name: 指定文件名,为None则命名为'dp_configs.ini' + :return: None + """ + ... -def raise_error(result: dict, ignore=None, user: bool = False) -> None: ... +def raise_error(result: dict, ignore=None, user: bool = False) -> None: + """抛出error对应报错 + :param result: 包含error的dict + :param ignore: 要忽略的错误 + :param user: 是否用户调用的 + :return: None + """ + ... diff --git a/DrissionPage/_functions/web.py b/DrissionPage/_functions/web.py index 7cd4783..58bfd22 100644 --- a/DrissionPage/_functions/web.py +++ b/DrissionPage/_functions/web.py @@ -16,10 +16,6 @@ from requests.structures import CaseInsensitiveDict def get_ele_txt(e): - """获取元素内所有文本 - :param e: 元素对象 - :return: 元素内所有文本 - """ # 前面无须换行的元素 nowrap_list = ('br', 'sub', 'sup', 'em', 'strong', 'a', 'font', 'b', 'span', 's', 'i', 'del', 'ins', 'img', 'td', 'th', 'abbr', 'bdi', 'bdo', 'cite', 'code', 'data', 'dfn', 'kbd', 'mark', 'q', 'rp', 'rt', 'ruby', @@ -106,20 +102,10 @@ def get_ele_txt(e): def format_html(text): - """处理html编码字符 - :param text: html文本 - :return: 格式化后的html文本 - """ return unescape(text).replace('\xa0', ' ') if text else text def location_in_viewport(page, loc_x, loc_y): - """判断给定的坐标是否在视口中 |n - :param page: ChromePage对象 - :param loc_x: 页面绝对坐标x - :param loc_y: 页面绝对坐标y - :return: bool - """ js = f'''function(){{let x = {loc_x}; let y = {loc_y}; const scrollLeft = document.documentElement.scrollLeft; const scrollTop = document.documentElement.scrollTop; @@ -131,13 +117,6 @@ def location_in_viewport(page, loc_x, loc_y): def offset_scroll(ele, offset_x, offset_y): - """接收元素及偏移坐标,把坐标滚动到页面中间,返回该点坐标 - 有偏移量时以元素左上角坐标为基准,没有时以click_point为基准 - :param ele: 元素对象 - :param offset_x: 偏移量x - :param offset_y: 偏移量y - :return: 相对坐标 - """ loc_x, loc_y = ele.rect.location cp_x, cp_y = ele.rect.click_point lx = loc_x + offset_x if offset_x else cp_x @@ -154,11 +133,6 @@ def offset_scroll(ele, offset_x, offset_y): def make_absolute_link(link, baseURI=None): - """获取绝对url - :param link: 超链接 - :param baseURI: 页面或iframe的url - :return: 绝对链接 - """ if not link: return link @@ -182,7 +156,6 @@ def make_absolute_link(link, baseURI=None): def is_js_func(func): - """检查文本是否js函数""" func = func.strip() if (func.startswith('function') or func.startswith('async ')) and func.endswith('}'): return True @@ -192,12 +165,6 @@ def is_js_func(func): def get_blob(page, url, as_bytes=True): - """获取知道blob资源 - :param page: 资源所在页面对象 - :param url: 资源url - :param as_bytes: 是否以字节形式返回 - :return: 资源内容 - """ if not url.startswith('blob'): raise TypeError('该链接非blob类型。') js = """ @@ -227,14 +194,6 @@ def get_blob(page, url, as_bytes=True): def save_page(tab, path=None, name=None, as_pdf=False, kwargs=None): - """把当前页面保存为文件,如果path和name参数都为None,只返回文本 - :param tab: Tab或Page对象 - :param path: 保存路径,为None且name不为None时保存在当前路径 - :param name: 文件名,为None且path不为None时用title属性值 - :param as_pdf: 为Ture保存为pdf,否则为mhtml且忽略kwargs参数 - :param kwargs: pdf生成参数 - :return: as_pdf为True时返回bytes,否则返回文件文本 - """ if name: if name.endswith('.pdf'): name = name[:-4] @@ -258,12 +217,6 @@ def save_page(tab, path=None, name=None, as_pdf=False, kwargs=None): def get_mhtml(page, path=None, name=None): - """把当前页面保存为mhtml文件,如果path和name参数都为None,只返回mhtml文本 - :param page: 要保存的页面对象 - :param path: 保存路径,为None且name不为None时保存在当前路径 - :param name: 文件名,为None且path不为None时用title属性值 - :return: mhtml文本 - """ r = page._run_cdp('Page.captureSnapshot')['data'] if path is None and name is None: return r @@ -277,13 +230,6 @@ def get_mhtml(page, path=None, name=None): def get_pdf(page, path=None, name=None, kwargs=None): - """把当前页面保存为pdf文件,如果path和name参数都为None,只返回字节 - :param page: 要保存的页面对象 - :param path: 保存路径,为None且name不为None时保存在当前路径 - :param name: 文件名,为None且path不为None时用title属性值 - :param kwargs: pdf生成参数 - :return: pdf文本 - """ if not kwargs: kwargs = {} kwargs['transferMode'] = 'ReturnAsBase64' @@ -307,13 +253,6 @@ def get_pdf(page, path=None, name=None, kwargs=None): def tree(ele_or_page, text=False, show_js=False, show_css=False): - """把页面或元素对象DOM结构打印出来 - :param ele_or_page: 页面或元素对象 - :param text: 是否打印文本,输入数字可指定打印文本长度上线 - :param show_js: 打印文本时是否包含