diff --git a/DrissionPage/action_chains.py b/DrissionPage/action_chains.py index 1d74668..856f615 100644 --- a/DrissionPage/action_chains.py +++ b/DrissionPage/action_chains.py @@ -18,8 +18,7 @@ class ActionChains: self.curr_x = 0 # 视口坐标 self.curr_y = 0 - def move_to(self, ele_or_loc, - offset_x: int = 0, offset_y: int = 0) -> 'ActionChains': + def move_to(self, ele_or_loc, offset_x=0, offset_y=0): """鼠标移动到元素中点,或页面上的某个绝对坐标。可设置偏移量 \n 当带偏移量时,偏移量相对于元素左上角坐标 :param ele_or_loc: 元素对象或绝对坐标,坐标为tuple(int, int)形式 @@ -40,13 +39,13 @@ class ActionChains: if not _location_in_viewport(self.page, lx, ly): self.page.scroll.to_location(lx, ly) - cx, cy = _location_to_client(self.page, lx, ly) + cx, cy = location_to_client(self.page, lx, ly) self._dr.Input.dispatchMouseEvent(type='mouseMoved', x=cx, y=cy, modifiers=self.modifier) self.curr_x = cx self.curr_y = cy return self - def move(self, offset_x: int = 0, offset_y: int = 0) -> 'ActionChains': + def move(self, offset_x=0, offset_y=0): """鼠标相对当前位置移动若干位置 \n :param offset_x: 偏移量x :param offset_y: 偏移量y @@ -57,7 +56,7 @@ class ActionChains: self._dr.Input.dispatchMouseEvent(type='mouseMoved', x=self.curr_x, y=self.curr_y, modifiers=self.modifier) return self - def hold(self, on_ele=None) -> 'ActionChains': + def hold(self, on_ele=None): """点击并按住当前坐标或指定元素 \n :param on_ele: ChromiumElement对象 :return: self @@ -68,7 +67,7 @@ class ActionChains: x=self.curr_x, y=self.curr_y, modifiers=self.modifier) return self - def click(self, on_ele=None) -> 'ActionChains': + def click(self, on_ele=None): """点击鼠标左键,可先移动到元素上 \n :param on_ele: ChromiumElement元素 :return: self @@ -81,7 +80,7 @@ class ActionChains: x=self.curr_x, y=self.curr_y, modifiers=self.modifier) return self - def r_click(self, on_ele=None) -> 'ActionChains': + def r_click(self, on_ele=None): """点击鼠标右键,可先移动到元素上 \n :param on_ele: ChromiumElement元素 :return: self @@ -94,7 +93,7 @@ class ActionChains: x=self.curr_x, y=self.curr_y, modifiers=self.modifier) return self - def release(self, on_ele=None) -> 'ActionChains': + def release(self, on_ele=None): """释放鼠标左键,可先移动到元素再释放 \n :param on_ele: ChromiumElement对象 :return: self @@ -105,7 +104,7 @@ class ActionChains: x=self.curr_x, y=self.curr_y, modifiers=self.modifier) return self - def scroll(self, delta_x: int = 0, delta_y: int = 0, on_ele=None) -> 'ActionChains': + def scroll(self, delta_x=0, delta_y=0, on_ele=None): """滚动鼠标滚轮,可先移动到元素上 \n :param delta_x: 滚轮变化值x :param delta_y: 滚轮变化值y @@ -118,23 +117,23 @@ class ActionChains: deltaX=delta_x, deltaY=delta_y, modifiers=self.modifier) return self - def up(self, pixel: int) -> 'ActionChains': + def up(self, pixel): """鼠标向上移动若干像素""" return self.move(0, -pixel) - def down(self, pixel: int) -> 'ActionChains': + def down(self, pixel): """鼠标向下移动若干像素""" return self.move(0, pixel) - def left(self, pixel: int) -> 'ActionChains': + def left(self, pixel): """鼠标向左移动若干像素""" return self.move(-pixel, 0) - def right(self, pixel: int) -> 'ActionChains': + def right(self, pixel): """鼠标向右移动若干像素""" return self.move(pixel, 0) - def key_down(self, key) -> 'ActionChains': + def key_down(self, key): """按下键盘上的按键 \n :param key: 按键,特殊字符见Keys :return: self @@ -147,7 +146,7 @@ class ActionChains: self.page.run_cdp('Input.dispatchKeyEvent', **data) return self - def key_up(self, key) -> 'ActionChains': + def key_up(self, key): """提起键盘上的按键 \n :param key: 按键,特殊字符见Keys :return: self @@ -160,12 +159,12 @@ class ActionChains: self.page.run_cdp('Input.dispatchKeyEvent', **data) return self - def wait(self, second: float) -> 'ActionChains': + def wait(self, second): """等待若干秒""" sleep(second) return self - def _get_key_data(self, key, action: str) -> dict: + def _get_key_data(self, key, action): """获取用于发送的按键信息 \n :param key: 按键 :param action: 'keyDown' 或 'keyUp' @@ -187,7 +186,7 @@ class ActionChains: 'isKeypad': description['location'] == 3} -def _location_to_client(page, lx: int, ly: int) -> tuple: +def location_to_client(page, lx, ly): """绝对坐标转换为视口坐标""" scrool_x = page.run_script('return document.documentElement.scrollLeft;') scrool_y = page.run_script('return document.documentElement.scrollTop;') diff --git a/DrissionPage/action_chains.pyi b/DrissionPage/action_chains.pyi index a4417f0..c3c8bee 100644 --- a/DrissionPage/action_chains.pyi +++ b/DrissionPage/action_chains.pyi @@ -1,6 +1,7 @@ # -*- coding:utf-8 -*- from typing import Union, Tuple +from .chromium_base import ChromiumBase from .tab import Tab from .chromium_element import ChromiumElement from .chromium_page import ChromiumPage @@ -9,7 +10,7 @@ from .chromium_page import ChromiumPage class ActionChains: """用于实现动作链的类""" - def __init__(self, page): + def __init__(self, page:ChromiumBase): self.page: ChromiumPage = ... self._dr: Tab = ... self.curr_x: int = ... @@ -21,15 +22,15 @@ class ActionChains: def move(self, offset_x: int = ..., offset_y: int = ...) -> 'ActionChains': ... - def hold(self, on_ele=...) -> 'ActionChains': ... + def hold(self, on_ele:ChromiumElement=...) -> 'ActionChains': ... - def click(self, on_ele=...) -> 'ActionChains': ... + def click(self, on_ele:ChromiumElement=...) -> 'ActionChains': ... - def r_click(self, on_ele=...) -> 'ActionChains': ... + def r_click(self, on_ele:ChromiumElement=...) -> 'ActionChains': ... - def release(self, on_ele=...) -> 'ActionChains': ... + def release(self, on_ele:ChromiumElement=...) -> 'ActionChains': ... - def scroll(self, delta_x: int = ..., delta_y: int = ..., on_ele=...) -> 'ActionChains': ... + def scroll(self, delta_x: int = ..., delta_y: int = ..., on_ele:ChromiumElement=...) -> 'ActionChains': ... def up(self, pixel: int) -> 'ActionChains': ... @@ -39,13 +40,13 @@ class ActionChains: def right(self, pixel: int) -> 'ActionChains': ... - def key_down(self, key) -> 'ActionChains': ... + def key_down(self, key:str) -> 'ActionChains': ... - def key_up(self, key) -> 'ActionChains': ... + def key_up(self, key:str) -> 'ActionChains': ... def wait(self, second: float) -> 'ActionChains': ... - def _get_key_data(self, key, action: str) -> dict: ... + def _get_key_data(self, key:str, action: str) -> dict: ... -def _location_to_client(page, lx: int, ly: int) -> tuple: ... +def location_to_client(page, lx: int, ly: int) -> tuple: ... diff --git a/DrissionPage/base.py b/DrissionPage/base.py index e4c1911..d2fab59 100644 --- a/DrissionPage/base.py +++ b/DrissionPage/base.py @@ -21,7 +21,7 @@ class BaseParser(object): def ele(self, loc_or_ele, timeout=None): return self._ele(loc_or_ele, timeout, True) - def eles(self, loc_or_str: Union[Tuple[str, str], str], timeout=None): + def eles(self, loc_or_str, timeout=None): return self._ele(loc_or_str, timeout, False) # ----------------以下属性或方法待后代实现---------------- @@ -56,19 +56,19 @@ class BaseElement(BaseParser): return True @abstractmethod - def _ele(self, loc_or_ele, timeout=None, single=True, relative=False): + def _ele(self, loc_or_str, timeout=None, single=True, relative=False): pass - def parent(self, level_or_loc: Union[tuple, str, int] = 1): + def parent(self, level_or_loc = 1): pass - def prev(self, index: int = 1) -> None: + def prev(self, index = 1) -> None: return None # ShadowRootElement直接继承 def prevs(self) -> None: return None # ShadowRootElement直接继承 - def next(self, index: int = 1): + def next(self, index = 1): pass def nexts(self): @@ -290,7 +290,7 @@ class DrissionElement(BaseElement): class BasePage(BaseParser): """页面类的基类""" - def __init__(self, timeout: float = 10): + def __init__(self, timeout = 10): """初始化函数""" self._url = None self.timeout = timeout @@ -299,32 +299,32 @@ class BasePage(BaseParser): self._url_available = None @property - def title(self) -> Union[str, None]: + def title(self): """返回网页title""" ele = self.ele('xpath://title') return ele.text if ele else None @property - def timeout(self) -> float: + def timeout(self): """返回查找元素时等待的秒数""" return self._timeout @timeout.setter - def timeout(self, second: float) -> None: + def timeout(self, second): """设置查找元素时等待的秒数""" self._timeout = second @property - def cookies(self) -> dict: + def cookies(self): """返回cookies""" return self.get_cookies(True) @property - def url_available(self) -> bool: + def url_available(self): """返回当前访问的url有效性""" return self._url_available - def _before_connect(self, url: str, retry: int, interval: float) -> tuple: + def _before_connect(self, url, retry, interval): """连接前的准备 \n :param url: 要访问的url :param retry: 重试次数 @@ -346,13 +346,9 @@ class BasePage(BaseParser): return @abstractmethod - def get_cookies(self, as_dict: bool = False): + def get_cookies(self, as_dict= False): return {} @abstractmethod - def get(self, - url: str, - show_errmsg: bool = False, - retry: int = None, - interval: float = None): + def get(self, url, show_errmsg= False, retry = None, interval= None): pass diff --git a/DrissionPage/base.pyi b/DrissionPage/base.pyi index 7621e46..26ff642 100644 --- a/DrissionPage/base.pyi +++ b/DrissionPage/base.pyi @@ -2,18 +2,13 @@ from abc import abstractmethod from typing import Union, Tuple, List -from selenium.webdriver.remote.webelement import WebElement - -from .driver_page import DriverPage -from .mix_page import MixPage - class BaseParser(object): """所有页面、元素类的基类""" - def __call__(self, loc_or_str): ... + def __call__(self, loc_or_str: Union[Tuple[str, str], str]): ... - def ele(self, loc_or_ele, timeout=...): ... + def ele(self, loc_or_ele: Union[Tuple[str, str], str, BaseElement], timeout:float=...): ... def eles(self, loc_or_str: Union[Tuple[str, str], str], timeout=...): ... @@ -21,12 +16,12 @@ class BaseParser(object): @property def html(self) -> str: ... - def s_ele(self, loc_or_ele): ... + def s_ele(self, loc_or_ele: Union[Tuple[str, str], str, BaseElement]): ... - def s_eles(self, loc_or_str): ... + def s_eles(self, loc_or_str: Union[Tuple[str, str], str]): ... @abstractmethod - def _ele(self, loc_or_ele, timeout=..., single=...): ... + def _ele(self, loc_or_ele, timeout:float=..., single:bool=...): ... class BaseElement(BaseParser): @@ -37,31 +32,23 @@ class BaseElement(BaseParser): # ----------------以下属性或方法由后代实现---------------- @property - def tag(self): - ... + def tag(self)->str: ... @property - def is_valid(self): - ... + def is_valid(self)->bool: ... @abstractmethod - def _ele(self, loc_or_ele, timeout=..., single=..., relative=...): - ... + def _ele(self, loc_or_str: Union[Tuple[str, str], str], timeout:float=..., single:bool=..., relative:bool=...): ... - def parent(self, level_or_loc: Union[tuple, str, int] = ...): - ... + def parent(self, level_or_loc: Union[tuple, str, int] = ...): ... - def prev(self, index: int = ...) -> None: - ... + def prev(self, index: int = ...) -> None: ... - def prevs(self) -> None: - ... + def prevs(self) -> None: ... - def next(self, index: int = ...): - ... + def next(self, index: int = ...): ... - def nexts(self): - ... + def nexts(self): ... class DrissionElement(BaseElement): @@ -72,98 +59,78 @@ class DrissionElement(BaseElement): self.page: BasePage = ... @property - def link(self) -> str: - ... + def link(self) -> str: ... @property - def css_path(self) -> str: - ... + def css_path(self) -> str: ... @property - def xpath(self) -> str: - ... + def xpath(self) -> str: ... @property - def comments(self) -> list: - ... + def comments(self) -> list: ... - def texts(self, text_node_only: bool = ...) -> list: - ... + def texts(self, text_node_only: bool = ...) -> list: ... - def parent(self, level_or_loc: Union[tuple, str, int] = ...) -> Union['DrissionElement', None]: - ... + def parent(self, level_or_loc: Union[tuple, str, int] = ...) -> Union['DrissionElement', None]: ... def prev(self, index: int = ..., filter_loc: Union[tuple, str] = ..., - timeout: float = ...) -> Union['DrissionElement', str, None]: - ... + timeout: float = ...) -> Union['DrissionElement', str, None]: ... def next(self, index: int = ..., filter_loc: Union[tuple, str] = ..., - timeout: float = ...) -> Union['DrissionElement', str, None]: - ... + timeout: float = ...) -> Union['DrissionElement', str, None]: ... def before(self, index: int = ..., filter_loc: Union[tuple, str] = ..., - timeout: float = ...) -> Union['DrissionElement', str, None]: - ... + timeout: float = ...) -> Union['DrissionElement', str, None]: ... def after(self, index: int = ..., filter_loc: Union[tuple, str] = ..., - timeout: float = ...) -> Union['DrissionElement', str, None]: - ... + timeout: float = ...) -> Union['DrissionElement', str, None]: ... def prevs(self, filter_loc: Union[tuple, str] = ..., - timeout: float = ...) -> List[Union['DrissionElement', str]]: - ... + timeout: float = ...) -> List[Union['DrissionElement', str]]: ... def nexts(self, filter_loc: Union[tuple, str] = ..., - timeout: float = ...) -> List[Union['DrissionElement', str]]: - ... + timeout: float = ...) -> List[Union['DrissionElement', str]]: ... def befores(self, filter_loc: Union[tuple, str] = ..., - timeout: float = ...) -> List[Union['DrissionElement', str]]: - ... + timeout: float = ...) -> List[Union['DrissionElement', str]]: ... def afters(self, filter_loc: Union[tuple, str] = ..., - timeout: float = ...) -> List[Union['DrissionElement', str]]: - ... + timeout: float = ...) -> List[Union['DrissionElement', str]]: ... def _get_brothers(self, index: int = ..., filter_loc: Union[tuple, str] = ..., direction: str = ..., brother: bool = ..., - timeout: float = ...) -> List[Union['DrissionElement', str]]: - ... + timeout: float = ...) -> List[Union['DrissionElement', str]]: ... # ----------------以下属性或方法由后代实现---------------- @property - def attrs(self) -> dict: - ... + def attrs(self) -> dict: ... @property - def text(self) -> str: - ... + def text(self) -> str: ... @property - def raw_text(self) -> str: - ... + def raw_text(self) -> str: ... @abstractmethod - def attr(self, attr: str) -> str: - ... + def attr(self, attr: str) -> str: ... - def _get_ele_path(self, mode) -> str: - ... + def _get_ele_path(self, mode) -> str: ... class BasePage(BaseParser): @@ -176,45 +143,35 @@ class BasePage(BaseParser): self._timeout = float = ... @property - def title(self) -> Union[str, None]: - ... + def title(self) -> Union[str, None]: ... @property - def timeout(self) -> float: - ... + def timeout(self) -> float: ... @timeout.setter - def timeout(self, second: float) -> None: - ... + def timeout(self, second: float) -> None: ... @property - def cookies(self) -> dict: - ... + def cookies(self) -> dict: ... @property - def url_available(self) -> bool: - ... + def url_available(self) -> bool: ... - def _before_connect(self, url: str, retry: int, interval: float) -> tuple: - ... + def _before_connect(self, url: str, retry: int, interval: float) -> tuple: ... # ----------------以下属性或方法由后代实现---------------- @property - def url(self): - ... + def url(self) -> str: ... @property - def json(self): - ... + def json(self) -> dict: ... @abstractmethod - def get_cookies(self, as_dict: bool = ...): - ... + def get_cookies(self, as_dict: bool = ...) -> Union[list, dict]: ... @abstractmethod def get(self, url: str, show_errmsg: bool = ..., retry: int = ..., - interval: float = ...): - ... + interval: float = ...): ... diff --git a/DrissionPage/chromium_base.py b/DrissionPage/chromium_base.py index c922f51..085f8cd 100644 --- a/DrissionPage/chromium_base.py +++ b/DrissionPage/chromium_base.py @@ -2,29 +2,24 @@ from json import loads from re import search from time import perf_counter, sleep -from typing import Union, Tuple, List, Any from urllib.parse import urlparse from requests import Session -from requests.cookies import RequestsCookieJar from .base import BasePage -from .chromium_element import ChromiumElementWaiter, ChromeScroll, ChromiumElement, _run_script +from .chromium_element import ChromiumElementWaiter, ChromeScroll, ChromiumElement, run_script from .common import get_loc -from .config import DriverOptions, _cookies_to_tuple -from .session_element import SessionElement, make_session_ele +from .config import cookies_to_tuple +from .session_element import make_session_ele from .tab import Tab class ChromiumBase(BasePage): """标签页、frame、页面基类""" - def __init__(self, - address: str, - tab_id: str = None, - timeout: float = None): + def __init__(self, address, tab_id=None, timeout=None): """初始化 \n - :param address: 浏览器地址:端口 + :param address: 浏览器 ip:port :param tab_id: 要控制的标签页id,不指定默认为激活的 :param timeout: 超时时间 """ @@ -34,9 +29,7 @@ class ChromiumBase(BasePage): self._debug = False self._connect_browser(address, tab_id) - def _connect_browser(self, - addr_tab_opts: Union[str, Tab, DriverOptions] = None, - tab_id: str = None) -> None: + def _connect_browser(self, addr_tab_opts=None, tab_id=None): """连接浏览器,在第一次时运行 \n :param addr_tab_opts: 浏览器地址、Tab对象或DriverOptions对象 :param tab_id: 要控制的标签页id,不指定默认为激活的 @@ -57,7 +50,7 @@ class ChromiumBase(BasePage): self._init_page(tab_id) self._get_document() - def _init_page(self, tab_id: str = None) -> None: + def _init_page(self, tab_id=None): """新建页面、页面刷新、切换标签页后要进行的cdp参数初始化 :param tab_id: 要跳转到的标签页id :return: None @@ -77,7 +70,7 @@ class ChromiumBase(BasePage): self._tab_obj.Page.loadEventFired = self._onLoadEventFired self._tab_obj.Page.frameNavigated = self._onFrameNavigated - def _get_document(self) -> None: + def _get_document(self): """刷新cdp使用的document数据""" if not self._is_reading: self._is_reading = True @@ -89,7 +82,7 @@ class ChromiumBase(BasePage): self._is_loading = False self._is_reading = False - def _wait_loading(self, timeout: float = None) -> bool: + def _wait_loading(self, timeout=None): """等待页面加载完成 :param timeout: 超时时间 :return: 是否成功,超时返回False @@ -147,11 +140,10 @@ class ChromiumBase(BasePage): if self._debug and not kwargs['frame'].get('parentId', None): print('navigated') - def _set_options(self) -> None: + def _set_options(self): pass - def __call__(self, loc_or_str: Union[Tuple[str, str], str, 'ChromiumElement'], - timeout: float = None) -> Union['ChromiumElement', 'ChromiumFrame', None]: + def __call__(self, loc_or_str, timeout=None): """在内部查找元素 \n 例:ele = page('@id=ele_id') \n :param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串 @@ -161,7 +153,7 @@ class ChromiumBase(BasePage): return self.ele(loc_or_str, timeout) @property - def driver(self) -> Tab: + def driver(self): """返回用于控制浏览器的Tab对象""" return self._tab_obj @@ -170,69 +162,69 @@ class ChromiumBase(BasePage): return self._tab_obj @property - def _wait_driver(self) -> Tab: + def _wait_driver(self): """返回用于控制浏览器的Tab对象,会先等待页面加载完毕""" while self._is_loading: sleep(.1) return self._tab_obj @property - def is_loading(self) -> bool: + def is_loading(self): """返回页面是否正在加载状态""" return self._is_loading @property - def url(self) -> str: + def url(self): """返回当前页面url""" json = self._control_session.get(f'http://{self.address}/json').json() return [i['url'] for i in json if i['id'] == self._tab_obj.id][0] # change_mode要调用,不能用_driver @property - def html(self) -> str: + def html(self): """返回当前页面html文本""" node_id = self._wait_driver.DOM.getDocument()['root']['nodeId'] return self._wait_driver.DOM.getOuterHTML(nodeId=node_id)['outerHTML'] @property - def json(self) -> dict: + def json(self): """当返回内容是json格式时,返回对应的字典""" return loads(self('t:pre').text) @property - def tab_id(self) -> str: + def tab_id(self): """返回当前标签页id""" return self.driver.id if self.driver.status == 'started' else '' @property - def ready_state(self) -> str: + def ready_state(self): """返回当前页面加载状态,'loading' 'interactive' 'complete'""" return self._tab_obj.Runtime.evaluate(expression='document.readyState;')['result']['value'] @property - def size(self) -> dict: + def size(self): """返回页面总长宽,{'height': int, 'width': int}""" w = self.run_script('document.body.scrollWidth;', as_expr=True) h = self.run_script('document.body.scrollHeight;', as_expr=True) return {'height': h, 'width': w} @property - def active_ele(self) -> ChromiumElement: + def active_ele(self): """返回当前焦点所在元素""" return self.run_script('return document.activeElement;') @property - def page_load_strategy(self) -> str: + def page_load_strategy(self): """返回页面加载策略""" return self._page_load_strategy @property - def scroll(self) -> 'ChromeScroll': + def scroll(self): """返回用于滚动滚动条的对象""" if not hasattr(self, '_scroll'): self._scroll = ChromeScroll(self) return self._scroll - def set_page_load_strategy(self, value: str) -> None: + def set_page_load_strategy(self, value): """设置页面加载策略 \n :param value: 可选'normal', 'eager', 'none' :return: None @@ -241,7 +233,7 @@ class ChromiumBase(BasePage): raise ValueError("只能选择'normal', 'eager', 'none'。") self._page_load_strategy = value - def set_timeouts(self, implicit: float = None, page_load: float = None, script: float = None) -> None: + def set_timeouts(self, implicit=None, page_load=None, script=None): """设置超时时间,单位为秒 \n :param implicit: 查找元素超时时间 :param page_load: 页面加载超时时间 @@ -257,16 +249,16 @@ class ChromiumBase(BasePage): if script is not None: self.timeouts.script = script - def run_script(self, script: str, as_expr: bool = False, *args: Any) -> Any: + def run_script(self, script, as_expr=False, *args): """运行javascript代码 \n :param script: js文本 :param as_expr: 是否作为表达式运行,为True时args无效 :param args: 参数,按顺序在js文本中对应argument[0]、argument[2]... :return: 运行的结果 """ - return _run_script(self, script, as_expr, self.timeouts.script, args) + return run_script(self, script, as_expr, self.timeouts.script, args) - def run_async_script(self, script: str, as_expr: bool = False, *args: Any) -> None: + def run_async_script(self, script, as_expr=False, *args): """以异步方式执行js代码 \n :param script: js文本 :param as_expr: 是否作为表达式运行,为True时args无效 @@ -274,14 +266,9 @@ class ChromiumBase(BasePage): :return: None """ from threading import Thread - Thread(target=_run_script, args=(self, script, as_expr, self.timeouts.script, args)).start() + Thread(target=run_script, args=(self, script, as_expr, self.timeouts.script, args)).start() - def get(self, - url: str, - show_errmsg: bool = False, - retry: int = None, - interval: float = None, - timeout: float = None) -> Union[None, bool]: + def get(self, url, show_errmsg=False, retry=None, interval=None, timeout=None): """访问url \n :param url: 目标url :param show_errmsg: 是否显示和抛出异常 @@ -293,13 +280,7 @@ class ChromiumBase(BasePage): self._url_available = self._get(url, show_errmsg, retry, interval, timeout) return self._url_available - def _get(self, - url: str, - show_errmsg: bool = False, - retry: int = None, - interval: float = None, - timeout: float = None, - frame_id: str = None) -> Union[None, bool]: + def _get(self, url: str, show_errmsg=False, retry=None, interval=None, timeout=None, frame_id=None): """访问url \n :param url: 目标url :param show_errmsg: 是否显示和抛出异常 @@ -316,7 +297,7 @@ class ChromiumBase(BasePage): timeout=timeout, frame_id=frame_id) - def get_cookies(self, as_dict: bool = False) -> Union[list, dict]: + def get_cookies(self, as_dict=False): """获取cookies信息 \n :param as_dict: 为True时返回由{name: value}键值对组成的dict :return: cookies信息 @@ -327,12 +308,12 @@ class ChromiumBase(BasePage): else: return cookies - def set_cookies(self, cookies: Union[RequestsCookieJar, list, tuple, str, dict]) -> None: + def set_cookies(self, cookies): """设置cookies值 \n :param cookies: cookies信息 :return: None """ - cookies = _cookies_to_tuple(cookies) + cookies = cookies_to_tuple(cookies) result_cookies = [] for cookie in cookies: if not cookie.get('domain', None): @@ -350,9 +331,7 @@ class ChromiumBase(BasePage): # """ # self.run_cdp('Network.setExtraHTTPHeaders', headers=headers) - def ele(self, - loc_or_ele: Union[Tuple[str, str], str, ChromiumElement, 'ChromiumFrame'], - timeout: float = None) -> Union[ChromiumElement, 'ChromiumFrame', None]: + def ele(self, loc_or_ele, timeout=None): """获取第一个符合条件的元素对象 \n :param loc_or_ele: 定位符或元素对象 :param timeout: 查找超时时间 @@ -360,9 +339,7 @@ class ChromiumBase(BasePage): """ return self._ele(loc_or_ele, timeout=timeout) - def eles(self, - loc_or_ele: Union[Tuple[str, str], str], - timeout: float = None) -> List[Union[ChromiumElement, 'ChromiumFrame']]: + def eles(self, loc_or_ele, timeout=None): """获取所有符合条件的元素对象 \n :param loc_or_ele: 定位符或元素对象 :param timeout: 查找超时时间 @@ -370,8 +347,7 @@ class ChromiumBase(BasePage): """ return self._ele(loc_or_ele, timeout=timeout, single=False) - def s_ele(self, loc_or_ele: Union[Tuple[str, str], str, ChromiumElement] = None) \ - -> Union[SessionElement, str, None]: + def s_ele(self, loc_or_ele=None): """查找第一个符合条件的元素以SessionElement形式返回,处理复杂页面时效率很高 \n :param loc_or_ele: 元素的定位信息,可以是loc元组,或查询字符串 :return: SessionElement对象或属性、文本 @@ -381,17 +357,14 @@ class ChromiumBase(BasePage): else: return make_session_ele(self, loc_or_ele) - def s_eles(self, loc_or_str: Union[Tuple[str, str], str] = None) -> List[Union[SessionElement, str]]: + def s_eles(self, loc_or_str=None): """查找所有符合条件的元素以SessionElement列表形式返回 \n :param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串 :return: SessionElement对象组成的列表 """ return make_session_ele(self, loc_or_str, single=False) - def _ele(self, - loc_or_ele: Union[Tuple[str, str], str, ChromiumElement, 'ChromiumFrame'], - timeout: float = None, single: bool = True, relative: bool = False) \ - -> Union[ChromiumElement, 'ChromiumFrame', None, List[Union[ChromiumElement, 'ChromiumFrame']]]: + def _ele(self, loc_or_ele, timeout=None, single=True, relative=False): """执行元素查找 :param loc_or_ele: 定位符或元素对象 :param timeout: 查找超时时间 @@ -434,9 +407,7 @@ class ChromiumBase(BasePage): return eles[0] if single else eles - def wait_ele(self, - loc_or_ele: Union[str, tuple, ChromiumElement], - timeout: float = None) -> 'ChromiumElementWaiter': + def wait_ele(self, loc_or_ele, timeout=None): """返回用于等待元素到达某个状态的等待器对象 \n :param loc_or_ele: 可以是元素、查询字符串、loc元组 :param timeout: 等待超时时间 @@ -444,7 +415,7 @@ class ChromiumBase(BasePage): """ return ChromiumElementWaiter(self, loc_or_ele, timeout) - def scroll_to_see(self, loc_or_ele: Union[str, tuple, ChromiumElement]) -> None: + def scroll_to_see(self, loc_or_ele): """滚动页面直到元素可见 \n :param loc_or_ele: 元素的定位信息,可以是loc元组,或查询字符串(详见ele函数注释) :return: None @@ -455,7 +426,7 @@ class ChromiumBase(BasePage): except Exception: self.ele(loc_or_ele).run_script("this.scrollIntoView();") - def refresh(self, ignore_cache: bool = False) -> None: + def refresh(self, ignore_cache=False): """刷新当前页面 \n :param ignore_cache: 是否忽略缓存 :return: None @@ -463,21 +434,21 @@ class ChromiumBase(BasePage): self._is_loading = True self._driver.Page.reload(ignoreCache=ignore_cache) - def forward(self, steps: int = 1) -> None: + def forward(self, steps=1): """在浏览历史中前进若干步 \n :param steps: 前进步数 :return: None """ self._forward_or_back(steps) - def back(self, steps: int = 1) -> None: + def back(self, steps=1): """在浏览历史中后退若干步 \n :param steps: 后退步数 :return: None """ self._forward_or_back(-steps) - def _forward_or_back(self, steps: int) -> None: + def _forward_or_back(self, steps): """执行浏览器前进或后退,会跳过url相同的历史记录 :param steps: 步数 :return: None @@ -503,7 +474,7 @@ class ChromiumBase(BasePage): self._is_loading = True self.run_cdp('Page.navigateToHistoryEntry', entryId=nid) - def stop_loading(self) -> None: + def stop_loading(self): """页面停止加载""" if self._debug: print('阻止页面加载') @@ -512,7 +483,7 @@ class ChromiumBase(BasePage): sleep(.1) # self._get_document() - def run_cdp(self, cmd: str, **cmd_args) -> dict: + def run_cdp(self, cmd, **cmd_args): """执行Chrome DevTools Protocol语句 \n :param cmd: 协议项目 :param cmd_args: 参数 @@ -525,14 +496,14 @@ class ChromiumBase(BasePage): raise RuntimeError('该元素已不在当前页面中。') raise - def set_user_agent(self, ua: str) -> None: + def set_user_agent(self, ua): """为当前tab设置user agent,只在当前tab有效 \n :param ua: user agent字符串 :return: None """ self._wait_driver.Network.setUserAgentOverride(userAgent=ua) - def get_session_storage(self, item: str = None) -> Union[str, dict, None]: + def get_session_storage(self, item=None): """获取sessionStorage信息,不设置item则获取全部 \n :param item: 要获取的项,不设置则返回全部 :return: sessionStorage一个或所有项内容 @@ -540,7 +511,7 @@ class ChromiumBase(BasePage): js = f'sessionStorage.getItem("{item}");' if item else 'sessionStorage;' return self.run_script(js, as_expr=True) - def get_local_storage(self, item: str = None) -> Union[str, dict, None]: + def get_local_storage(self, item=None): """获取localStorage信息,不设置item则获取全部 \n :param item: 要获取的项目,不设置则返回全部 :return: localStorage一个或所有项内容 @@ -548,7 +519,7 @@ class ChromiumBase(BasePage): js = f'localStorage.getItem("{item}");' if item else 'localStorage;' return self.run_script(js, as_expr=True) - def set_session_storage(self, item: str, value: Union[str, bool]) -> None: + def set_session_storage(self, item, value): """设置或删除某项sessionStorage信息 \n :param item: 要设置的项 :param value: 项的值,设置为False时,删除该项 @@ -557,7 +528,7 @@ class ChromiumBase(BasePage): js = f'sessionStorage.removeItem("{item}");' if item is False else f'sessionStorage.setItem("{item}","{value}");' return self.run_script(js, as_expr=True) - def set_local_storage(self, item: str, value: Union[str, bool]) -> None: + def set_local_storage(self, item, value): """设置或删除某项localStorage信息 \n :param item: 要设置的项 :param value: 项的值,设置为False时,删除该项 @@ -566,11 +537,7 @@ class ChromiumBase(BasePage): js = f'localStorage.removeItem("{item}");' if item is False else f'localStorage.setItem("{item}","{value}");' return self.run_script(js, as_expr=True) - def clear_cache(self, - session_storage: bool = True, - local_storage: bool = True, - cache: bool = True, - cookies: bool = True) -> None: + def clear_cache(self, session_storage=True, local_storage=True, cache=True, cookies=True): """清除缓存,可选要清除的项 \n :param session_storage: 是否清除sessionStorage :param local_storage: 是否清除localStorage @@ -587,13 +554,7 @@ class ChromiumBase(BasePage): if cookies: self._wait_driver.Network.clearBrowserCookies() - def _d_connect(self, - to_url: str, - times: int = 0, - interval: float = 1, - show_errmsg: bool = False, - timeout: float = None, - frame_id: str = None) -> Union[bool, None]: + def _d_connect(self, to_url, times=0, interval=1, show_errmsg=False, timeout=None, frame_id=None): """尝试连接,重试若干次 \n :param to_url: 要访问的url :param times: 重试次数 @@ -644,10 +605,9 @@ class ChromiumBase(BasePage): class ChromiumFrame(ChromiumBase): """实现浏览器frame的类""" - def __init__(self, page, - ele: ChromiumElement): + def __init__(self, page, ele): """初始化 \n - :param page: 浏览器地址:端口、Tab对象或DriverOptions对象 + :param page: 页面对象 :param ele: 页面上的frame元素 """ self.page = page @@ -655,18 +615,18 @@ class ChromiumFrame(ChromiumBase): frame_id = page.run_cdp('DOM.describeNode', nodeId=ele.node_id)['node'].get('frameId', None) super().__init__(page.address, frame_id, page.timeout) - def __repr__(self) -> str: + def __repr__(self): attrs = self.attrs attrs = [f"{attr}='{attrs[attr]}'" for attr in attrs] return f'' @property - def tag(self) -> str: + def tag(self): """返回元素tag""" return self._inner_ele.tag @property - def html(self) -> str: + def html(self): """返回元素outerHTML文本""" tag = self.tag out_html = self.page.run_cdp('DOM.getOuterHTML', nodeId=self._inner_ele.node_id)['outerHTML'] @@ -675,53 +635,53 @@ class ChromiumFrame(ChromiumBase): return f'{sign}{in_html}' @property - def inner_html(self) -> str: + def inner_html(self): """返回元素innerHTML文本""" return super().html @property - def attrs(self) -> dict: + def attrs(self): return self._inner_ele.attrs @property - def frame_size(self) -> dict: + def frame_size(self): """返回frame元素大小""" return self._inner_ele.size - def _set_options(self) -> None: + def _set_options(self): self.set_timeouts(page_load=self.page.timeouts.page_load, script=self.page.timeouts.script, implicit=self.page.timeouts.implicit if self.timeout is None else self.timeout) self._page_load_strategy = self.page.page_load_strategy @property - def obj_id(self) -> str: + def obj_id(self): """返回js中的object id""" return self._inner_ele.obj_id @property - def node_id(self) -> str: + def node_id(self): """返回cdp中的node id""" return self._inner_ele.node_id @property - def location(self) -> dict: + def location(self): """返回frame元素左上角的绝对坐标""" return self._inner_ele.location @property - def is_displayed(self) -> bool: + def is_displayed(self): """返回frame元素是否显示""" return self._inner_ele.is_displayed - def attr(self, attr: str) -> Union[str, None]: + def attr(self, attr): """返回frame元素attribute属性值 \n :param attr: 属性名 :return: 属性值文本,没有该属性返回None """ return self._inner_ele.attr(attr) - def set_attr(self, attr: str, value: str) -> None: + def set_attr(self, attr, value): """设置frame元素attribute属性 \n :param attr: 属性名 :param value: 属性值 @@ -729,24 +689,21 @@ class ChromiumFrame(ChromiumBase): """ self._inner_ele.set_attr(attr, value) - def remove_attr(self, attr: str) -> None: + def remove_attr(self, attr): """删除frame元素attribute属性 \n :param attr: 属性名 :return: None """ self._inner_ele.remove_attr(attr) - def parent(self, level_or_loc: Union[tuple, str, int] = 1) -> Union['ChromiumElement', None]: + def parent(self, level_or_loc=1): """返回上面某一级父元素,可指定层数或用查询语法定位 \n :param level_or_loc: 第几级父元素,或定位符 :return: 上级元素对象 """ return self._inner_ele.parent(level_or_loc) - def prev(self, - index: int = 1, - filter_loc: Union[tuple, str] = '', - timeout: float = 0) -> Union['ChromiumElement', str, None]: + def prev(self, index=1, filter_loc='', timeout=0): """返回前面的一个兄弟元素,可用查询语法筛选,可指定返回筛选结果的第几个 \n :param index: 前面第几个查询结果元素 :param filter_loc: 用于筛选元素的查询语法 @@ -755,10 +712,7 @@ class ChromiumFrame(ChromiumBase): """ return self._inner_ele.prev(index, filter_loc, timeout) - def next(self, - index: int = 1, - filter_loc: Union[tuple, str] = '', - timeout: float = 0) -> Union['ChromiumElement', str, None]: + def next(self, index=1, filter_loc='', timeout=0): """返回后面的一个兄弟元素,可用查询语法筛选,可指定返回筛选结果的第几个 \n :param index: 后面第几个查询结果元素 :param filter_loc: 用于筛选元素的查询语法 @@ -767,10 +721,7 @@ class ChromiumFrame(ChromiumBase): """ return self._inner_ele.next(index, filter_loc, timeout) - def before(self, - index: int = 1, - filter_loc: Union[tuple, str] = '', - timeout: float = None) -> Union['ChromiumElement', str, None]: + def before(self, index=1, filter_loc='', timeout=None): """返回当前元素前面的一个元素,可指定筛选条件和第几个。查找范围不限兄弟元素,而是整个DOM文档 \n :param index: 前面第几个查询结果元素 :param filter_loc: 用于筛选元素的查询语法 @@ -779,10 +730,7 @@ class ChromiumFrame(ChromiumBase): """ return self._inner_ele.before(index, filter_loc, timeout) - def after(self, - index: int = 1, - filter_loc: Union[tuple, str] = '', - timeout: float = None) -> Union['ChromiumElement', str, None]: + def after(self, index=1, filter_loc='', timeout=None): """返回当前元素后面的一个元素,可指定筛选条件和第几个。查找范围不限兄弟元素,而是整个DOM文档 \n :param index: 后面第几个查询结果元素 :param filter_loc: 用于筛选元素的查询语法 @@ -791,9 +739,7 @@ class ChromiumFrame(ChromiumBase): """ return self._inner_ele.after(index, filter_loc, timeout) - def prevs(self, - filter_loc: Union[tuple, str] = '', - timeout: float = 0) -> List[Union['ChromiumElement', str]]: + def prevs(self, filter_loc='', timeout=0): """返回前面全部兄弟元素或节点组成的列表,可用查询语法筛选 \n :param filter_loc: 用于筛选元素的查询语法 :param timeout: 查找元素的超时时间 @@ -801,9 +747,7 @@ class ChromiumFrame(ChromiumBase): """ return self._inner_ele.prevs(filter_loc, timeout) - def nexts(self, - filter_loc: Union[tuple, str] = '', - timeout: float = 0) -> List[Union['ChromiumElement', str]]: + def nexts(self, filter_loc='', timeout=0): """返回后面全部兄弟元素或节点组成的列表,可用查询语法筛选 \n :param filter_loc: 用于筛选元素的查询语法 :param timeout: 查找元素的超时时间 @@ -811,9 +755,7 @@ class ChromiumFrame(ChromiumBase): """ return self._inner_ele.nexts(filter_loc, timeout) - def befores(self, - filter_loc: Union[tuple, str] = '', - timeout: float = None) -> List[Union['ChromiumElement', str]]: + def befores(self, filter_loc='', timeout=None): """返回当前元素后面符合条件的全部兄弟元素或节点组成的列表,可用查询语法筛选。查找范围不限兄弟元素,而是整个DOM文档 \n :param filter_loc: 用于筛选元素的查询语法 :param timeout: 查找元素的超时时间 diff --git a/DrissionPage/chromium_base.pyi b/DrissionPage/chromium_base.pyi index 8efbf5c..460de0a 100644 --- a/DrissionPage/chromium_base.pyi +++ b/DrissionPage/chromium_base.pyi @@ -30,6 +30,7 @@ class ChromiumBase(BasePage): self._page_load_strategy: str = ... self._scroll: ChromeScroll = ... self._url: str = ... + self._root_id: str = ... def _connect_browser(self, addr_tab_opts: Union[str, Tab, DriverOptions] = ..., @@ -60,7 +61,7 @@ class ChromiumBase(BasePage): def driver(self) -> Tab: ... @property - def _driver(self): ... + def _driver(self) -> Tab: ... @property def _wait_driver(self) -> Tab: ... @@ -186,7 +187,7 @@ class ChromiumBase(BasePage): class ChromiumFrame(ChromiumBase): """实现浏览器frame的类""" - def __init__(self, page, + def __init__(self, page: ChromiumBase, ele: ChromiumElement): self._inner_ele: ChromiumElement = ... self.page: ChromiumBase = ... diff --git a/DrissionPage/chromium_element.py b/DrissionPage/chromium_element.py index 4435996..3db754d 100644 --- a/DrissionPage/chromium_element.py +++ b/DrissionPage/chromium_element.py @@ -9,19 +9,18 @@ from os.path import basename from pathlib import Path from re import search from time import perf_counter, sleep -from typing import Union, Tuple, List, Any from urllib.parse import urlparse from .base import DrissionElement, BaseElement from .common import make_absolute_link, get_loc, get_ele_txt, format_html, is_js_func, _location_in_viewport from .keys import _keys_to_typing, _keyDescriptionForString, _keyDefinitions -from .session_element import make_session_ele, SessionElement +from .session_element import make_session_ele class ChromiumElement(DrissionElement): """ChromePage页面对象中的元素对象""" - def __init__(self, page, node_id: str = None, obj_id: str = None): + def __init__(self, page, node_id=None, obj_id=None): """初始化,node_id和obj_id必须至少传入一个 \n :param page: 元素所在ChromePage页面对象 :param node_id: cdp中的node id @@ -41,14 +40,12 @@ class ChromiumElement(DrissionElement): self._node_id = self._get_node_id(obj_id) self._obj_id = obj_id - def __repr__(self) -> str: + def __repr__(self): attrs = self.attrs attrs = [f"{attr}='{attrs[attr]}'" for attr in attrs] return f'' - def __call__(self, - loc_or_str: Union[Tuple[str, str], str], - timeout: float = None) -> Union['ChromiumElement', str, None]: + def __call__(self, loc_or_str, timeout=None): """在内部查找元素 \n 例:ele2 = ele1('@id=ele_id') \n :param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串 @@ -58,14 +55,14 @@ class ChromiumElement(DrissionElement): return self.ele(loc_or_str, timeout) @property - def tag(self) -> str: + def tag(self): """返回元素tag""" if self._tag is None: self._tag = self.page.run_cdp('DOM.describeNode', nodeId=self._node_id)['node']['localName'].lower() return self._tag @property - def html(self) -> str: + def html(self): """返回元素outerHTML文本""" tag = self.tag if tag in ('iframe', 'frame'): @@ -76,7 +73,7 @@ class ChromiumElement(DrissionElement): return self.page.run_cdp('DOM.getOuterHTML', nodeId=self._node_id)['outerHTML'] @property - def inner_html(self) -> str: + def inner_html(self): """返回元素innerHTML文本""" if self.tag in ('iframe', 'frame'): return self.run_script('return this.contentDocument.documentElement;').html @@ -84,35 +81,35 @@ class ChromiumElement(DrissionElement): return self.run_script('return this.innerHTML;') @property - def attrs(self) -> dict: + def attrs(self): """返回元素所有attribute属性""" attrs = self.page.run_cdp('DOM.getAttributes', nodeId=self._node_id)['attributes'] attrs_len = len(attrs) return {attrs[i]: attrs[i + 1] for i in range(0, attrs_len, 2)} @property - def text(self) -> str: + def text(self): """返回元素内所有文本,文本已格式化""" return get_ele_txt(make_session_ele(self.html)) @property - def raw_text(self) -> str: + def raw_text(self): """返回未格式化处理的元素内文本""" return self.prop('innerText') # -----------------d模式独有属性------------------- @property - def obj_id(self) -> str: + def obj_id(self): """返回js中的object id""" return self._obj_id @property - def node_id(self) -> str: + def node_id(self): """返回cdp中的node id""" return self._node_id @property - def size(self) -> dict: + def size(self): """返回元素宽和高""" try: model = self.page.run_cdp('DOM.getBoxModel', nodeId=self._node_id)['model'] @@ -121,43 +118,43 @@ class ChromiumElement(DrissionElement): return {'height': 0, 'width': 0} @property - def client_location(self) -> Union[dict, None]: + def client_location(self): """返回元素左上角在视口中的坐标""" m = self._get_client_rect('border') return {'x': m[0], 'y': m[1]} if m else None @property - def client_midpoint(self) -> Union[dict, None]: + def client_midpoint(self): """返回元素中间点在视口中的坐标""" m = self._get_client_rect('border') return {'x': m[2] - m[0], 'y': m[5] - m[1]} if m else None @property - def location(self) -> Union[dict, None]: + def location(self): """返回元素左上角的绝对坐标""" cl = self.client_location return self._get_absolute_rect(cl['x'], cl['y']) if cl else None @property - def midpoint(self) -> dict: + def midpoint(self): """返回元素中间点的绝对坐标""" cl = self.client_midpoint return self._get_absolute_rect(cl['x'], cl['y']) if cl else None @property - def _client_click_point(self) -> Union[dict, None]: + def _client_click_point(self): """返回元素左上角可接受点击的点视口坐标""" m = self._get_client_rect('padding') return {'x': m[0], 'y': m[1]} if m else None @property - def _click_point(self) -> Union[dict, None]: + def _click_point(self): """返回元素左上角可接受点击的点的绝对坐标""" cl = self._client_click_point return self._get_absolute_rect(cl['x'], cl['y']) if cl else None @property - def shadow_root(self) -> Union[None, 'ChromiumShadowRootElement']: + def shadow_root(self): """返回当前元素的shadow_root元素对象""" info = self.page.run_cdp('DOM.describeNode', nodeId=self.node_id)['node'] if not info.get('shadowRoots', None): @@ -171,33 +168,30 @@ class ChromiumElement(DrissionElement): return self.shadow_root @property - def pseudo_before(self) -> str: + def pseudo_before(self): """返回当前元素的::before伪元素内容""" return self.style('content', 'before') @property - def pseudo_after(self) -> str: + def pseudo_after(self): """返回当前元素的::after伪元素内容""" return self.style('content', 'after') @property - def scroll(self) -> 'ChromeScroll': + def scroll(self): """用于滚动滚动条的对象""" if self._scroll is None: self._scroll = ChromeScroll(self) return self._scroll - def parent(self, level_or_loc: Union[tuple, str, int] = 1) -> Union['ChromiumElement', None]: + def parent(self, level_or_loc=1): """返回上面某一级父元素,可指定层数或用查询语法定位 \n :param level_or_loc: 第几级父元素,或定位符 :return: 上级元素对象 """ return super().parent(level_or_loc) - def prev(self, - index: int = 1, - filter_loc: Union[tuple, str] = '', - timeout: float = 0) -> Union['ChromiumElement', str, None]: + def prev(self, index=1, filter_loc='', timeout=0): """返回前面的一个兄弟元素,可用查询语法筛选,可指定返回筛选结果的第几个 \n :param index: 前面第几个查询结果元素 :param filter_loc: 用于筛选元素的查询语法 @@ -206,10 +200,7 @@ class ChromiumElement(DrissionElement): """ return super().prev(index, filter_loc, timeout) - def next(self, - index: int = 1, - filter_loc: Union[tuple, str] = '', - timeout: float = 0) -> Union['ChromiumElement', str, None]: + def next(self, index=1, filter_loc='', timeout=0): """返回后面的一个兄弟元素,可用查询语法筛选,可指定返回筛选结果的第几个 \n :param index: 后面第几个查询结果元素 :param filter_loc: 用于筛选元素的查询语法 @@ -218,10 +209,7 @@ class ChromiumElement(DrissionElement): """ return super().next(index, filter_loc, timeout) - def before(self, - index: int = 1, - filter_loc: Union[tuple, str] = '', - timeout: float = None) -> Union['ChromiumElement', str, None]: + def before(self, index=1, filter_loc='', timeout=None): """返回当前元素前面的一个元素,可指定筛选条件和第几个。查找范围不限兄弟元素,而是整个DOM文档 \n :param index: 前面第几个查询结果元素 :param filter_loc: 用于筛选元素的查询语法 @@ -230,10 +218,7 @@ class ChromiumElement(DrissionElement): """ return super().before(index, filter_loc, timeout) - def after(self, - index: int = 1, - filter_loc: Union[tuple, str] = '', - timeout: float = None) -> Union['ChromiumElement', str, None]: + def after(self, index=1, filter_loc='', timeout=None): """返回当前元素后面的一个元素,可指定筛选条件和第几个。查找范围不限兄弟元素,而是整个DOM文档 \n :param index: 后面第几个查询结果元素 :param filter_loc: 用于筛选元素的查询语法 @@ -242,9 +227,7 @@ class ChromiumElement(DrissionElement): """ return super().after(index, filter_loc, timeout) - def prevs(self, - filter_loc: Union[tuple, str] = '', - timeout: float = 0) -> List[Union['ChromiumElement', str]]: + def prevs(self, filter_loc='', timeout=0): """返回前面全部兄弟元素或节点组成的列表,可用查询语法筛选 \n :param filter_loc: 用于筛选元素的查询语法 :param timeout: 查找元素的超时时间 @@ -252,9 +235,7 @@ class ChromiumElement(DrissionElement): """ return super().prevs(filter_loc, timeout) - def nexts(self, - filter_loc: Union[tuple, str] = '', - timeout: float = 0) -> List[Union['ChromiumElement', str]]: + def nexts(self, filter_loc='', timeout=0): """返回后面全部兄弟元素或节点组成的列表,可用查询语法筛选 \n :param filter_loc: 用于筛选元素的查询语法 :param timeout: 查找元素的超时时间 @@ -262,9 +243,7 @@ class ChromiumElement(DrissionElement): """ return super().nexts(filter_loc, timeout) - def befores(self, - filter_loc: Union[tuple, str] = '', - timeout: float = None) -> List[Union['ChromiumElement', str]]: + def befores(self, filter_loc='', timeout=None): """返回当前元素后面符合条件的全部兄弟元素或节点组成的列表,可用查询语法筛选。查找范围不限兄弟元素,而是整个DOM文档 \n :param filter_loc: 用于筛选元素的查询语法 :param timeout: 查找元素的超时时间 @@ -272,9 +251,7 @@ class ChromiumElement(DrissionElement): """ return super().befores(filter_loc, timeout) - def wait_ele(self, - loc_or_ele: Union[str, tuple, 'ChromiumElement'], - timeout: float = None) -> 'ChromiumElementWaiter': + def wait_ele(self, loc_or_ele, timeout=None): """返回用于等待子元素到达某个状态的等待器对象 \n :param loc_or_ele: 可以是元素、查询字符串、loc元组 :param timeout: 等待超时时间 @@ -283,7 +260,7 @@ class ChromiumElement(DrissionElement): return ChromiumElementWaiter(self, loc_or_ele, timeout) @property - def select(self) -> 'ChromeSelect': + def select(self): """返回专门处理下拉列表的Select类,非下拉列表元素返回False""" if self._select is None: if self.tag != 'select': @@ -294,24 +271,24 @@ class ChromiumElement(DrissionElement): return self._select @property - def is_selected(self) -> bool: + def is_selected(self): """返回元素是否被选择""" return self.run_script('return this.selected;') @property - def is_displayed(self) -> bool: + def is_displayed(self): """返回元素是否显示""" return not (self.style('visibility') == 'hidden' or self.run_script('return this.offsetParent === null;') or self.style('display') == 'none') @property - def is_enabled(self) -> bool: + def is_enabled(self): """返回元素是否可用""" return not self.run_script('return this.disabled;') @property - def is_alive(self) -> bool: + def is_alive(self): """返回元素是否仍在DOM中""" try: d = self.attrs @@ -320,12 +297,12 @@ class ChromiumElement(DrissionElement): return False @property - def is_in_viewport(self) -> bool: + def is_in_viewport(self): """返回元素是否出现在视口中,以元素可以接受点击的点为判断""" loc = self.location return _location_in_viewport(self.page, loc['x'], loc['y']) if loc else False - def attr(self, attr: str) -> Union[str, None]: + def attr(self, attr): """返回attribute属性值 \n :param attr: 属性名 :return: 属性值文本,没有该属性返回None @@ -356,7 +333,7 @@ class ChromiumElement(DrissionElement): else: return attrs.get(attr, None) - def set_attr(self, attr: str, value: str) -> None: + def set_attr(self, attr, value): """设置元素attribute属性 \n :param attr: 属性名 :param value: 属性值 @@ -364,14 +341,14 @@ class ChromiumElement(DrissionElement): """ self.run_script(f'this.setAttribute(arguments[0], arguments[1]);', False, attr, str(value)) - def remove_attr(self, attr: str) -> None: + def remove_attr(self, attr): """删除元素attribute属性 \n :param attr: 属性名 :return: None """ self.run_script(f'this.removeAttribute("{attr}");') - def prop(self, prop: str) -> Union[str, int, None]: + def prop(self, prop): """获取property属性值 \n :param prop: 属性名 :return: 属性值文本 @@ -384,7 +361,7 @@ class ChromiumElement(DrissionElement): return format_html(i['value']['value']) - def set_prop(self, prop: str, value: str) -> None: + def set_prop(self, prop, value): """设置元素property属性 \n :param prop: 属性名 :param value: 属性值 @@ -393,23 +370,23 @@ class ChromiumElement(DrissionElement): value = value.replace('"', r'\"') self.run_script(f'this.{prop}="{value}";') - def set_innerHTML(self, html: str) -> None: + def set_innerHTML(self, html): """设置元素innerHTML \n :param html: html文本 :return: None """ self.set_prop('innerHTML', html) - def run_script(self, script: str, as_expr: bool = False, *args: Any) -> Any: + def run_script(self, script, as_expr=False, *args): """运行javascript代码 \n :param script: js文本 :param as_expr: 是否作为表达式运行,为True时args无效 :param args: 参数,按顺序在js文本中对应argument[0]、argument[2]... :return: 运行的结果 """ - return _run_script(self, script, as_expr, self.page.timeouts.script, args) + return run_script(self, script, as_expr, self.page.timeouts.script, args) - def run_async_script(self, script: str, as_expr: bool = False, *args: Any) -> None: + def run_async_script(self, script, as_expr=False, *args): """以异步方式执行js代码 \n :param script: js文本 :param as_expr: 是否作为表达式运行,为True时args无效 @@ -417,11 +394,9 @@ class ChromiumElement(DrissionElement): :return: None """ from threading import Thread - Thread(target=_run_script, args=(self, script, as_expr, self.page.timeouts.script, args)).start() + Thread(target=run_script, args=(self, script, as_expr, self.page.timeouts.script, args)).start() - def ele(self, - loc_or_str: Union[Tuple[str, str], str], - timeout: float = None) -> Union['ChromiumElement', str, None]: + def ele(self, loc_or_str, timeout=None): """返回当前元素下级符合条件的第一个元素、属性或节点文本 \n :param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串 :param timeout: 查找元素超时时间,默认与元素所在页面等待时间一致 @@ -429,9 +404,7 @@ class ChromiumElement(DrissionElement): """ return self._ele(loc_or_str, timeout) - def eles(self, - loc_or_str: Union[Tuple[str, str], str], - timeout: float = None) -> List[Union['ChromiumElement', str]]: + def eles(self, loc_or_str, timeout=None): """返回当前元素下级所有符合条件的子元素、属性或节点文本 \n :param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串 :param timeout: 查找元素超时时间,默认与元素所在页面等待时间一致 @@ -439,7 +412,7 @@ class ChromiumElement(DrissionElement): """ return self._ele(loc_or_str, timeout=timeout, single=False) - def s_ele(self, loc_or_str: Union[Tuple[str, str], str] = None) -> Union[SessionElement, str, None]: + def s_ele(self, loc_or_str=None): """查找第一个符合条件的元素以SessionElement形式返回,处理复杂页面时效率很高 \n :param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串 :return: SessionElement对象或属性、文本 @@ -448,7 +421,7 @@ class ChromiumElement(DrissionElement): return make_session_ele(self.inner_html, loc_or_str) return make_session_ele(self, loc_or_str) - def s_eles(self, loc_or_str: Union[Tuple[str, str], str] = None) -> List[Union[SessionElement, str]]: + def s_eles(self, loc_or_str=None): """查找所有符合条件的元素以SessionElement列表形式返回 \n :param loc_or_str: 定位符 :return: SessionElement或属性、文本组成的列表 @@ -457,11 +430,7 @@ class ChromiumElement(DrissionElement): return make_session_ele(self.inner_html, loc_or_str, single=False) return make_session_ele(self, loc_or_str, single=False) - def _ele(self, - loc_or_str: Union[Tuple[str, str], str], - timeout: float = None, single: bool = True, relative=False) \ - -> Union['ChromiumElement', str, None, - List[Union['ChromiumElement', str]]]: + def _ele(self, loc_or_str, timeout=None, single=True, relative=False): """返回当前元素下级符合条件的子元素、属性或节点文本,默认返回第一个 \n :param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串 :param timeout: 查找元素超时时间 @@ -470,7 +439,7 @@ class ChromiumElement(DrissionElement): """ return make_chromium_ele(self, loc_or_str, single, timeout, relative=relative) - def style(self, style: str, pseudo_ele: str = '') -> str: + def style(self, style, pseudo_ele=''): """返回元素样式属性值,可获取伪元素属性值 \n :param style: 样式属性名称 :param pseudo_ele: 伪元素名称(如有) @@ -481,7 +450,7 @@ class ChromiumElement(DrissionElement): js = f'return window.getComputedStyle(this{pseudo_ele}).getPropertyValue("{style}");' return self.run_script(js) - def get_src(self) -> Union[bytes, str, None]: + def get_src(self): """返回元素src资源,base64的会转为bytes返回,其它返回str""" src = self.attr('src') if not src: @@ -510,7 +479,7 @@ class ChromiumElement(DrissionElement): data = result['content'] return data - def save(self, path: [str, bool] = None, rename: str = None) -> None: + def save(self, path=None, rename=None): """保存图片或其它有src属性的元素的资源 \n :param path: 文件保存路径,为None时保存到当前文件夹,为False时不保存 :param rename: 文件名称,为None时从资源url获取 @@ -528,8 +497,7 @@ class ChromiumElement(DrissionElement): with open(f'{path}{sep}{rename}', write_type) as f: f.write(data) - def get_screenshot(self, path: [str, Path] = None, - as_bytes: [bool, str] = None) -> Union[str, bytes]: + def get_screenshot(self, path=None, as_bytes=None): """对当前元素截图 \n :param path: 完整路径,后缀可选 'jpg','jpeg','png','webp' :param as_bytes: 是否已字节形式返回图片,可选 'jpg','jpeg','png','webp',生效时path参数无效 @@ -550,8 +518,7 @@ class ChromiumElement(DrissionElement): return self.page.get_screenshot(path, as_bytes=as_bytes, full_page=False, left_top=left_top, right_bottom=right_bottom) - def input(self, vals: Union[str, tuple, list], - clear: bool = True) -> None: + def input(self, vals, clear=True): """输入文本或组合键,也可用于输入文件路径到input元素(文件间用\n间隔) \n :param vals: 文本值或按键组合 :param clear: 输入前是否清空文本框 @@ -584,7 +551,7 @@ class ChromiumElement(DrissionElement): else: self.page.driver.Input.insertText(text=vals) - def _set_file_input(self, files: Union[str, list, tuple]) -> None: + def _set_file_input(self, files): """设置上传控件值 :param files: 文件路径列表或字符串,字符串时多个文件用回车分隔 :return: None @@ -593,7 +560,7 @@ class ChromiumElement(DrissionElement): files = files.split('\n') self.page.run_cdp('DOM.setFileInputFiles', files=files, nodeId=self._node_id) - def clear(self, by_js: bool = False) -> None: + def clear(self, by_js=False): """清空元素文本 \n :param by_js: 是否用js方式清空 :return: None @@ -604,7 +571,7 @@ class ChromiumElement(DrissionElement): else: self.input(('\ue009', 'a', '\ue017'), clear=False) - def click(self, by_js: bool = None, retry: bool = False, timeout: float = .2) -> bool: + def click(self, by_js=None, retry=False, timeout=.2): """点击元素 \n 如果遇到遮挡,会重新尝试点击直到超时,若都失败就改用js点击 \n :param by_js: 是否用js点击,为True时直接用js点击,为False时重试失败也不会改用js @@ -613,7 +580,7 @@ class ChromiumElement(DrissionElement): :return: 是否点击成功 """ - def do_it(cx, cy, lx, ly) -> Union[None, bool]: + def do_it(cx, cy, lx, ly): """无遮挡返回True,有遮挡返回False,无元素返回None""" try: r = self.page.driver.DOM.getNodeForLocation(x=lx, y=ly) @@ -655,10 +622,7 @@ class ChromiumElement(DrissionElement): return False - def click_at(self, - offset_x: Union[int, str] = None, - offset_y: Union[int, str] = None, - button: str = 'left') -> None: + def click_at(self, offset_x=None, offset_y=None, button='left'): """带偏移量点击本元素,相对于左上角坐标。不传入x或y值时点击元素左上角可接受点击的点 \n :param offset_x: 相对元素左上角坐标的x轴偏移量 :param offset_y: 相对元素左上角坐标的y轴偏移量 @@ -668,13 +632,13 @@ class ChromiumElement(DrissionElement): x, y = _offset_scroll(self, offset_x, offset_y) self._click(x, y, button) - def r_click(self) -> None: + def r_click(self): """右键单击""" self.page.scroll_to_see(self) xy = self._client_click_point self._click(xy['x'], xy['y'], 'right') - def r_click_at(self, offset_x: Union[int, str] = None, offset_y: Union[int, str] = None) -> None: + def r_click_at(self, offset_x=None, offset_y=None): """带偏移量右键单击本元素,相对于左上角坐标。不传入x或y值时点击元素中点 \n :param offset_x: 相对元素左上角坐标的x轴偏移量 :param offset_y: 相对元素左上角坐标的y轴偏移量 @@ -682,7 +646,7 @@ class ChromiumElement(DrissionElement): """ self.click_at(offset_x, offset_y, 'right') - def _click(self, client_x: int, client_y: int, button: str = 'left') -> None: + def _click(self, client_x, client_y, button='left'): """实施点击 \n :param client_x: 视口中的x坐标 :param client_y: 视口中的y坐标 @@ -694,7 +658,7 @@ class ChromiumElement(DrissionElement): sleep(.1) self.page.driver.Input.dispatchMouseEvent(type='mouseReleased', x=client_x, y=client_y, button=button) - def hover(self, offset_x: int = None, offset_y: int = None) -> None: + def hover(self, offset_x=None, offset_y=None): """鼠标悬停,可接受偏移量,偏移量相对于元素左上角坐标。不传入x或y值时悬停在元素中点 \n :param offset_x: 相对元素左上角坐标的x轴偏移量 :param offset_y: 相对元素左上角坐标的y轴偏移量 @@ -703,7 +667,7 @@ class ChromiumElement(DrissionElement): x, y = _offset_scroll(self, offset_x, offset_y) self.page.driver.Input.dispatchMouseEvent(type='mouseMoved', x=x, y=y) - def drag(self, offset_x: int = 0, offset_y: int = 0, speed: int = 40, shake: bool = True) -> None: + def drag(self, offset_x=0, offset_y=0, speed=40, shake=True): """拖拽当前元素到相对位置 \n :param offset_x: x变化值 :param offset_y: y变化值 @@ -716,10 +680,7 @@ class ChromiumElement(DrissionElement): offset_y += curr_xy['y'] self.drag_to((offset_x, offset_y), speed, shake) - def drag_to(self, - ele_or_loc: Union[tuple, 'ChromiumElement'], - speed: int = 40, - shake: bool = True) -> None: + def drag_to(self, ele_or_loc, speed=40, shake=True): """拖拽当前元素,目标为另一个元素或坐标元组 \n :param ele_or_loc: 另一个元素或坐标元组,坐标为元素中点的坐标 :param speed: 拖动的速度,传入0即瞬间到达 @@ -761,21 +722,21 @@ class ChromiumElement(DrissionElement): current_x, current_y = x, y actions.release() - def _get_obj_id(self, node_id) -> str: + def _get_obj_id(self, node_id): """根据传入node id获取js中的object id \n :param node_id: cdp中的node id :return: js中的object id """ return self.page.run_cdp('DOM.resolveNode', nodeId=node_id)['object']['objectId'] - def _get_node_id(self, obj_id) -> str: + def _get_node_id(self, obj_id): """根据传入object id获取cdp中的node id \n :param obj_id: js中的object id :return: cdp中的node id """ return self.page.run_cdp('DOM.requestNode', objectId=obj_id)['nodeId'] - def _get_ele_path(self, mode) -> str: + def _get_ele_path(self, mode): """返获取css路径或xpath路径""" if mode == 'xpath': txt1 = 'var tag = el.nodeName.toLowerCase();' @@ -817,7 +778,7 @@ class ChromiumElement(DrissionElement): t = self.run_script(js) return f':root{t}' if mode == 'css' else t - def _get_client_rect(self, quad: str) -> Union[dict, None]: + def _get_client_rect(self, quad): """按照类型返回坐标 :param quad: 方框类型,margin border padding :return: 四个角坐标,大小为0时返回None @@ -827,7 +788,7 @@ class ChromiumElement(DrissionElement): except: return None - def _get_absolute_rect(self, x, y) -> dict: + def _get_absolute_rect(self, x, y): """根据绝对坐标获取窗口坐标""" js = 'return document.documentElement.scrollLeft+" "+document.documentElement.scrollTop;' xy = self.run_script(js) @@ -838,10 +799,7 @@ class ChromiumElement(DrissionElement): class ChromiumShadowRootElement(BaseElement): """ChromiumShadowRootElement是用于处理ShadowRoot的类,使用方法和ChromiumElement基本一致""" - def __init__(self, - parent_ele: ChromiumElement, - obj_id: str = None, - backend_id: str = None): + def __init__(self, parent_ele, obj_id=None, backend_id=None): """ :param parent_ele: shadow root 所在父元素 :param obj_id: js中的object id @@ -858,12 +816,10 @@ class ChromiumShadowRootElement(BaseElement): self._node_id = self._get_node_id(obj_id) self._backend_id = self._get_backend_id(self._node_id) - def __repr__(self) -> str: + def __repr__(self): return f'' - def __call__(self, - loc_or_str: Union[Tuple[str, str], str], - timeout: float = None) -> Union[ChromiumElement, None]: + def __call__(self, loc_or_str, timeout=None): """在内部查找元素 \n 例:ele2 = ele1('@id=ele_id') \n :param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串 @@ -873,12 +829,12 @@ class ChromiumShadowRootElement(BaseElement): return self.ele(loc_or_str, timeout) @property - def is_enabled(self) -> bool: + def is_enabled(self): """返回元素是否可用""" return not self.run_script('return this.disabled;') @property - def is_alive(self) -> bool: + def is_alive(self): """返回元素是否仍在DOM中""" try: self.page.run_cdp('DOM.describeNode', nodeId=self._node_id) @@ -887,40 +843,40 @@ class ChromiumShadowRootElement(BaseElement): return False @property - def node_id(self) -> str: + def node_id(self): """返回元素cdp中的node id""" return self._node_id @property - def obj_id(self) -> str: + def obj_id(self): """返回元素js中的obect id""" return self._obj_id @property - def tag(self) -> str: + def tag(self): """返回元素标签名""" return 'shadow-root' @property - def html(self) -> str: + def html(self): """返回outerHTML文本""" return f'{self.inner_html}' @property - def inner_html(self) -> str: + def inner_html(self): """返回内部的html文本""" return self.run_script('return this.innerHTML;') - def run_script(self, script: str, as_expr: bool = False, *args: Any) -> Any: + def run_script(self, script, as_expr=False, *args): """运行javascript代码 \n :param script: js文本 :param as_expr: 是否作为表达式运行,为True时args无效 :param args: 参数,按顺序在js文本中对应argument[0]、argument[2]... :return: 运行的结果 """ - return _run_script(self, script, as_expr, self.page.timeouts.script, args) + return run_script(self, script, as_expr, self.page.timeouts.script, args) - def run_async_script(self, script: str, as_expr: bool = False, *args: Any) -> None: + def run_async_script(self, script, as_expr=False, *args): """以异步方式执行js代码 \n :param script: js文本 :param as_expr: 是否作为表达式运行,为True时args无效 @@ -928,9 +884,9 @@ class ChromiumShadowRootElement(BaseElement): :return: None """ from threading import Thread - Thread(target=_run_script, args=(self, script, as_expr, self.page.timeouts.script, args)).start() + Thread(target=run_script, args=(self, script, as_expr, self.page.timeouts.script, args)).start() - def parent(self, level_or_loc: Union[str, int] = 1) -> ChromiumElement: + def parent(self, level_or_loc=1): """返回上面某一级父元素,可指定层数或用查询语法定位 \n :param level_or_loc: 第几级父元素,或定位符 :return: ChromiumElement对象 @@ -951,9 +907,7 @@ class ChromiumShadowRootElement(BaseElement): return self.parent_ele._ele(loc, timeout=0, relative=True) - def next(self, - index: int = 1, - filter_loc: Union[tuple, str] = '') -> Union[ChromiumElement, str, None]: + def next(self, index=1, filter_loc=''): """返回后面的一个兄弟元素,可用查询语法筛选,可指定返回筛选结果的第几个 \n :param index: 第几个查询结果元素 :param filter_loc: 用于筛选元素的查询语法 @@ -962,9 +916,7 @@ class ChromiumShadowRootElement(BaseElement): nodes = self.nexts(filter_loc=filter_loc) return nodes[index - 1] if nodes else None - def before(self, - index: int = 1, - filter_loc: Union[tuple, str] = '') -> Union[ChromiumElement, str, None]: + def before(self, index=1, filter_loc=''): """返回前面的一个兄弟元素,可用查询语法筛选,可指定返回筛选结果的第几个 \n :param index: 前面第几个查询结果元素 :param filter_loc: 用于筛选元素的查询语法 @@ -973,8 +925,7 @@ class ChromiumShadowRootElement(BaseElement): nodes = self.befores(filter_loc=filter_loc) return nodes[index - 1] if nodes else None - def after(self, index: int = 1, - filter_loc: Union[tuple, str] = '') -> Union[ChromiumElement, str, None]: + def after(self, index=1, filter_loc=''): """返回后面的一个兄弟元素,可用查询语法筛选,可指定返回筛选结果的第几个 \n :param index: 后面第几个查询结果元素 :param filter_loc: 用于筛选元素的查询语法 @@ -983,7 +934,7 @@ class ChromiumShadowRootElement(BaseElement): nodes = self.afters(filter_loc=filter_loc) return nodes[index - 1] if nodes else None - def nexts(self, filter_loc: Union[tuple, str] = '') -> List[Union[ChromiumElement, str]]: + def nexts(self, filter_loc=''): """返回后面所有兄弟元素或节点组成的列表 \n :param filter_loc: 用于筛选元素的查询语法 :return: ChromiumElement对象组成的列表 @@ -996,7 +947,7 @@ class ChromiumShadowRootElement(BaseElement): xpath = f'xpath:./{loc}' return self.parent_ele._ele(xpath, timeout=0.1, single=False, relative=True) - def befores(self, filter_loc: Union[tuple, str] = '') -> List[Union[ChromiumElement, str]]: + def befores(self, filter_loc=''): """返回后面全部兄弟元素或节点组成的列表,可用查询语法筛选 \n :param filter_loc: 用于筛选元素的查询语法 :return: 本元素前面的元素或节点组成的列表 @@ -1009,7 +960,7 @@ class ChromiumShadowRootElement(BaseElement): xpath = f'xpath:./preceding::{loc}' return self.parent_ele._ele(xpath, timeout=0.1, single=False, relative=True) - def afters(self, filter_loc: Union[tuple, str] = '') -> List[Union[ChromiumElement, str]]: + def afters(self, filter_loc=''): """返回前面全部兄弟元素或节点组成的列表,可用查询语法筛选 \n :param filter_loc: 用于筛选元素的查询语法 :return: 本元素后面的元素或节点组成的列表 @@ -1019,9 +970,7 @@ class ChromiumShadowRootElement(BaseElement): xpath = f'xpath:./following::{loc}' return eles1 + self.parent_ele._ele(xpath, timeout=0.1, single=False, relative=True) - def ele(self, - loc_or_str: Union[Tuple[str, str], str], - timeout: float = None) -> Union[ChromiumElement, None]: + def ele(self, loc_or_str, timeout=None): """返回当前元素下级符合条件的第一个元素 \n :param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串 :param timeout: 查找元素超时时间,默认与元素所在页面等待时间一致 @@ -1029,9 +978,7 @@ class ChromiumShadowRootElement(BaseElement): """ return self._ele(loc_or_str, timeout) - def eles(self, - loc_or_str: Union[Tuple[str, str], str], - timeout: float = None) -> List[ChromiumElement]: + def eles(self, loc_or_str, timeout=None): """返回当前元素下级所有符合条件的子元素 \n :param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串 :param timeout: 查找元素超时时间,默认与元素所在页面等待时间一致 @@ -1039,24 +986,21 @@ class ChromiumShadowRootElement(BaseElement): """ return self._ele(loc_or_str, timeout=timeout, single=False) - def s_ele(self, loc_or_ele=None) -> Union[SessionElement, str, None]: + def s_ele(self, loc_or_str=None): """查找第一个符合条件的元素以SessionElement形式返回,处理复杂页面时效率很高 \n - :param loc_or_ele: 元素的定位信息,可以是loc元组,或查询字符串 + :param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串 :return: SessionElement对象或属性、文本 """ - return make_session_ele(self, loc_or_ele) + return make_session_ele(self, loc_or_str) - def s_eles(self, loc_or_ele) -> List[SessionElement]: + def s_eles(self, loc_or_str): """查找所有符合条件的元素以SessionElement列表形式返回,处理复杂页面时效率很高 \n - :param loc_or_ele: 元素的定位信息,可以是loc元组,或查询字符串 + :param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串 :return: SessionElement对象 """ - return make_session_ele(self, loc_or_ele, single=False) + return make_session_ele(self, loc_or_str, single=False) - def _ele(self, - loc_or_str: Union[Tuple[str, str], str], - timeout: float = None, - single: bool = True, relative=False) -> Union['ChromiumElement', None, List[ChromiumElement]]: + def _ele(self, loc_or_str, timeout=None, single=True, relative=False): """返回当前元素下级符合条件的子元素、属性或节点文本,默认返回第一个 \n :param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串 :param timeout: 查找元素超时时间 @@ -1089,15 +1033,15 @@ class ChromiumShadowRootElement(BaseElement): results.append(ChromiumElement(self.page, node_id)) return results - def _get_node_id(self, obj_id) -> str: + def _get_node_id(self, obj_id): """返回元素node id""" return self.page.run_cdp('DOM.requestNode', objectId=obj_id)['nodeId'] - def _get_obj_id(self, back_id) -> str: + def _get_obj_id(self, back_id): """返回元素object id""" return self.page.run_cdp('DOM.resolveNode', backendNodeId=back_id)['object']['objectId'] - def _get_backend_id(self, node_id) -> str: + def _get_backend_id(self, node_id): """返回元素object id""" return self.page.run_cdp('DOM.describeNode', nodeId=node_id)['node']['backendNodeId'] @@ -1347,11 +1291,7 @@ class ChromiumShadowRootElement(BaseElement): # return self._inner_ele.befores(filter_loc, timeout) -def make_chromium_ele(ele: ChromiumElement, - loc: Union[str, Tuple[str, str]], - single: bool = True, - timeout: float = None, - relative=True) -> Union[ChromiumElement, str, None, List[Union[ChromiumElement, str]]]: +def make_chromium_ele(ele, loc, single=True, timeout=None, relative=True): """在chromium元素中查找 \n :param ele: ChromiumElement对象 :param loc: 元素定位元组 @@ -1383,11 +1323,7 @@ def make_chromium_ele(ele: ChromiumElement, return _find_by_css(ele, loc[1], single, timeout) -def _find_by_xpath(ele: ChromiumElement, - xpath: str, - single: bool, - timeout: float, - relative=True) -> Union[ChromiumElement, List[ChromiumElement], None]: +def _find_by_xpath(ele, xpath, single, timeout, relative=True): """执行用xpath在元素中查找元素 :param ele: 在此元素中查找 :param xpath: 查找语句 @@ -1438,10 +1374,7 @@ def _find_by_xpath(ele: ChromiumElement, for i in r[:-1]] -def _find_by_css(ele: ChromiumElement, - selector: str, - single: bool, - timeout: float) -> Union[ChromiumElement, List[ChromiumElement], None]: +def _find_by_css(ele, selector, single, timeout): """执行用css selector在元素中查找元素 :param ele: 在此元素中查找 :param selector: 查找语句 @@ -1480,7 +1413,7 @@ def _find_by_css(ele: ChromiumElement, return [_make_chromium_ele(ele.page, obj_id=i['value']['objectId']) for i in r] -def _make_chromium_ele(page, node_id: str = None, obj_id: str = None): +def _make_chromium_ele(page, node_id=None, obj_id=None): """根据node id或object id生成相应元素对象""" ele = ChromiumElement(page, obj_id=obj_id, node_id=node_id) if ele.tag in ('iframe', 'frame') and ele.attr('src'): @@ -1495,7 +1428,7 @@ def _make_chromium_ele(page, node_id: str = None, obj_id: str = None): return ele -def _make_js_for_find_ele_by_xpath(xpath: str, type_txt: str, node_txt: str) -> str: +def _make_js_for_find_ele_by_xpath(xpath, type_txt, node_txt): """生成用xpath在元素中查找元素的js文本 :param xpath: xpath文本 :param type_txt: 查找类型 @@ -1536,11 +1469,12 @@ else{a.push(e.snapshotItem(i));}}""" return js -def _run_script(page_or_ele, script: str, as_expr: bool = False, timeout: float = None, args: tuple = None) -> Any: +def run_script(page_or_ele, script, as_expr=False, timeout=None, args=None): """运行javascript代码 \n :param page_or_ele: 页面对象或元素对象 :param script: js文本 :param as_expr: 是否作为表达式运行,为True时args无效 + :param timeout: 超时时间 :param args: 参数,按顺序在js文本中对应argument[0]、argument[2]... :return: js执行结果 """ @@ -1580,7 +1514,7 @@ def _run_script(page_or_ele, script: str, as_expr: bool = False, timeout: float return res -def _parse_js_result(page, ele, result: dict): +def _parse_js_result(page, ele, result): """解析js返回的结果""" if 'unserializableValue' in result: return result['unserializableValue'] @@ -1612,7 +1546,7 @@ def _parse_js_result(page, ele, result: dict): return result['value'] -def _convert_argument(arg: Any) -> dict: +def _convert_argument(arg): """把参数转换成js能够接收的形式""" if isinstance(arg, ChromiumElement): return {'objectId': arg.obj_id} @@ -1627,7 +1561,7 @@ def _convert_argument(arg: Any) -> dict: return {'unserializableValue': '-Infinity'} -def _send_enter(ele: ChromiumElement) -> None: +def _send_enter(ele): """发送回车""" data = {'type': 'keyDown', 'modifiers': 0, 'windowsVirtualKeyCode': 13, 'code': 'Enter', 'key': 'Enter', 'text': '\r', 'autoRepeat': False, 'unmodifiedText': '\r', 'location': 0, 'isKeypad': False} @@ -1637,7 +1571,7 @@ def _send_enter(ele: ChromiumElement) -> None: ele.page.driver.Input.dispatchKeyEvent(**data) -def _send_key(ele: ChromiumElement, modifier: int, key: str) -> None: +def _send_key(ele, modifier, key): """发送一个字,在键盘中的字符触发按键,其它直接发送文本""" if key not in _keyDefinitions: ele.page.driver.Input.insertText(text=key) @@ -1661,7 +1595,7 @@ def _send_key(ele: ChromiumElement, modifier: int, key: str) -> None: ele.page.driver.Input.dispatchKeyEvent(**data) -def _offset_scroll(ele: ChromiumElement, offset_x: int, offset_y: int) -> tuple: +def _offset_scroll(ele, offset_x, offset_y): """接收元素及偏移坐标,滚动到偏移坐标,返回该点在视口中的坐标 :param ele: 元素对象 :param offset_x: 偏移量x @@ -1699,34 +1633,34 @@ class ChromeScroll(object): self.obj_id = None self.page = page_or_ele - def _run_script(self, js: str): + def _run_script(self, js): js = js.format(self.t1, self.t2, self.t2) if self.obj_id: self.page.run_script(js) else: self.page.driver.Runtime.evaluate(expression=js) - def to_top(self) -> None: + def to_top(self): """滚动到顶端,水平位置不变""" self._run_script('{}.scrollTo({}.scrollLeft,0);') - def to_bottom(self) -> None: + def to_bottom(self): """滚动到底端,水平位置不变""" self._run_script('{}.scrollTo({}.scrollLeft,{}.scrollHeight);') - def to_half(self) -> None: + def to_half(self): """滚动到垂直中间位置,水平位置不变""" self._run_script('{}.scrollTo({}.scrollLeft,{}.scrollHeight/2);') - def to_rightmost(self) -> None: + def to_rightmost(self): """滚动到最右边,垂直位置不变""" self._run_script('{}.scrollTo({}.scrollWidth,{}.scrollTop);') - def to_leftmost(self) -> None: + def to_leftmost(self): """滚动到最左边,垂直位置不变""" self._run_script('{}.scrollTo(0,{}.scrollTop);') - def to_location(self, x: int, y: int) -> None: + def to_location(self, x, y): """滚动到指定位置 \n :param x: 水平距离 :param y: 垂直距离 @@ -1734,7 +1668,7 @@ class ChromeScroll(object): """ self._run_script(f'{{}}.scrollTo({x},{y});') - def up(self, pixel: int = 300) -> None: + def up(self, pixel=300): """向上滚动若干像素,水平位置不变 \n :param pixel: 滚动的像素 :return: None @@ -1742,14 +1676,14 @@ class ChromeScroll(object): pixel = -pixel self._run_script(f'{{}}.scrollBy(0,{pixel});') - def down(self, pixel: int = 300) -> None: + def down(self, pixel=300): """向下滚动若干像素,水平位置不变 \n :param pixel: 滚动的像素 :return: None """ self._run_script(f'{{}}.scrollBy(0,{pixel});') - def left(self, pixel: int = 300) -> None: + def left(self, pixel=300): """向左滚动若干像素,垂直位置不变 \n :param pixel: 滚动的像素 :return: None @@ -1757,7 +1691,7 @@ class ChromeScroll(object): pixel = -pixel self._run_script(f'{{}}.scrollBy({pixel},0);') - def right(self, pixel: int = 300) -> None: + def right(self, pixel=300): """向右滚动若干像素,垂直位置不变 \n :param pixel: 滚动的像素 :return: None @@ -1768,7 +1702,7 @@ class ChromeScroll(object): class ChromeSelect(object): """ChromeSelect 类专门用于处理 d 模式下 select 标签""" - def __init__(self, ele: ChromiumElement): + def __init__(self, ele): """初始化 \n :param ele: select 元素对象 """ @@ -1777,7 +1711,7 @@ class ChromeSelect(object): self._ele = ele - def __call__(self, text_or_index: Union[str, int, list, tuple], timeout=None) -> bool: + def __call__(self, text_or_index, timeout=None): """选定下拉列表中子元素 \n :param text_or_index: 根据文本、值选或序号择选项,若允许多选,传入list或tuple可多选 :param timeout: 超时时间,不输入默认实用页面超时时间 @@ -1788,18 +1722,18 @@ class ChromeSelect(object): return self._select(text_or_index, para_type, timeout=timeout) @property - def is_multi(self) -> bool: + def is_multi(self): """返回是否多选表单""" multi = self._ele.attr('multiple') return multi and multi.lower() != "false" @property - def options(self) -> List[ChromiumElement]: + def options(self): """返回所有选项元素组成的列表""" return self._ele.eles('tag:option') @property - def selected_option(self) -> Union[ChromiumElement, None]: + def selected_option(self): """返回第一个被选中的option元素 \n :return: ChromiumElement对象或None """ @@ -1807,13 +1741,13 @@ class ChromeSelect(object): return ele @property - def selected_options(self) -> List[ChromiumElement]: + def selected_options(self): """返回所有被选中的option元素列表 \n :return: ChromiumElement对象组成的列表 """ return [x for x in self.options if x.is_selected] - def clear(self) -> None: + def clear(self): """清除所有已选项""" if not self.is_multi: raise NotImplementedError("只能在多选菜单执行此操作。") @@ -1821,7 +1755,7 @@ class ChromeSelect(object): if opt.is_selected: opt.click(by_js=True) - def by_text(self, text: Union[str, list, tuple], timeout=None) -> bool: + def by_text(self, text, timeout=None): """此方法用于根据text值选择项。当元素是多选列表时,可以接收list或tuple \n :param text: text属性值,传入list或tuple可选择多项 :param timeout: 超时时间,不输入默认实用页面超时时间 @@ -1830,7 +1764,7 @@ class ChromeSelect(object): timeout = timeout if timeout is not None else self._ele.page.timeout return self._select(text, 'text', False, timeout) - def by_value(self, value: Union[str, list, tuple], timeout=None) -> bool: + def by_value(self, value, timeout=None): """此方法用于根据value值选择项。当元素是多选列表时,可以接收list或tuple \n :param value: value属性值,传入list或tuple可选择多项 :param timeout: 超时时间,不输入默认实用页面超时时间 @@ -1839,7 +1773,7 @@ class ChromeSelect(object): timeout = timeout if timeout is not None else self._ele.page.timeout return self._select(value, 'value', False, timeout) - def by_index(self, index: Union[int, list, tuple], timeout=None) -> bool: + def by_index(self, index, timeout=None): """此方法用于根据index值选择项。当元素是多选列表时,可以接收list或tuple \n :param index: index属性值,传入list或tuple可选择多项 :param timeout: 超时时间,不输入默认实用页面超时时间 @@ -1848,7 +1782,7 @@ class ChromeSelect(object): timeout = timeout if timeout is not None else self._ele.page.timeout return self._select(index, 'index', False, timeout) - def cancel_by_text(self, text: Union[str, list, tuple], timeout=None) -> bool: + def cancel_by_text(self, text, timeout=None): """此方法用于根据text值取消选择项。当元素是多选列表时,可以接收list或tuple \n :param text: text属性值,传入list或tuple可取消多项 :param timeout: 超时时间,不输入默认实用页面超时时间 @@ -1857,7 +1791,7 @@ class ChromeSelect(object): timeout = timeout if timeout is not None else self._ele.page.timeout return self._select(text, 'text', True, timeout) - def cancel_by_value(self, value: Union[str, list, tuple], timeout=None) -> bool: + def cancel_by_value(self, value, timeout=None): """此方法用于根据value值取消选择项。当元素是多选列表时,可以接收list或tuple \n :param value: value属性值,传入list或tuple可取消多项 :param timeout: 超时时间,不输入默认实用页面超时时间 @@ -1866,7 +1800,7 @@ class ChromeSelect(object): timeout = timeout if timeout is not None else self._ele.page.timeout return self._select(value, 'value', True, timeout) - def cancel_by_index(self, index: Union[int, list, tuple], timeout=None) -> bool: + def cancel_by_index(self, index, timeout=None): """此方法用于根据index值取消选择项。当元素是多选列表时,可以接收list或tuple \n :param index: value属性值,传入list或tuple可取消多项 :param timeout: 超时时间,不输入默认实用页面超时时间 @@ -1875,7 +1809,7 @@ class ChromeSelect(object): timeout = timeout if timeout is not None else self._ele.page.timeout return self._select(index, 'index', True, timeout) - def invert(self) -> None: + def invert(self): """反选""" if not self.is_multi: raise NotImplementedError("只能对多项选框执行反选。") @@ -1883,11 +1817,7 @@ class ChromeSelect(object): for i in self.options: i.click(by_js=True) - def _select(self, - text_value_index: Union[str, int, list, tuple] = None, - para_type: str = 'text', - deselect: bool = False, - timeout=None) -> bool: + def _select(self, text_value_index=None, para_type='text', deselect=False, timeout=None): """选定或取消选定下拉列表中子元素 \n :param text_value_index: 根据文本、值选或序号择选项,若允许多选,传入list或tuple可多选 :param para_type: 参数类型,可选 'text'、'value'、'index' @@ -1930,9 +1860,9 @@ class ChromeSelect(object): raise TypeError('只能传入str、int、list和tuple类型。') def _select_multi(self, - text_value_index: Union[list, tuple] = None, - para_type: str = 'text', - deselect: bool = False) -> bool: + text_value_index=None, + para_type='text', + deselect=False): """选定或取消选定下拉列表中多个子元素 \n :param text_value_index: 根据文本、值选或序号择选多项 :param para_type: 参数类型,可选 'text'、'value'、'index' @@ -1960,10 +1890,7 @@ class ChromeSelect(object): class ChromiumElementWaiter(object): """等待元素在dom中某种状态,如删除、显示、隐藏""" - def __init__(self, - page_or_ele, - loc_or_ele: Union[str, tuple, ChromiumElement], - timeout: float = None): + def __init__(self, page_or_ele, loc_or_ele, timeout=None): """等待元素在dom中某种状态,如删除、显示、隐藏 \n :param page_or_ele: 页面或父元素 :param loc_or_ele: 要等待的元素,可以是已有元素、定位符 @@ -1979,7 +1906,7 @@ class ChromiumElementWaiter(object): else: self.timeout = page_or_ele.page.timeout if isinstance(page_or_ele, ChromiumElement) else page_or_ele.timeout - def delete(self) -> bool: + def delete(self): """等待元素从dom删除""" if isinstance(self.loc_or_ele, ChromiumElement): end_time = perf_counter() + self.timeout @@ -1998,15 +1925,15 @@ class ChromiumElementWaiter(object): return False - def display(self) -> bool: + def display(self): """等待元素从dom显示""" return self._wait_ele('display') - def hidden(self) -> bool: + def hidden(self): """等待元素从dom隐藏""" return self._wait_ele('hidden') - def _wait_ele(self, mode: str) -> Union[None, bool]: + def _wait_ele(self, mode): """执行等待 :param mode: 等待模式 :return: 是否等待成功 diff --git a/DrissionPage/chromium_element.pyi b/DrissionPage/chromium_element.pyi index e61a36c..c6f3762 100644 --- a/DrissionPage/chromium_element.pyi +++ b/DrissionPage/chromium_element.pyi @@ -19,8 +19,8 @@ class ChromiumElement(DrissionElement): def __init__(self, page: ChromiumBase, - node_id: str = None, - obj_id: str = None): + node_id: str = ..., + obj_id: str = ...): self._tag: str = ... self.page: Union[ChromiumPage, WebPage] = ... self._node_id: str = ... @@ -181,9 +181,8 @@ class ChromiumElement(DrissionElement): def _ele(self, loc_or_str: Union[Tuple[str, str], str], - timeout: float = ..., single: bool = ..., relative=...) \ - -> Union['ChromiumElement', str, None, - List[Union['ChromiumElement', str]]]: ... + timeout: float = ..., single: bool = ..., relative: bool = ...) \ + -> Union['ChromiumElement', str, None, List[Union['ChromiumElement', str]]]: ... def style(self, style: str, pseudo_ele: str = ...) -> str: ... @@ -236,11 +235,10 @@ class ChromiumShadowRootElement(BaseElement): parent_ele: ChromiumElement, obj_id: str = ..., backend_id: str = ...): - self._obj_id:str=... - self._node_id:str=... - self.page:ChromiumPage=... - self.parent_ele:ChromiumElement=... - + self._obj_id: str = ... + self._node_id: str = ... + self.page: ChromiumPage = ... + self.parent_ele: ChromiumElement = ... def __repr__(self) -> str: ... @@ -300,34 +298,34 @@ class ChromiumShadowRootElement(BaseElement): loc_or_str: Union[Tuple[str, str], str], timeout: float = ...) -> List[ChromiumElement]: ... - def s_ele(self, loc_or_ele=...) -> Union[SessionElement, str, None]: ... + def s_ele(self, loc_or_str: Union[Tuple[str, str], str]=...) -> Union[SessionElement, str, None]: ... - def s_eles(self, loc_or_ele) -> List[SessionElement]: ... + def s_eles(self, loc_or_str: Union[Tuple[str, str], str]) -> List[SessionElement]: ... def _ele(self, loc_or_str: Union[Tuple[str, str], str], timeout: float = ..., - single: bool = ..., relative=...) -> Union['ChromiumElement', None, List[ChromiumElement]]: ... + single: bool = ..., relative: bool =...) -> Union['ChromiumElement', None, List[ChromiumElement]]: ... - def _get_node_id(self, obj_id) -> str: ... + def _get_node_id(self, obj_id:str) -> str: ... - def _get_obj_id(self, back_id) -> str: ... + def _get_obj_id(self, back_id:str) -> str: ... - def _get_backend_id(self, node_id) -> str: ... + def _get_backend_id(self, node_id:str) -> str: ... def make_chromium_ele(ele: ChromiumElement, loc: Union[str, Tuple[str, str]], single: bool = ..., timeout: float = ..., - relative=...) -> Union[ChromiumElement, str, None, List[Union[ChromiumElement, str]]]: ... + relative: bool =...) -> Union[ChromiumElement, str, None, List[Union[ChromiumElement, str]]]: ... def _find_by_xpath(ele: ChromiumElement, xpath: str, single: bool, timeout: float, - relative=...) -> Union[ChromiumElement, List[ChromiumElement], None]: ... + relative: bool = ...) -> Union[ChromiumElement, List[ChromiumElement], None]: ... def _find_by_css(ele: ChromiumElement, @@ -336,16 +334,17 @@ def _find_by_css(ele: ChromiumElement, timeout: float) -> Union[ChromiumElement, List[ChromiumElement], None]: ... -def _make_chromium_ele(page, node_id: str = ..., obj_id: str = ...): ... +def _make_chromium_ele(page: ChromiumBase, node_id: str = ..., obj_id: str = ...) -> ChromiumElement: ... def _make_js_for_find_ele_by_xpath(xpath: str, type_txt: str, node_txt: str) -> str: ... -def _run_script(page_or_ele, script: str, as_expr: bool = ..., timeout: float = ..., args: tuple = ...) -> Any: ... +def run_script(page_or_ele: Union[ChromiumBase, ChromiumElement, ChromiumShadowRootElement], script: str, as_expr: bool = ..., + timeout: float = ..., args: tuple = ...) -> Any: ... -def _parse_js_result(page, ele, result: dict): ... +def _parse_js_result(page: ChromiumBase, ele: ChromiumElement, result: dict): ... def _convert_argument(arg: Any) -> dict: ... @@ -363,11 +362,11 @@ def _offset_scroll(ele: ChromiumElement, offset_x: int, offset_y: int) -> tuple: class ChromeScroll(object): """用于滚动的对象""" - def __init__(self, page_or_ele): - self.t1 :str=... - self.t2 :str=... - self.obj_id:str=... - self.page:ChromiumPage=... + def __init__(self, page_or_ele: Union[ChromiumBase, ChromiumElement]): + self.t1: str = ... + self.t2: str = ... + self.obj_id: str = ... + self.page: ChromiumPage = ... def _run_script(self, js: str): ... @@ -396,10 +395,9 @@ class ChromeSelect(object): """ChromeSelect 类专门用于处理 d 模式下 select 标签""" def __init__(self, ele: ChromiumElement): - self._ele:ChromiumElement=... + self._ele: ChromiumElement = ... - - def __call__(self, text_or_index: Union[str, int, list, tuple], timeout=None) -> bool: ... + def __call__(self, text_or_index: Union[str, int, list, tuple], timeout: float = None) -> bool: ... @property def is_multi(self) -> bool: ... @@ -415,17 +413,17 @@ class ChromeSelect(object): def clear(self) -> None: ... - def by_text(self, text: Union[str, list, tuple], timeout=...) -> bool: ... + def by_text(self, text: Union[str, list, tuple], timeout: float = ...) -> bool: ... - def by_value(self, value: Union[str, list, tuple], timeout=...) -> bool: ... + def by_value(self, value: Union[str, list, tuple], timeout: float = ...) -> bool: ... - def by_index(self, index: Union[int, list, tuple], timeout=...) -> bool: ... + def by_index(self, index: Union[int, list, tuple], timeout: float = ...) -> bool: ... - def cancel_by_text(self, text: Union[str, list, tuple], timeout=...) -> bool: ... + def cancel_by_text(self, text: Union[str, list, tuple], timeout: float = ...) -> bool: ... - def cancel_by_value(self, value: Union[str, list, tuple], timeout=...) -> bool: ... + def cancel_by_value(self, value: Union[str, list, tuple], timeout: float = ...) -> bool: ... - def cancel_by_index(self, index: Union[int, list, tuple], timeout=...) -> bool: ... + def cancel_by_index(self, index: Union[int, list, tuple], timeout: float = ...) -> bool: ... def invert(self) -> None: ... @@ -433,7 +431,7 @@ class ChromeSelect(object): text_value_index: Union[str, int, list, tuple] = ..., para_type: str = ..., deselect: bool = ..., - timeout=...) -> bool: ... + timeout: float = ...) -> bool: ... def _select_multi(self, text_value_index: Union[list, tuple] = ..., @@ -448,9 +446,9 @@ class ChromiumElementWaiter(object): page_or_ele, loc_or_ele: Union[str, tuple, ChromiumElement], timeout: float = ...): - self.loc_or_ele: Union[str, tuple, ChromiumElement]=... - self.timeout:float=... - self.driver:Union[ChromiumPage, ChromiumPage]=... + self.loc_or_ele: Union[str, tuple, ChromiumElement] = ... + self.timeout: float = ... + self.driver: Union[ChromiumPage, ChromiumPage] = ... def delete(self) -> bool: ... diff --git a/DrissionPage/chromium_page.py b/DrissionPage/chromium_page.py index 0a40b80..875614e 100644 --- a/DrissionPage/chromium_page.py +++ b/DrissionPage/chromium_page.py @@ -3,24 +3,20 @@ from pathlib import Path from platform import system from re import search from time import perf_counter, sleep -from typing import Union, Tuple, List from requests import Session from .chromium_base import Timeout, ChromiumBase from .chromium_tab import ChromiumTab -from .config import DriverOptions from .common import connect_chrome +from .config import DriverOptions from .tab import Tab class ChromiumPage(ChromiumBase): """用于管理浏览器的类""" - def __init__(self, - addr_tab_opts: Union[str, Tab, DriverOptions] = None, - tab_id: str = None, - timeout: float = None): + def __init__(self, addr_tab_opts=None, tab_id=None, timeout=None): """初始化 \n :param addr_tab_opts: 浏览器地址:端口、Tab对象或DriverOptions对象 :param tab_id: 要控制的标签页id,不指定默认为激活的 @@ -28,9 +24,7 @@ class ChromiumPage(ChromiumBase): """ super().__init__(addr_tab_opts, tab_id, timeout) - def _connect_browser(self, - addr_tab_opts: Union[str, Tab, DriverOptions] = None, - tab_id: str = None) -> None: + def _connect_browser(self, addr_tab_opts=None, tab_id=None): """连接浏览器,在第一次时运行 \n :param addr_tab_opts: 浏览器地址、Tab对象或DriverOptions对象 :param tab_id: 要控制的标签页id,不指定默认为激活的 @@ -79,7 +73,7 @@ class ChromiumPage(ChromiumBase): self._first_run = False self.main_tab: str = self.tab_id - def _init_page(self, tab_id: str = None) -> None: + def _init_page(self, tab_id=None): """新建页面、页面刷新、切换标签页后要进行的cdp参数初始化 :param tab_id: 要跳转到的标签页id :return: None @@ -89,26 +83,26 @@ class ChromiumPage(ChromiumBase): self._tab_obj.Page.javascriptDialogOpening = self._on_alert_open self._tab_obj.Page.javascriptDialogClosed = self._on_alert_close - def _set_options(self) -> None: + def _set_options(self): self.set_timeouts(page_load=self.options.timeouts['pageLoad'] / 1000, script=self.options.timeouts['script'] / 1000, implicit=self.options.timeouts['implicit'] / 1000 if self.timeout is None else self.timeout) self._page_load_strategy = self.options.page_load_strategy @property - def tabs_count(self) -> int: + def tabs_count(self): """返回标签页数量""" return len(self.tabs) @property - def tabs(self) -> list: + def tabs(self): """返回所有标签页id""" self._driver json = self._control_session.get(f'http://{self.address}/json').json() return [i['id'] for i in json if i['type'] == 'page'] @property - def process_id(self) -> Union[None, int]: + def process_id(self): """返回浏览器进程id""" try: return self._driver.SystemInfo.getProcessInfo()['id'] @@ -116,13 +110,13 @@ class ChromiumPage(ChromiumBase): return None @property - def set_window(self) -> 'WindowSizeSetter': + def set_window(self): """返回用于设置窗口大小的对象""" if not hasattr(self, '_window_setter'): self._window_setter = WindowSizeSetter(self) return self._window_setter - def get_tab(self, tab_id: str = None) -> ChromiumTab: + def get_tab(self, tab_id=None): """获取一个标签页对象 \n :param tab_id: 要获取的标签页id,为None时获取当前tab :return: 标签页对象 @@ -130,11 +124,7 @@ class ChromiumPage(ChromiumBase): tab_id = tab_id or self.tab_id return ChromiumTab(self, tab_id) - def get_screenshot(self, path: [str, Path] = None, - as_bytes: [bool, str] = None, - full_page: bool = False, - left_top: Tuple[int, int] = None, - right_bottom: Tuple[int, int] = None) -> Union[str, bytes]: + def get_screenshot(self, path=None, as_bytes=None, full_page=False, left_top=None, right_bottom=None): """对页面进行截图,可对整个网页、可见网页、指定范围截图。对可视范围外截图需要90以上版本浏览器支持 \n :param path: 完整路径,后缀可选'jpg','jpeg','png','webp' :param as_bytes: 是否已字节形式返回图片,可选'jpg','jpeg','png','webp',生效时path参数无效 @@ -186,11 +176,11 @@ class ChromiumPage(ChromiumBase): f.write(png) return str(path.absolute()) - def to_front(self) -> None: + def to_front(self): """激活当前标签页使其处于最前面""" self._control_session.get(f'http://{self.address}/json/activate/{self.tab_id}') - def new_tab(self, url: str = None, switch_to: bool = True) -> None: + def new_tab(self, url=None, switch_to=True): """新建并定位到一个标签页,该标签页在最后面 \n :param url: 新标签页跳转到的网址 :param switch_to: 新建标签页后是否把焦点移过去 @@ -214,11 +204,11 @@ class ChromiumPage(ChromiumBase): else: self._control_session.get(f'http://{self.address}/json/new') - def to_main_tab(self) -> None: + def to_main_tab(self): """跳转到主标签页""" self.to_tab(self.main_tab) - def to_tab(self, tab_id: str = None, activate: bool = True) -> None: + def to_tab(self, tab_id=None, activate=True): """跳转到标签页 \n :param tab_id: 标签页id字符串,默认跳转到main_tab :param activate: 切换后是否变为活动状态 @@ -226,7 +216,7 @@ class ChromiumPage(ChromiumBase): """ self._to_tab(tab_id, activate) - def _to_tab(self, tab_id: str = None, activate: bool = True, read_doc: bool = True) -> None: + def _to_tab(self, tab_id=None, activate=True, read_doc=True): """跳转到标签页 \n :param tab_id: 标签页id字符串,默认跳转到main_tab :param activate: 切换后是否变为活动状态 @@ -250,7 +240,7 @@ class ChromiumPage(ChromiumBase): if read_doc and self.ready_state == 'complete': self._get_document() - def close_tabs(self, tab_ids: Union[str, List[str], Tuple[str]] = None, others: bool = False) -> None: + def close_tabs(self, tab_ids=None, others=False): """关闭传入的标签页,默认关闭当前页。可传入多个 \n :param tab_ids: 要关闭的标签页id,可传入id组成的列表或元组,为None时关闭当前页 :param others: 是否关闭指定标签页之外的 @@ -285,14 +275,14 @@ class ChromiumPage(ChromiumBase): self.to_tab() - def close_other_tabs(self, tab_ids: Union[str, List[str], Tuple[str]] = None) -> None: + def close_other_tabs(self, tab_ids=None): """关闭传入的标签页以外标签页,默认保留当前页。可传入多个 \n :param tab_ids: 要保留的标签页id,可传入id组成的列表或元组,为None时保存当前页 :return: None """ self.close_tabs(tab_ids, True) - def handle_alert(self, accept: bool = True, send: str = None, timeout: float = None) -> Union[str, None]: + def handle_alert(self, accept=True, send=None, timeout=None): """处理提示框,可以自动等待提示框出现 \n :param accept: True表示确认,False表示取消,其它值不会按按钮但依然返回文本值 :param send: 处理prompt提示框时可输入文本 @@ -313,15 +303,15 @@ class ChromiumPage(ChromiumBase): self._driver.Page.handleJavaScriptDialog(accept=accept) return res_text - def hide_browser(self) -> None: + def hide_browser(self): """隐藏浏览器窗口,只在Windows系统可用""" - _show_or_hide_browser(self, hide=True) + show_or_hide_browser(self, hide=True) - def show_browser(self) -> None: + def show_browser(self): """显示浏览器窗口,只在Windows系统可用""" - _show_or_hide_browser(self, hide=False) + show_or_hide_browser(self, hide=False) - def quit(self) -> None: + def quit(self): """关闭浏览器""" self._tab_obj.Browser.close() self._tab_obj.stop() @@ -360,27 +350,27 @@ class Alert(object): class WindowSizeSetter(object): """用于设置窗口大小的类""" - def __init__(self, page: ChromiumPage): + def __init__(self, page): self.driver = page._driver self.window_id = self._get_info()['windowId'] - def maximized(self) -> None: + def maximized(self): """窗口最大化""" self._perform({'windowState': 'maximized'}) - def minimized(self) -> None: + def minimized(self): """窗口最小化""" self._perform({'windowState': 'minimized'}) - def fullscreen(self) -> None: + def fullscreen(self): """设置窗口为全屏""" self._perform({'windowState': 'fullscreen'}) - def normal(self) -> None: + def normal(self): """设置窗口为常规模式""" self._perform({'windowState': 'normal'}) - def new_size(self, width: int = None, height: int = None) -> None: + def new_size(self, width=None, height=None): """设置窗口大小 \n :param width: 窗口宽度 :param height: 窗口高度 @@ -392,7 +382,7 @@ class WindowSizeSetter(object): height = height or info['height'] self._perform({'width': width, 'height': height}) - def to_location(self, x: int = None, y: int = None) -> None: + def to_location(self, x=None, y=None): """设置窗口在屏幕中的位置,相对左上角坐标 \n :param x: 距离顶部距离 :param y: 距离左边距离 @@ -405,11 +395,11 @@ class WindowSizeSetter(object): y = y or info['top'] self._perform({'left': x, 'top': y}) - def _get_info(self) -> dict: + def _get_info(self): """获取窗口位置及大小信息""" return self.driver.Browser.getWindowBounds() - def _perform(self, bounds: dict) -> None: + def _perform(self, bounds): """执行改变窗口大小操作 :param bounds: 控制数据 :return: None @@ -417,7 +407,7 @@ class WindowSizeSetter(object): self.driver.Browser.setWindowBounds(windowId=self.window_id, bounds=bounds) -def _show_or_hide_browser(page: ChromiumPage, hide: bool = True) -> None: +def show_or_hide_browser(page, hide=True): """执行显示或隐藏浏览器窗口 :param page: ChromePage对象 :param hide: 是否隐藏 @@ -435,20 +425,20 @@ def _show_or_hide_browser(page: ChromiumPage, hide: bool = True) -> None: except ImportError: raise ImportError('请先安装:pip install pypiwin32') - pid = page.process_id or _get_browser_progress_id(page.process, page.address) + pid = page.process_id or get_browser_progress_id(page.process, page.address) if not pid: return None - hds = _get_chrome_hwnds_from_pid(pid, page.title) + hds = get_chrome_hwnds_from_pid(pid, page.title) sw = SW_HIDE if hide else SW_SHOW for hd in hds: ShowWindow(hd, sw) -def _get_browser_progress_id(progress, address: str) -> Union[str, None]: +def get_browser_progress_id(progress, address): """获取浏览器进程id :param progress: 已知的进程对象,没有时传入None :param address: 浏览器管理地址,含端口 - :return: 进程id + :return: 进程id或None """ if progress: return progress.pid @@ -467,7 +457,7 @@ def _get_browser_progress_id(progress, address: str) -> Union[str, None]: return txt.split(' ')[-1] -def _get_chrome_hwnds_from_pid(pid, title) -> list: +def get_chrome_hwnds_from_pid(pid, title): """通过PID查询句柄ID :param pid: 进程id :param title: 窗口标题 diff --git a/DrissionPage/chromium_page.pyi b/DrissionPage/chromium_page.pyi index 49b2a4e..ce4e8d9 100644 --- a/DrissionPage/chromium_page.pyi +++ b/DrissionPage/chromium_page.pyi @@ -1,4 +1,5 @@ # -*- coding:utf-8 -*- +from os import popen from pathlib import Path from typing import Union, Tuple, List @@ -16,6 +17,7 @@ class ChromiumPage(ChromiumBase): tab_id: str = ..., timeout: float = ...): self.options: DriverOptions = ... + self.process: popen = ... self._window_setter: WindowSizeSetter = ... self.main_tab: str = ... self._alert: Alert = ... @@ -111,10 +113,10 @@ class WindowSizeSetter(object): def _perform(self, bounds: dict) -> None: ... -def _show_or_hide_browser(page: ChromiumPage, hide: bool = ...) -> None: ... +def show_or_hide_browser(page: ChromiumPage, hide: bool = ...) -> None: ... -def _get_browser_progress_id(progress, address: str) -> Union[str, None]: ... +def get_browser_progress_id(progress: Union[popen, None], address: str) -> Union[str, None]: ... -def _get_chrome_hwnds_from_pid(pid, title) -> list: ... +def get_chrome_hwnds_from_pid(pid: str, title: str) -> list: ... diff --git a/DrissionPage/chromium_tab.py b/DrissionPage/chromium_tab.py index b96ce53..b9541cb 100644 --- a/DrissionPage/chromium_tab.py +++ b/DrissionPage/chromium_tab.py @@ -5,9 +5,7 @@ from .chromium_base import ChromiumBase class ChromiumTab(ChromiumBase): """实现浏览器标签页的类""" - def __init__(self, - page, - tab_id=None): + def __init__(self, page, tab_id=None): """初始化 \n :param page: ChromiumPage对象 :param tab_id: 要控制的标签页id,不指定默认为激活的 @@ -15,7 +13,7 @@ class ChromiumTab(ChromiumBase): self.page = page super().__init__(page.address, tab_id, page.timeout) - def _set_options(self) -> None: + def _set_options(self): self.set_timeouts(page_load=self.page.timeouts.page_load, script=self.page.timeouts.script, implicit=self.page.timeouts.implicit if self.timeout is None else self.timeout) diff --git a/DrissionPage/chromium_tab.pyi b/DrissionPage/chromium_tab.pyi index 4a7f346..9609f51 100644 --- a/DrissionPage/chromium_tab.pyi +++ b/DrissionPage/chromium_tab.pyi @@ -1,5 +1,4 @@ # -*- coding:utf-8 -*- - from .chromium_base import ChromiumBase from .chromium_page import ChromiumPage diff --git a/DrissionPage/common.py b/DrissionPage/common.py index 28ec24b..41ee9af 100644 --- a/DrissionPage/common.py +++ b/DrissionPage/common.py @@ -15,7 +15,6 @@ from typing import Union from zipfile import ZipFile from urllib.parse import urlparse, urljoin, urlunparse from requests import get as requests_get -from requests.exceptions import ConnectionError as requests_connection_err from .config import DriverOptions diff --git a/DrissionPage/config.py b/DrissionPage/config.py index d2629f7..006b3d7 100644 --- a/DrissionPage/config.py +++ b/DrissionPage/config.py @@ -8,7 +8,6 @@ from configparser import RawConfigParser, NoSectionError, NoOptionError from http.cookiejar import Cookie from pathlib import Path -from typing import Any, Union, List from requests.cookies import RequestsCookieJar from selenium.webdriver.chrome.options import Options @@ -17,7 +16,7 @@ from selenium.webdriver.chrome.options import Options class OptionsManager(object): """管理配置文件内容的类""" - def __init__(self, path: str = None): + def __init__(self, path=None): """初始化,读取配置文件,如没有设置临时文件夹,则设置并新建 \n :param path: ini文件的路径,默认读取模块文件夹下的 """ @@ -31,7 +30,7 @@ class OptionsManager(object): self._chrome_options = None self._session_options = None - def __text__(self) -> str: + def __text__(self): """打印ini文件内容""" return (f"paths:\n" f"{self.get_option('paths')}\n\n" @@ -41,7 +40,7 @@ class OptionsManager(object): f"{self.get_option('session_options')}") @property - def paths(self) -> dict: + def paths(self): """返回paths设置""" if self._paths is None: self._paths = self.get_option('paths') @@ -49,7 +48,7 @@ class OptionsManager(object): return self._paths @property - def chrome_options(self) -> dict: + def chrome_options(self): """返回chrome设置""" if self._chrome_options is None: self._chrome_options = self.get_option('chrome_options') @@ -57,14 +56,14 @@ class OptionsManager(object): return self._chrome_options @property - def session_options(self) -> dict: + def session_options(self): """返回session设置""" if self._session_options is None: self._session_options = self.get_option('session_options') return self._session_options - def get_value(self, section: str, item: str) -> Any: + def get_value(self, section, item): """获取配置的值 \n :param section: 段名 :param item: 项名 @@ -77,7 +76,7 @@ class OptionsManager(object): except NoSectionError and NoOptionError: return None - def get_option(self, section: str) -> dict: + def get_option(self, section): """把section内容以字典方式返回 \n :param section: 段名 :return: 段内容生成的字典 @@ -93,7 +92,7 @@ class OptionsManager(object): return option - def set_item(self, section: str, item: str, value: Any): + def set_item(self, section, item, value): """设置配置值 \n :param section: 段名 :param item: 项名 @@ -104,7 +103,7 @@ class OptionsManager(object): self.__setattr__(f'_{section}', None) return self - def save(self, path: str = None) -> str: + def save(self, path=None): """保存配置文件 \n :param path: ini文件的路径,传入 'default' 保存到默认ini文件 :return: 保存路径 @@ -128,13 +127,13 @@ class OptionsManager(object): return path - def save_to_default(self) -> str: + def save_to_default(self): """保存当前配置到默认ini文件""" return self.save('default') class SessionOptions(object): - def __init__(self, read_file: bool = True, ini_path: str = None): + def __init__(self, read_file=True, ini_path=None): """requests的Session对象配置类 \n :param read_file: 是否从文件读取配置 :param ini_path: ini文件路径 @@ -195,14 +194,14 @@ class SessionOptions(object): self._max_redirects = options_dict['max_redirects'] @property - def headers(self) -> dict: + def headers(self): """返回headers设置信息""" if self._headers is None: self._headers = {} return self._headers @property - def cookies(self) -> list: + def cookies(self): """返回cookies设置信息""" if self._cookies is None: self._cookies = [] @@ -210,12 +209,12 @@ class SessionOptions(object): return self._cookies @property - def auth(self) -> tuple: + def auth(self): """返回auth设置信息""" return self._auth @property - def proxies(self) -> dict: + def proxies(self): """返回proxies设置信息""" if self._proxies is None: self._proxies = {} @@ -223,7 +222,7 @@ class SessionOptions(object): return self._proxies @property - def hooks(self) -> dict: + def hooks(self): """返回hooks设置信息""" if self._hooks is None: self._hooks = {} @@ -231,19 +230,19 @@ class SessionOptions(object): return self._hooks @property - def params(self) -> dict: + def params(self): """返回params设置信息""" if self._params is None: self._params = {} return self._params @property - def verify(self) -> bool: + def verify(self): """返回verify设置信息""" return self._verify @property - def cert(self) -> Union[str, tuple]: + def cert(self): """返回cert设置信息""" return self._cert @@ -253,22 +252,22 @@ class SessionOptions(object): return self._adapters @property - def stream(self) -> bool: + def stream(self): """返回stream设置信息""" return self._stream @property - def trust_env(self) -> bool: + def trust_env(self): """返回trust_env设置信息""" return self._trust_env @property - def max_redirects(self) -> int: + def max_redirects(self): """返回max_redirects设置信息""" return self._max_redirects @headers.setter - def headers(self, headers: dict) -> None: + def headers(self, headers): """设置headers参数 \n :param headers: 参数值 :return: None @@ -276,7 +275,7 @@ class SessionOptions(object): self.set_headers(headers) @cookies.setter - def cookies(self, cookies: Union[RequestsCookieJar, list, tuple, str, dict]) -> None: + def cookies(self, cookies): """设置cookies参数 \n :param cookies: 参数值 :return: None @@ -284,7 +283,7 @@ class SessionOptions(object): self._cookies = cookies @auth.setter - def auth(self, auth: tuple) -> None: + def auth(self, auth): """设置auth参数 \n :param auth: 参数值 :return: None @@ -292,7 +291,7 @@ class SessionOptions(object): self._auth = auth @proxies.setter - def proxies(self, proxies: dict) -> None: + def proxies(self, proxies): """设置proxies参数 \n :param proxies: 参数值 :return: None @@ -300,7 +299,7 @@ class SessionOptions(object): self.set_proxies(proxies) @hooks.setter - def hooks(self, hooks: dict) -> None: + def hooks(self, hooks): """设置hooks参数 \n :param hooks: 参数值 :return: None @@ -308,7 +307,7 @@ class SessionOptions(object): self._hooks = hooks @params.setter - def params(self, params: dict) -> None: + def params(self, params): """设置params参数 \n :param params: 参数值 :return: None @@ -316,7 +315,7 @@ class SessionOptions(object): self._params = params @verify.setter - def verify(self, verify: bool) -> None: + def verify(self, verify): """设置verify参数 \n :param verify: 参数值 :return: None @@ -324,7 +323,7 @@ class SessionOptions(object): self._verify = verify @cert.setter - def cert(self, cert: Union[str, tuple]) -> None: + def cert(self, cert): """设置cert参数 \n :param cert: 参数值 :return: None @@ -332,7 +331,7 @@ class SessionOptions(object): self._cert = cert @adapters.setter - def adapters(self, adapters) -> None: + def adapters(self, adapters): """设置 \n :param adapters: 参数值 :return: None @@ -340,7 +339,7 @@ class SessionOptions(object): self._adapters = adapters @stream.setter - def stream(self, stream: bool) -> None: + def stream(self, stream): """设置stream参数 \n :param stream: 参数值 :return: None @@ -348,7 +347,7 @@ class SessionOptions(object): self._stream = stream @trust_env.setter - def trust_env(self, trust_env: bool) -> None: + def trust_env(self, trust_env): """设置trust_env参数 \n :param trust_env: 参数值 :return: None @@ -356,14 +355,14 @@ class SessionOptions(object): self._trust_env = trust_env @max_redirects.setter - def max_redirects(self, max_redirects: int) -> None: + def max_redirects(self, max_redirects): """设置max_redirects参数 \n :param max_redirects: 参数值 :return: None """ self._max_redirects = max_redirects - def set_headers(self, headers: dict) -> 'SessionOptions': + def set_headers(self, headers): """设置headers参数 \n :param headers: 参数值 :return: 返回当前对象 @@ -371,7 +370,7 @@ class SessionOptions(object): self._headers = {key.lower(): headers[key] for key in headers} return self - def set_a_header(self, attr: str, value: str) -> 'SessionOptions': + def set_a_header(self, attr, value): """设置headers中一个项 \n :param attr: 设置名称 :param value: 设置值 @@ -383,7 +382,7 @@ class SessionOptions(object): self._headers[attr.lower()] = value return self - def remove_a_header(self, attr: str) -> 'SessionOptions': + def remove_a_header(self, attr): """从headers中删除一个设置 \n :param attr: 要删除的设置 :return: 返回当前对象 @@ -397,7 +396,7 @@ class SessionOptions(object): return self - def set_proxies(self, proxies: dict) -> 'SessionOptions': + def set_proxies(self, proxies): """设置proxies参数 \n {'http': 'http://xx.xx.xx.xx:xxxx', 'https': 'http://xx.xx.xx.xx:xxxx'} @@ -407,7 +406,7 @@ class SessionOptions(object): self._proxies = proxies return self - def save(self, path: str = None) -> str: + def save(self, path=None): """保存设置到文件 \n :param path: ini文件的路径,传入 'default' 保存到默认ini文件 :return: 保存文件的绝对路径 @@ -431,7 +430,7 @@ class SessionOptions(object): else: om = OptionsManager(self.ini_path or str(Path(__file__).parent / 'configs.ini')) - options = _session_options_to_dict(self) + options = session_options_to_dict(self) for i in options: om.set_item('session_options', i, options[i]) @@ -441,13 +440,13 @@ class SessionOptions(object): return path - def save_to_default(self) -> str: + def save_to_default(self): """保存当前配置到默认ini文件""" return self.save('default') - def as_dict(self) -> dict: + def as_dict(self): """以字典形式返回本对象""" - return _session_options_to_dict(self) + return session_options_to_dict(self) class DriverOptions(Options): @@ -455,7 +454,7 @@ class DriverOptions(Options): 增加了删除配置和保存到文件方法。 """ - def __init__(self, read_file: bool = True, ini_path: str = None): + def __init__(self, read_file=True, ini_path=None): """初始化,默认从文件读取设置 \n :param read_file: 是否从默认ini文件中读取配置信息 :param ini_path: ini文件路径,为None则读取默认ini文件 @@ -493,22 +492,22 @@ class DriverOptions(Options): self._arguments.append('--no-sandbox') @property - def driver_path(self) -> str: + def driver_path(self): """chromedriver文件路径""" return self._driver_path @property - def chrome_path(self) -> str: + def chrome_path(self): """浏览器启动文件路径""" return self.binary_location or 'chrome' @property - def user_data_path(self) -> str: + def user_data_path(self): """返回用户文件夹路径""" return self._user_data_path # -------------重写父类方法,实现链式操作------------- - def add_argument(self, argument) -> 'DriverOptions': + def add_argument(self, argument): """添加一个配置项 \n :param argument: 配置项内容 :return: 当前对象 @@ -516,7 +515,7 @@ class DriverOptions(Options): super().add_argument(argument) return self - def set_capability(self, name, value) -> 'DriverOptions': + def set_capability(self, name, value): """设置一个capability \n :param name: capability名称 :param value: capability值 @@ -525,7 +524,7 @@ class DriverOptions(Options): super().set_capability(name, value) return self - def add_extension(self, extension: str) -> 'DriverOptions': + def add_extension(self, extension): """添加插件 \n :param extension: crx文件路径 :return: 当前对象 @@ -533,7 +532,7 @@ class DriverOptions(Options): super().add_extension(extension) return self - def add_encoded_extension(self, extension: str) -> 'DriverOptions': + def add_encoded_extension(self, extension): """将带有扩展数据的 Base64 编码字符串添加到将用于将其提取到 ChromeDriver 的列表中 \n :param extension: 带有扩展数据的 Base64 编码字符串 :return: 当前对象 @@ -541,7 +540,7 @@ class DriverOptions(Options): super().add_encoded_extension(extension) return self - def add_experimental_option(self, name: str, value: Union[str, int, dict, List[str]]) -> 'DriverOptions': + def add_experimental_option(self, name, value): """添加一个实验选项到浏览器 \n :param name: 选项名称 :param value: 选项值 @@ -552,7 +551,7 @@ class DriverOptions(Options): # -------------重写父类方法结束------------- - def save(self, path: str = None) -> str: + def save(self, path=None): """保存设置到文件 \n :param path: ini文件的路径, None 保存到当前读取的配置文件,传入 'default' 保存到默认ini文件 :return: 保存文件的绝对路径 @@ -589,11 +588,11 @@ class DriverOptions(Options): return path - def save_to_default(self) -> str: + def save_to_default(self): """保存当前配置到默认ini文件""" return self.save('default') - def remove_argument(self, value: str) -> 'DriverOptions': + def remove_argument(self, value): """移除一个argument项 \n :param value: 设置项名,有值的设置项传入设置名称即可 :return: 当前对象 @@ -609,7 +608,7 @@ class DriverOptions(Options): return self - def remove_experimental_option(self, key: str) -> 'DriverOptions': + def remove_experimental_option(self, key): """移除一个实验设置,传入key值删除 \n :param key: 实验设置的名称 :return: 当前对象 @@ -619,7 +618,7 @@ class DriverOptions(Options): return self - def remove_all_extensions(self) -> 'DriverOptions': + def remove_all_extensions(self): """移除所有插件 \n :return: 当前对象 """ @@ -627,7 +626,7 @@ class DriverOptions(Options): self._extensions = [] return self - def set_argument(self, arg: str, value: Union[bool, str]) -> 'DriverOptions': + def set_argument(self, arg, value): """设置浏览器配置的argument属性 \n :param arg: 属性名 :param value: 属性值,有值的属性传入值,没有的传入bool @@ -641,7 +640,7 @@ class DriverOptions(Options): return self - def set_timeouts(self, implicit: float = None, pageLoad: float = None, script: float = None) -> 'DriverOptions': + def set_timeouts(self, implicit=None, pageLoad=None, script=None): """设置超时时间,设置单位为秒,selenium4以上版本有效 \n :param implicit: 查找元素超时时间 :param pageLoad: 页面加载超时时间 @@ -659,7 +658,7 @@ class DriverOptions(Options): return self - def set_headless(self, on_off: bool = True) -> 'DriverOptions': + def set_headless(self, on_off=True): """设置是否隐藏浏览器界面 \n :param on_off: 开或关 :return: 当前对象 @@ -667,7 +666,7 @@ class DriverOptions(Options): on_off = True if on_off else False return self.set_argument('--headless', on_off) - def set_no_imgs(self, on_off: bool = True) -> 'DriverOptions': + def set_no_imgs(self, on_off=True): """设置是否加载图片 \n :param on_off: 开或关 :return: 当前对象 @@ -675,7 +674,7 @@ class DriverOptions(Options): on_off = True if on_off else False return self.set_argument('--blink-settings=imagesEnabled=false', on_off) - def set_no_js(self, on_off: bool = True) -> 'DriverOptions': + def set_no_js(self, on_off=True): """设置是否禁用js \n :param on_off: 开或关 :return: 当前对象 @@ -683,7 +682,7 @@ class DriverOptions(Options): on_off = True if on_off else False return self.set_argument('--disable-javascript', on_off) - def set_mute(self, on_off: bool = True) -> 'DriverOptions': + def set_mute(self, on_off=True): """设置是否静音 \n :param on_off: 开或关 :return: 当前对象 @@ -691,21 +690,21 @@ class DriverOptions(Options): on_off = True if on_off else False return self.set_argument('--mute-audio', on_off) - def set_user_agent(self, user_agent: str) -> 'DriverOptions': + def set_user_agent(self, user_agent): """设置user agent \n :param user_agent: user agent文本 :return: 当前对象 """ return self.set_argument('--user-agent', user_agent) - def set_proxy(self, proxy: str) -> 'DriverOptions': + def set_proxy(self, proxy): """设置代理 \n :param proxy: 代理url和端口 :return: 当前对象 """ return self.set_argument('--proxy-server', proxy) - def set_page_load_strategy(self, value: str) -> 'DriverOptions': + def set_page_load_strategy(self, value): """设置page_load_strategy,可接收 'normal', 'eager', 'none' \n selenium4以上版本才支持此功能 normal:默认情况下使用, 等待所有资源下载完成 @@ -719,14 +718,8 @@ class DriverOptions(Options): self.page_load_strategy = value.lower() return self - def set_paths(self, - driver_path: str = None, - chrome_path: str = None, - local_port: Union[int, str] = None, - debugger_address: str = None, - download_path: str = None, - user_data_path: str = None, - cache_path: str = None) -> 'DriverOptions': + def set_paths(self, driver_path=None, chrome_path=None, local_port=None, debugger_address=None, download_path=None, + user_data_path=None, cache_path=None): """快捷的路径设置函数 \n :param driver_path: chromedriver.exe路径 :param chrome_path: chrome.exe路径 @@ -761,12 +754,12 @@ class DriverOptions(Options): return self - def as_dict(self) -> dict: + def as_dict(self): """已dict方式返回所有配置信息""" - return _chrome_options_to_dict(self) + return chrome_options_to_dict(self) -def _chrome_options_to_dict(options: Union[dict, DriverOptions, Options, None, bool]) -> Union[dict, None]: +def chrome_options_to_dict(options): """把chrome配置对象转换为字典 \n :param options: chrome配置对象,字典或DriverOptions对象 :return: 配置字典 @@ -798,7 +791,7 @@ def _chrome_options_to_dict(options: Union[dict, DriverOptions, Options, None, b return re_dict -def _session_options_to_dict(options: Union[dict, SessionOptions, None]) -> Union[dict, None]: +def session_options_to_dict(options): """把session配置对象转换为字典 \n :param options: session配置对象或字典 :return: 配置字典 @@ -815,7 +808,7 @@ def _session_options_to_dict(options: Union[dict, SessionOptions, None]) -> Unio cookies = options.__getattribute__('_cookies') if cookies is not None: - re_dict['cookies'] = _cookies_to_tuple(cookies) + re_dict['cookies'] = cookies_to_tuple(cookies) for attr in attrs: val = options.__getattribute__(f'_{attr}') @@ -829,7 +822,7 @@ def _session_options_to_dict(options: Union[dict, SessionOptions, None]) -> Unio return re_dict -def _cookie_to_dict(cookie: Union[Cookie, str, dict]) -> dict: +def cookie_to_dict(cookie): """把Cookie对象转为dict格式 \n :param cookie: Cookie对象 :return: cookie字典 @@ -864,16 +857,16 @@ def _cookie_to_dict(cookie: Union[Cookie, str, dict]) -> dict: return cookie_dict -def _cookies_to_tuple(cookies: Union[RequestsCookieJar, list, tuple, str, dict]) -> tuple: +def cookies_to_tuple(cookies): """把cookies转为tuple格式 \n :param cookies: cookies信息,可为CookieJar, list, tuple, str, dict :return: 返回tuple形式的cookies """ if isinstance(cookies, (list, tuple, RequestsCookieJar)): - cookies = tuple(_cookie_to_dict(cookie) for cookie in cookies) + cookies = tuple(cookie_to_dict(cookie) for cookie in cookies) elif isinstance(cookies, str): - cookies = tuple(_cookie_to_dict(cookie.lstrip()) for cookie in cookies.split(";")) + cookies = tuple(cookie_to_dict(cookie.lstrip()) for cookie in cookies.split(";")) elif isinstance(cookies, dict): cookies = tuple({'name': cookie, 'value': cookies[cookie]} for cookie in cookies) diff --git a/DrissionPage/config.pyi b/DrissionPage/config.pyi new file mode 100644 index 0000000..41c7c5a --- /dev/null +++ b/DrissionPage/config.pyi @@ -0,0 +1,226 @@ +# -*- coding:utf-8 -*- +from configparser import RawConfigParser +from http.cookiejar import Cookie +from typing import Any, Union, List + +from requests.cookies import RequestsCookieJar +from selenium.webdriver.chrome.options import Options + + +class OptionsManager(object): + """管理配置文件内容的类""" + + def __init__(self, path: str = ...): + self.ini_path: str = ... + self._conf: RawConfigParser = ... + self._paths: dict = ... + self._chrome_options: dict = ... + self._session_options: dict = ... + + def __text__(self) -> str: ... + + @property + def paths(self) -> dict: ... + + @property + def chrome_options(self) -> dict: ... + + @property + def session_options(self) -> dict: ... + + def get_value(self, section: str, item: str) -> Any: ... + + def get_option(self, section: str) -> dict: ... + + def set_item(self, section: str, item: str, value: Any) -> OptionsManager: ... + + def save(self, path: str = ...) -> str: ... + + def save_to_default(self) -> str: ... + + +class SessionOptions(object): + def __init__(self, read_file: bool = ..., ini_path: str = ...): + self.ini_path: str = ... + self._headers: dict = ... + self._cookies: list = ... + self._auth: tuple = ... + self._proxies: dict = ... + self._hooks: dict = ... + self._params: dict = ... + self._verify: bool = ... + self._cert: Union[str, tuple] = ... + self._adapters: str = ... + self._stream: bool = ... + self._trust_env: bool = ... + self._max_redirects: int = ... + + @property + def headers(self) -> dict: ... + + @property + def cookies(self) -> list: ... + + @property + def auth(self) -> tuple: ... + + @property + def proxies(self) -> dict: ... + + @property + def hooks(self) -> dict: ... + + @property + def params(self) -> dict: ... + + @property + def verify(self) -> bool: ... + + @property + def cert(self) -> Union[str, tuple]: ... + + @property + def adapters(self): ... + + @property + def stream(self) -> bool: ... + + @property + def trust_env(self) -> bool: ... + + @property + def max_redirects(self) -> int: ... + + @headers.setter + def headers(self, headers: dict) -> None: ... + + @cookies.setter + def cookies(self, cookies: Union[RequestsCookieJar, list, tuple, str, dict]) -> None: ... + + @auth.setter + def auth(self, auth: tuple) -> None: ... + + @proxies.setter + def proxies(self, proxies: dict) -> None: ... + + @hooks.setter + def hooks(self, hooks: dict) -> None: ... + + @params.setter + def params(self, params: dict) -> None: ... + + @verify.setter + def verify(self, verify: bool) -> None: ... + + @cert.setter + def cert(self, cert: Union[str, tuple]) -> None: ... + + @adapters.setter + def adapters(self, adapters) -> None: ... + + @stream.setter + def stream(self, stream: bool) -> None: ... + + @trust_env.setter + def trust_env(self, trust_env: bool) -> None: ... + + @max_redirects.setter + def max_redirects(self, max_redirects: int) -> None: ... + + def set_headers(self, headers: dict) -> 'SessionOptions': ... + + def set_a_header(self, attr: str, value: str) -> 'SessionOptions': ... + + def remove_a_header(self, attr: str) -> 'SessionOptions': ... + + def set_proxies(self, proxies: dict) -> 'SessionOptions': ... + + def save(self, path: str = ...) -> str: ... + + def save_to_default(self) -> str: ... + + def as_dict(self) -> dict: ... + + +class DriverOptions(Options): + """chrome浏览器配置类,继承自selenium.webdriver.chrome.options的Options类, + 增加了删除配置和保存到文件方法。 + """ + + def __init__(self, read_file: bool = ..., ini_path: str = ...): + self.ini_path: str = ... + self._driver_path: str = ... + self._user_data_path: str = ... + + @property + def driver_path(self) -> str: ... + + @property + def chrome_path(self) -> str: ... + + @property + def user_data_path(self) -> str: ... + + # -------------重写父类方法,实现链式操作------------- + def add_argument(self, argument: str) -> 'DriverOptions': ... + + def set_capability(self, name: str, value: str) -> 'DriverOptions': ... + + def add_extension(self, extension: str) -> 'DriverOptions': ... + + def add_encoded_extension(self, extension: str) -> 'DriverOptions': ... + + def add_experimental_option(self, name: str, value: Union[str, int, dict, List[str]]) -> 'DriverOptions': ... + + # -------------重写父类方法结束------------- + + def save(self, path: str = ...) -> str: ... + + def save_to_default(self) -> str: ... + + def remove_argument(self, value: str) -> 'DriverOptions': ... + + def remove_experimental_option(self, key: str) -> 'DriverOptions': ... + + def remove_all_extensions(self) -> 'DriverOptions': ... + + def set_argument(self, arg: str, value: Union[bool, str]) -> 'DriverOptions': ... + + def set_timeouts(self, implicit: float = ..., pageLoad: float = ..., script: float = ...) -> 'DriverOptions': ... + + def set_headless(self, on_off: bool = ...) -> 'DriverOptions': ... + + def set_no_imgs(self, on_off: bool = ...) -> 'DriverOptions': ... + + def set_no_js(self, on_off: bool = ...) -> 'DriverOptions': ... + + def set_mute(self, on_off: bool = ...) -> 'DriverOptions': ... + + def set_user_agent(self, user_agent: str) -> 'DriverOptions': ... + + def set_proxy(self, proxy: str) -> 'DriverOptions': ... + + def set_page_load_strategy(self, value: str) -> 'DriverOptions': ... + + def set_paths(self, + driver_path: str = ..., + chrome_path: str = ..., + local_port: Union[int, str] = ..., + debugger_address: str = ..., + download_path: str = ..., + user_data_path: str = ..., + cache_path: str = ...) -> 'DriverOptions': ... + + def as_dict(self) -> dict: ... + + +def chrome_options_to_dict(options: Union[dict, DriverOptions, Options, None, bool]) -> Union[dict, None]: ... + + +def session_options_to_dict(options: Union[dict, SessionOptions, None]) -> Union[dict, None]: ... + + +def cookie_to_dict(cookie: Union[Cookie, str, dict]) -> dict: ... + + +def cookies_to_tuple(cookies: Union[RequestsCookieJar, list, tuple, str, dict]) -> tuple: ... diff --git a/DrissionPage/drission.py b/DrissionPage/drission.py index 4fcf370..8692c7b 100644 --- a/DrissionPage/drission.py +++ b/DrissionPage/drission.py @@ -4,32 +4,25 @@ @Contact : g1879@qq.com @File : drission.py """ -from sys import exit -from typing import Union - from platform import system +from sys import exit + from requests import Session -from requests.cookies import RequestsCookieJar from requests.structures import CaseInsensitiveDict from selenium import webdriver from selenium.common.exceptions import SessionNotCreatedException, WebDriverException from selenium.webdriver.chrome.options import Options -from selenium.webdriver.chrome.webdriver import WebDriver from selenium.webdriver.remote.webdriver import WebDriver as RemoteWebDriver from tldextract import extract from .common import get_pid_from_port, connect_chrome -from .config import _session_options_to_dict, SessionOptions, DriverOptions, _cookies_to_tuple +from .config import SessionOptions, DriverOptions, cookies_to_tuple, session_options_to_dict class Drission(object): """Drission类用于管理WebDriver对象和Session对象,是驱动器的角色""" - def __init__(self, - driver_or_options: Union[RemoteWebDriver, Options, DriverOptions, bool] = None, - session_or_options: Union[Session, dict, SessionOptions, bool] = None, - ini_path: str = None, - proxy: dict = None): + def __init__(self, driver_or_options=None, session_or_options=None, ini_path=None, proxy=None): """初始化,可接收现成的WebDriver和Session对象,或接收它们的配置信息生成对象 \n :param driver_or_options: driver对象或DriverOptions、Options类,传入False则创建空配置对象 :param session_or_options: Session对象或设置字典,传入False则创建空配置对象 @@ -86,7 +79,7 @@ class Drission(object): pass @property - def session(self) -> Session: + def session(self): """返回Session对象,如未初始化则按配置信息创建""" if self._session is None: self._set_session(self._session_options) @@ -97,7 +90,7 @@ class Drission(object): return self._session @property - def driver(self) -> WebDriver: + def driver(self): """返回WebDriver对象,如未初始化则按配置信息创建。 \n 如设置了本地调试浏览器,可自动接入或打开浏览器进程。 """ @@ -114,7 +107,7 @@ class Drission(object): chrome_path, self._debugger = connect_chrome(self.driver_options) # -----------创建WebDriver对象----------- - self._driver = _create_driver(chrome_path, driver_path, self.driver_options) + self._driver = create_driver(chrome_path, driver_path, self.driver_options) # -----------解决接管新版浏览器不能定位到正确的标签页的问题----------- active_tab = self._driver.window_handles[0] @@ -130,31 +123,31 @@ class Drission(object): return self._driver @property - def driver_options(self) -> Union[DriverOptions, Options]: + def driver_options(self): """返回driver配置信息""" return self._driver_options @property - def session_options(self) -> dict: + def session_options(self): """返回session配置信息""" return self._session_options @session_options.setter - def session_options(self, options: Union[dict, SessionOptions]) -> None: + def session_options(self, options): """设置session配置 \n :param options: session配置字典 :return: None """ - self._session_options = _session_options_to_dict(options) + self._session_options = session_options_to_dict(options) self._set_session(self._session_options) @property - def proxy(self) -> Union[None, dict]: + def proxy(self): """返回代理信息""" return self._proxy @proxy.setter - def proxy(self, proxies: dict = None) -> None: + def proxy(self, proxies=None): """设置代理信息 \n :param proxies: 代理信息字典 :return: None @@ -180,13 +173,13 @@ class Drission(object): """调试浏览器进程""" return self._debugger - def kill_browser(self) -> None: + def kill_browser(self): """关闭浏览器进程(如果可以)""" pid = self.get_browser_progress_id() - if not _kill_progress(pid): + if not kill_progress(pid): self._driver.quit() - def get_browser_progress_id(self) -> Union[str, None]: + def get_browser_progress_id(self): """获取浏览器进程id""" if self.debugger_progress: return self.debugger_progress.pid @@ -209,15 +202,15 @@ class Drission(object): return txt.split(' ')[-1] - def hide_browser(self) -> None: + def hide_browser(self): """隐藏浏览器界面""" self._show_or_hide_browser() - def show_browser(self) -> None: + def show_browser(self): """显示浏览器界面""" self._show_or_hide_browser(False) - def _show_or_hide_browser(self, hide: bool = True) -> None: + def _show_or_hide_browser(self, hide=True): if system().lower() != 'windows': raise OSError('该方法只能在Windows系统使用。') @@ -231,22 +224,19 @@ class Drission(object): if not pid: print('只有设置了debugger_address参数才能使用 show_browser() 和 hide_browser()') return - hds = _get_chrome_hwnds_from_pid(pid) + hds = get_chrome_hwnds_from_pid(pid) sw = SW_HIDE if hide else SW_SHOW for hd in hds: ShowWindow(hd, sw) - def set_cookies(self, - cookies: Union[RequestsCookieJar, list, tuple, str, dict], - set_session: bool = False, - set_driver: bool = False) -> None: + def set_cookies(self, cookies, set_session=False, set_driver=False): """设置cookies \n :param cookies: cookies信息,可为CookieJar, list, tuple, str, dict :param set_session: 是否设置session的cookies :param set_driver: 是否设置driver的cookies :return: None """ - cookies = _cookies_to_tuple(cookies) + cookies = cookies_to_tuple(cookies) for cookie in cookies: if cookie['value'] is None: @@ -296,7 +286,7 @@ class Drission(object): self.driver.add_cookie(cookie) - def _set_session(self, data: dict) -> None: + def _set_session(self, data): """根据传入字典对session进行设置 \n :param data: session配置字典 :return: None @@ -315,7 +305,7 @@ class Drission(object): if i in data: self._session.__setattr__(i, data[i]) - def cookies_to_session(self, copy_user_agent: bool = False) -> None: + def cookies_to_session(self, copy_user_agent=False): """把driver对象的cookies复制到session对象 \n :param copy_user_agent: 是否复制ua信息 :return: None @@ -325,7 +315,7 @@ class Drission(object): self.set_cookies(self.driver.get_cookies(), set_session=True) - def cookies_to_driver(self, url: str) -> None: + def cookies_to_driver(self, url): """把session对象的cookies复制到driver对象 \n :param url: 作用域 :return: None @@ -348,10 +338,10 @@ class Drission(object): self.set_cookies(cookies, set_driver=True) - def close_driver(self, kill: bool = False) -> None: + def close_driver(self, kill=False): """关闭driver和浏览器""" if self._driver: - _kill_progress(port=self._driver.service.port) # 关闭chromedriver.exe进程 + kill_progress(port=self._driver.service.port) # 关闭chromedriver.exe进程 if kill: self.kill_browser() @@ -360,13 +350,13 @@ class Drission(object): self._driver = None - def close_session(self) -> None: + def close_session(self): """关闭session""" if self._session: self._session.close() self._session = None - def close(self) -> None: + def close(self): """关闭session、driver和浏览器""" if self._driver: self.close_driver() @@ -375,7 +365,7 @@ class Drission(object): self.close_session() -def user_agent_to_session(driver: RemoteWebDriver, session: Session) -> None: +def user_agent_to_session(driver, session): """把driver的user-agent复制到session \n :param driver: 来源driver对象 :param session: 目标session对象 @@ -387,7 +377,7 @@ def user_agent_to_session(driver: RemoteWebDriver, session: Session) -> None: session.headers.update({"User-Agent": selenium_user_agent}) -def _create_driver(chrome_path: str, driver_path: str, options: Options) -> WebDriver: +def create_driver(chrome_path, driver_path, options): """创建 WebDriver 对象 \n :param chrome_path: chrome.exe 路径 :param driver_path: chromedriver.exe 路径 @@ -424,7 +414,7 @@ def _create_driver(chrome_path: str, driver_path: str, options: Options) -> WebD exit(0) -def _get_chrome_hwnds_from_pid(pid) -> list: +def get_chrome_hwnds_from_pid(pid): """通过PID查询句柄ID""" try: from win32gui import IsWindow, GetWindowText, EnumWindows @@ -444,7 +434,7 @@ def _get_chrome_hwnds_from_pid(pid) -> list: return hwnds -def _kill_progress(pid: str = None, port: int = None) -> bool: +def kill_progress(pid=None, port=None): """关闭浏览器进程 \n :param pid: 进程id :param port: 端口号,如没有进程id,从端口号获取 diff --git a/DrissionPage/drission.pyi b/DrissionPage/drission.pyi index 2c14468..74e534e 100644 --- a/DrissionPage/drission.pyi +++ b/DrissionPage/drission.pyi @@ -83,10 +83,10 @@ class Drission(object): def user_agent_to_session(driver: RemoteWebDriver, session: Session) -> None: ... -def _create_driver(chrome_path: str, driver_path: str, options: Options) -> WebDriver: ... +def create_driver(chrome_path: str, driver_path: str, options: Options) -> WebDriver: ... -def _get_chrome_hwnds_from_pid(pid) -> list: ... +def get_chrome_hwnds_from_pid(pid:str) -> list: ... -def _kill_progress(pid: str = ..., port: int = ...) -> bool: ... +def kill_progress(pid: str = ..., port: int = ...) -> bool: ... diff --git a/DrissionPage/driver_element.py b/DrissionPage/driver_element.py index 252f7b5..b533364 100644 --- a/DrissionPage/driver_element.py +++ b/DrissionPage/driver_element.py @@ -7,7 +7,6 @@ from os import sep from pathlib import Path from time import time, perf_counter, sleep -from typing import Union, List, Any, Tuple from selenium.common.exceptions import TimeoutException, JavascriptException, InvalidElementStateException, \ NoSuchElementException @@ -18,13 +17,13 @@ from selenium.webdriver.support.wait import WebDriverWait from .base import DrissionElement, BaseElement from .common import str_to_loc, get_usable_path, format_html, get_ele_txt, get_loc -from .session_element import make_session_ele, SessionElement +from .session_element import make_session_ele class DriverElement(DrissionElement): """driver模式的元素对象,包装了一个WebElement对象,并封装了常用功能""" - def __init__(self, ele: WebElement, page=None): + def __init__(self, ele, page=None): """初始化对象 \n :param ele: 被包装的WebElement元素 :param page: 元素所在页面 @@ -34,13 +33,11 @@ class DriverElement(DrissionElement): self._scroll = None self._inner_ele = ele - def __repr__(self) -> str: + def __repr__(self): attrs = [f"{attr}='{self.attrs[attr]}'" for attr in self.attrs] return f'' - def __call__(self, - loc_or_str: Union[Tuple[str, str], str], - timeout: float = None) -> Union['DriverElement', str, None]: + def __call__(self, loc_or_str, timeout=None): """在内部查找元素 \n 例:ele2 = ele1('@id=ele_id') \n :param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串 @@ -51,26 +48,26 @@ class DriverElement(DrissionElement): # -----------------共有属性和方法------------------- @property - def inner_ele(self) -> WebElement: + def inner_ele(self): return self._inner_ele @property - def tag(self) -> str: + def tag(self): """返回元素类型""" return self._inner_ele.tag_name.lower() @property - def html(self) -> str: + def html(self): """返回元素outerHTML文本""" return self.inner_ele.get_attribute('outerHTML') @property - def inner_html(self) -> str: + def inner_html(self): """返回元素innerHTML文本""" return self.inner_ele.get_attribute('innerHTML') @property - def attrs(self) -> dict: + def attrs(self): """返回元素所有属性及值""" js = ''' var dom=arguments[0]; @@ -89,16 +86,16 @@ class DriverElement(DrissionElement): return {attr: self.attr(attr) for attr in eval(self.run_script(js))} @property - def text(self) -> str: + def text(self): """返回元素内所有文本""" return get_ele_txt(make_session_ele(self.html)) @property - def raw_text(self) -> str: + def raw_text(self): """返回未格式化处理的元素内文本""" return self.inner_ele.get_attribute('innerText') - def attr(self, attr: str) -> str: + def attr(self, attr): """获取attribute属性值 \n :param attr: 属性名 :return: 属性值文本 @@ -114,9 +111,7 @@ class DriverElement(DrissionElement): else: return format_html(self.inner_ele.get_attribute(attr)) - def ele(self, - loc_or_str: Union[Tuple[str, str], str], - timeout: float = None) -> Union['DriverElement', str, None]: + def ele(self, loc_or_str, timeout=None): """返回当前元素下级符合条件的第一个元素、属性或节点文本 \n :param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串 :param timeout: 查找元素超时时间,默认与元素所在页面等待时间一致 @@ -124,9 +119,7 @@ class DriverElement(DrissionElement): """ return self._ele(loc_or_str, timeout) - def eles(self, - loc_or_str: Union[Tuple[str, str], str], - timeout: float = None) -> List[Union['DriverElement', str]]: + def eles(self, loc_or_str, timeout=None): """返回当前元素下级所有符合条件的子元素、属性或节点文本 \n :param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串 :param timeout: 查找元素超时时间,默认与元素所在页面等待时间一致 @@ -134,25 +127,21 @@ class DriverElement(DrissionElement): """ return self._ele(loc_or_str, timeout=timeout, single=False) - def s_ele(self, loc_or_str: Union[Tuple[str, str], str] = None) -> Union[SessionElement, str, None]: + def s_ele(self, loc_or_str=None): """查找第一个符合条件的元素以SessionElement形式返回,处理复杂页面时效率很高 \n :param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串 :return: SessionElement对象或属性、文本 """ return make_session_ele(self, loc_or_str) - def s_eles(self, loc_or_str: Union[Tuple[str, str], str] = None) -> List[Union[SessionElement, str]]: + def s_eles(self, loc_or_str=None): """查找所有符合条件的元素以SessionElement列表形式返回 \n :param loc_or_str: 定位符 :return: SessionElement或属性、文本组成的列表 """ return make_session_ele(self, loc_or_str, single=False) - def _ele(self, - loc_or_str: Union[Tuple[str, str], str], - timeout: float = None, - single: bool = True, - relative: bool = False) -> Union['DriverElement', str, None, List[Union['DriverElement', str]]]: + def _ele(self, loc_or_str, timeout=None, single=True, relative=False): """返回当前元素下级符合条件的子元素、属性或节点文本,默认返回第一个 \n :param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串 :param timeout: 查找元素超时时间 @@ -162,7 +151,7 @@ class DriverElement(DrissionElement): """ return make_driver_ele(self, loc_or_str, single, timeout) - def _get_ele_path(self, mode) -> str: + def _get_ele_path(self, mode): """返获取css路径或xpath路径""" if mode == 'xpath': txt1 = 'var tag = el.nodeName.toLowerCase();' @@ -206,12 +195,12 @@ class DriverElement(DrissionElement): # -----------------driver独有属性和方法------------------- @property - def size(self) -> dict: + def size(self): """返回元素宽和高""" return self.inner_ele.size @property - def location(self) -> dict: + def location(self): """返回元素左上角坐标""" return self.inner_ele.location @@ -229,12 +218,12 @@ class DriverElement(DrissionElement): return self.shadow_root @property - def pseudo_before(self) -> str: + def pseudo_before(self): """返回当前元素的::before伪元素内容""" return self.style('content', 'before') @property - def pseudo_after(self) -> str: + def pseudo_after(self): """返回当前元素的::after伪元素内容""" return self.style('content', 'after') @@ -250,23 +239,20 @@ class DriverElement(DrissionElement): return self._select @property - def scroll(self) -> 'Scroll': + def scroll(self): """用于滚动滚动条的对象""" if self._scroll is None: self._scroll = Scroll(self) return self._scroll - def parent(self, level_or_loc: Union[tuple, str, int] = 1) -> Union['DriverElement', None]: + def parent(self, level_or_loc=1): """返回上面某一级父元素,可指定层数或用查询语法定位 \n :param level_or_loc: 第几级父元素,或定位符 :return: 上级元素对象 """ return super().parent(level_or_loc) - def prev(self, - index: int = 1, - filter_loc: Union[tuple, str] = '', - timeout: float = 0) -> Union['DriverElement', str, None]: + def prev(self, index=1, filter_loc='', timeout=0): """返回前面的一个兄弟元素,可用查询语法筛选,可指定返回筛选结果的第几个 \n :param index: 前面第几个查询结果元素 :param filter_loc: 用于筛选元素的查询语法 @@ -275,10 +261,7 @@ class DriverElement(DrissionElement): """ return super().prev(index, filter_loc, timeout) - def next(self, - index: int = 1, - filter_loc: Union[tuple, str] = '', - timeout: float = 0) -> Union['DriverElement', str, None]: + def next(self, index=1, filter_loc='', timeout=0): """返回后面的一个兄弟元素,可用查询语法筛选,可指定返回筛选结果的第几个 \n :param index: 后面第几个查询结果元素 :param filter_loc: 用于筛选元素的查询语法 @@ -287,10 +270,7 @@ class DriverElement(DrissionElement): """ return super().next(index, filter_loc, timeout) - def before(self, - index: int = 1, - filter_loc: Union[tuple, str] = '', - timeout: float = None) -> Union['DriverElement', str, None]: + def before(self, index=1, filter_loc='', timeout=None): """返回当前元素前面的一个元素,可指定筛选条件和第几个。查找范围不限兄弟元,而是整个DOM文档 \n :param index: 前面第几个查询结果元素 :param filter_loc: 用于筛选元素的查询语法 @@ -299,10 +279,7 @@ class DriverElement(DrissionElement): """ return super().before(index, filter_loc, timeout) - def after(self, - index: int = 1, - filter_loc: Union[tuple, str] = '', - timeout: float = None) -> Union['DriverElement', str, None]: + def after(self, index=1, filter_loc='', timeout=None): """返回当前元素后面的一个元素,可指定筛选条件和第几个。查找范围不限兄弟元,而是整个DOM文档 \n :param index: 后面第几个查询结果元素 :param filter_loc: 用于筛选元素的查询语法 @@ -311,9 +288,7 @@ class DriverElement(DrissionElement): """ return super().after(index, filter_loc, timeout) - def prevs(self, - filter_loc: Union[tuple, str] = '', - timeout: float = 0) -> List[Union['DriverElement', str]]: + def prevs(self, filter_loc='', timeout=0): """返回前面全部兄弟元素或节点组成的列表,可用查询语法筛选 \n :param filter_loc: 用于筛选元素的查询语法 :param timeout: 查找元素的超时时间 @@ -321,9 +296,7 @@ class DriverElement(DrissionElement): """ return super().prevs(filter_loc, timeout) - def nexts(self, - filter_loc: Union[tuple, str] = '', - timeout: float = 0) -> List[Union['DriverElement', str]]: + def nexts(self, filter_loc='', timeout=0): """返回后面全部兄弟元素或节点组成的列表,可用查询语法筛选 \n :param filter_loc: 用于筛选元素的查询语法 :param timeout: 查找元素的超时时间 @@ -331,9 +304,7 @@ class DriverElement(DrissionElement): """ return super().nexts(filter_loc, timeout) - def befores(self, - filter_loc: Union[tuple, str] = '', - timeout: float = None) -> List[Union['DriverElement', str]]: + def befores(self, filter_loc='', timeout=None): """返回当前元素后面符合条件的全部兄弟元素或节点组成的列表,可用查询语法筛选。查找范围不限兄弟元,而是整个DOM文档 \n :param filter_loc: 用于筛选元素的查询语法 :param timeout: 查找元素的超时时间 @@ -341,9 +312,7 @@ class DriverElement(DrissionElement): """ return super().befores(filter_loc, timeout) - def afters(self, - filter_loc: Union[tuple, str] = '', - timeout: float = None) -> List[Union['DriverElement', str]]: + def afters(self, filter_loc='', timeout=None): """返回当前元素前面符合条件的全部兄弟元素或节点组成的列表,可用查询语法筛选。查找范围不限兄弟元,而是整个DOM文档 \n :param filter_loc: 用于筛选元素的查询语法 :param timeout: 查找元素的超时时间 @@ -351,7 +320,7 @@ class DriverElement(DrissionElement): """ return super().afters(filter_loc, timeout) - def left(self, index: int = 1, filter_loc: Union[tuple, str] = '') -> 'DriverElement': + def left(self, index=1, filter_loc=''): """获取网页上显示在当前元素左边的某个元素,可设置选取条件,可指定结果中第几个 \n :param index: 获取第几个 :param filter_loc: 筛选条件,可用selenium的(By, str),也可用本库定位语法 @@ -360,7 +329,7 @@ class DriverElement(DrissionElement): eles = self._get_relative_eles('left', filter_loc) return eles[index - 1] if index <= len(eles) else None - def right(self, index: int = 1, filter_loc: Union[tuple, str] = '') -> 'DriverElement': + def right(self, index=1, filter_loc=''): """获取网页上显示在当前元素右边的某个元素,可设置选取条件,可指定结果中第几个 \n :param index: 获取第几个 :param filter_loc: 筛选条件,可用selenium的(By, str),也可用本库定位语法 @@ -369,7 +338,7 @@ class DriverElement(DrissionElement): eles = self._get_relative_eles('right', filter_loc) return eles[index - 1] if index <= len(eles) else None - def above(self, index: int = 1, filter_loc: Union[tuple, str] = '') -> 'DriverElement': + def above(self, index=1, filter_loc=''): """获取网页上显示在当前元素上边的某个元素,可设置选取条件,可指定结果中第几个 \n :param index: 获取第几个 :param filter_loc: 筛选条件,可用selenium的(By, str),也可用本库定位语法 @@ -378,7 +347,7 @@ class DriverElement(DrissionElement): eles = self._get_relative_eles('left', filter_loc) return eles[index - 1] if index <= len(eles) else None - def below(self, index: int = 1, filter_loc: Union[tuple, str] = '') -> 'DriverElement': + def below(self, index=1, filter_loc=''): """获取网页上显示在当前元素下边的某个元素,可设置选取条件,可指定结果中第几个 \n :param index: 获取第几个 :param filter_loc: 筛选条件,可用selenium的(By, str),也可用本库定位语法 @@ -387,7 +356,7 @@ class DriverElement(DrissionElement): eles = self._get_relative_eles('left', filter_loc) return eles[index - 1] if index <= len(eles) else None - def near(self, index: int = 1, filter_loc: Union[tuple, str] = '') -> 'DriverElement': + def near(self, index=1, filter_loc=''): """获取网页上显示在当前元素最近的某个元素,可设置选取条件,可指定结果中第几个 \n :param index: 获取第几个 :param filter_loc: 筛选条件,可用selenium的(By, str),也可用本库定位语法 @@ -396,44 +365,42 @@ class DriverElement(DrissionElement): eles = self._get_relative_eles('near', filter_loc) return eles[index - 1] if index <= len(eles) else None - def lefts(self, filter_loc: Union[tuple, str] = '') -> List['DriverElement']: + def lefts(self, filter_loc=''): """获取网页上显示在当前元素左边的所有元素,可设置选取条件,从近到远排列 \n :param filter_loc: 筛选条件,可用selenium的(By, str),也可用本库定位语法 :return: DriverElement对象组成的列表 """ return self._get_relative_eles('left', filter_loc) - def rights(self, filter_loc: Union[tuple, str] = '') -> List['DriverElement']: + def rights(self, filter_loc=''): """获取网页上显示在当前元素右边的所有元,可设置选取条件,从近到远排列 \n :param filter_loc: 筛选条件,可用selenium的(By, str),也可用本库定位语法 :return: DriverElement对象组成的列表 """ return self._get_relative_eles('right', filter_loc) - def aboves(self, filter_loc: Union[tuple, str] = '') -> List['DriverElement']: + def aboves(self, filter_loc=''): """获取网页上显示在当前元素上边的所有元素,可设置选取条件,从近到远排列 \n :param filter_loc: 筛选条件,可用selenium的(By, str),也可用本库定位语法 :return: DriverElement对象组成的列表 """ return self._get_relative_eles('left', filter_loc) - def belows(self, filter_loc: Union[tuple, str] = '') -> List['DriverElement']: + def belows(self, filter_loc=''): """获取网页上显示在当前元素下边的所有元素,可设置选取条件,从近到远排列 \n :param filter_loc: 筛选条件,可用selenium的(By, str),也可用本库定位语法 :return: DriverElement对象组成的列表 """ return self._get_relative_eles('left', filter_loc) - def nears(self, filter_loc: Union[tuple, str] = '') -> List['DriverElement']: + def nears(self, filter_loc=''): """获取网页上显示在当前元素附近元素,可设置选取条件,从近到远排列 \n :param filter_loc: 筛选条件,可用selenium的(By, str),也可用本库定位语法 :return: DriverElement对象组成的列表 """ return self._get_relative_eles('near', filter_loc) - def wait_ele(self, - loc_or_ele: Union[str, tuple, DrissionElement, WebElement], - timeout: float = None) -> 'ElementWaiter': + def wait_ele(self, loc_or_ele, timeout=None): """等待子元素从dom删除、显示、隐藏 \n :param loc_or_ele: 可以是元素、查询字符串、loc元组 :param timeout: 等待超时时间 @@ -441,7 +408,7 @@ class DriverElement(DrissionElement): """ return ElementWaiter(self, loc_or_ele, timeout) - def style(self, style: str, pseudo_ele: str = '') -> str: + def style(self, style, pseudo_ele=''): """返回元素样式属性值,可获取伪元素属性值 \n :param style: 样式属性名称 :param pseudo_ele: 伪元素名称(如有) @@ -453,7 +420,7 @@ class DriverElement(DrissionElement): return None if r == 'none' else r - def click(self, by_js: bool = None, timeout: float = None) -> bool: + def click(self, by_js=None, timeout=None): """点击元素 \n 尝试点击直到超时,若都失败就改用js点击 \n :param by_js: 是否用js点击,为True时直接用js点击,为False时重试失败也不会改用js @@ -485,10 +452,7 @@ class DriverElement(DrissionElement): return False - def click_at(self, - x: Union[int, str] = None, - y: Union[int, str] = None, - by_js: bool = False) -> None: + def click_at(self, x=None, y=None, by_js=False): """带偏移量点击本元素,相对于左上角坐标。不传入x或y值时点击元素中点 \n :param x: 相对元素左上角坐标的x轴偏移量 :param y: 相对元素左上角坐标的y轴偏移量 @@ -514,12 +478,12 @@ class DriverElement(DrissionElement): from selenium.webdriver import ActionChains ActionChains(self.page.driver).move_to_element_with_offset(self.inner_ele, x, y).click().perform() - def r_click(self) -> None: + def r_click(self): """右键单击""" from selenium.webdriver import ActionChains ActionChains(self.page.driver).context_click(self.inner_ele).perform() - def r_click_at(self, x: Union[int, str] = None, y: Union[int, str] = None) -> None: + def r_click_at(self, x=None, y=None): """带偏移量右键单击本元素,相对于左上角坐标。不传入x或y值时点击元素中点 \n :param x: 相对元素左上角坐标的x轴偏移量 :param y: 相对元素左上角坐标的y轴偏移量 @@ -530,11 +494,7 @@ class DriverElement(DrissionElement): from selenium.webdriver import ActionChains ActionChains(self.page.driver).move_to_element_with_offset(self.inner_ele, x, y).context_click().perform() - def input(self, - vals: Union[str, tuple], - clear: bool = True, - insure: bool = True, - timeout: float = None) -> bool: + def input(self, vals, clear=True, insure=True, timeout=None): """输入文本或组合键,也可用于输入文件路径到input元素(文件间用\n间隔) \n :param vals: 文本值或按键组合 :param clear: 输入前是否清空文本框 @@ -580,7 +540,7 @@ class DriverElement(DrissionElement): self.inner_ele.send_keys(enter) return True - def run_script(self, script: str, *args) -> Any: + def run_script(self, script, *args): """执行js代码,代码中用arguments[0]表示自己 \n :param script: js文本 :param args: 传入的参数 @@ -588,7 +548,7 @@ class DriverElement(DrissionElement): """ return self.inner_ele.parent.execute_script(script, self.inner_ele, *args) - def submit(self) -> Union[bool, None]: + def submit(self): """提交表单""" try: self.inner_ele.submit() @@ -596,7 +556,7 @@ class DriverElement(DrissionElement): except Exception: pass - def clear(self, insure: bool = True) -> Union[None, bool]: + def clear(self, insure=True): """清空元素文本 \n :param insure: 是否确保清空 :return: 是否清空成功,不能清空的元素返回None @@ -611,19 +571,19 @@ class DriverElement(DrissionElement): except InvalidElementStateException: return None - def is_selected(self) -> bool: + def is_selected(self): """是否选中""" return self.inner_ele.is_selected() - def is_enabled(self) -> bool: + def is_enabled(self): """是否可用""" return self.inner_ele.is_enabled() - def is_displayed(self) -> bool: + def is_displayed(self): """是否可见""" return self.inner_ele.is_displayed() - def is_valid(self) -> bool: + def is_valid(self): """用于判断元素是否还在DOM内,应对页面跳转元素不能用的情况""" try: self.is_enabled() @@ -631,7 +591,7 @@ class DriverElement(DrissionElement): except Exception: return False - def screenshot(self, path: str = None, filename: str = None, as_bytes: bool = False) -> Union[str, bytes]: + def screenshot(self, path=None, filename=None, as_bytes=False): """对元素进行截图 \n :param path: 保存路径 :param filename: 图片文件名,不传入时以元素tag name命名 @@ -661,14 +621,14 @@ class DriverElement(DrissionElement): return img_path - def prop(self, prop: str) -> str: + def prop(self, prop): """获取property属性值 \n :param prop: 属性名 :return: 属性值文本 """ return format_html(self.inner_ele.get_property(prop)) - def set_prop(self, prop: str, value: str) -> bool: + def set_prop(self, prop, value): """设置元素property属性 \n :param prop: 属性名 :param value: 属性值 @@ -681,7 +641,7 @@ class DriverElement(DrissionElement): except Exception: return False - def set_attr(self, attr: str, value: str) -> bool: + def set_attr(self, attr, value): """设置元素attribute属性 \n :param attr: 属性名 :param value: 属性值 @@ -693,7 +653,7 @@ class DriverElement(DrissionElement): except Exception: return False - def remove_attr(self, attr: str) -> bool: + def remove_attr(self, attr): """删除元素attribute属性 \n :param attr: 属性名 :return: 是否删除成功 @@ -704,7 +664,7 @@ class DriverElement(DrissionElement): except Exception: return False - def drag(self, x: int, y: int, speed: int = 40, shake: bool = True) -> None: + def drag(self, x, y, speed=40, shake=True): """拖拽当前元素到相对位置 \n :param x: x变化值 :param y: y变化值 @@ -716,10 +676,7 @@ class DriverElement(DrissionElement): y += self.location['y'] + self.size['height'] // 2 self.drag_to((x, y), speed, shake) - def drag_to(self, - ele_or_loc: Union[tuple, WebElement, DrissionElement], - speed: int = 40, - shake: bool = True) -> None: + def drag_to(self, ele_or_loc, speed=40, shake=True): """拖拽当前元素,目标为另一个元素或坐标元组 \n :param ele_or_loc: 另一个元素或坐标元组,坐标为元素中点的坐标 :param speed: 拖动的速度,传入0即瞬间到达 @@ -759,7 +716,7 @@ class DriverElement(DrissionElement): current_x, current_y = x, y actions.release().perform() - def hover(self, x: int = None, y: int = None) -> None: + def hover(self, x=None, y=None): """鼠标悬停,可接受偏移量,偏移量相对于元素左上角坐标。不传入x或y值时悬停在元素中点 \n :param x: 相对元素左上角坐标的x轴偏移量 :param y: 相对元素左上角坐标的y轴偏移量 @@ -770,9 +727,7 @@ class DriverElement(DrissionElement): y = int(y) if y is not None else self.size['height'] // 2 ActionChains(self.page.driver).move_to_element_with_offset(self.inner_ele, x, y).perform() - def _get_relative_eles(self, - mode: str, - loc: Union[tuple, str] = '') -> Union[List['DriverElement'], 'DriverElement']: + def _get_relative_eles(self, mode, loc=''): """获取网页上相对于当前元素周围的某个元素,可设置选取条件 \n :param mode: 可选:'left', 'right', 'above', 'below', 'near' :param loc: 筛选条件,可用selenium的(By, str),也可用本库定位语法 @@ -801,10 +756,7 @@ class DriverElement(DrissionElement): raise ValueError('未找到元素,请检查浏览器版本,低版本的浏览器无法使用此方法。') -def make_driver_ele(page_or_ele, - loc: Union[str, Tuple[str, str]], - single: bool = True, - timeout: float = None) -> Union[DriverElement, str, None, List[Union[DriverElement, str]]]: +def make_driver_ele(page_or_ele, loc, single=True, timeout=None): """执行driver模式元素的查找 \n 页面查找元素及元素查找下级元素皆使用此方法 \n :param page_or_ele: DriverPage对象或DriverElement对象 @@ -873,7 +825,7 @@ def make_driver_ele(page_or_ele, class ElementsByXpath(object): """用js通过xpath获取元素、节点或属性,与WebDriverWait配合使用""" - def __init__(self, page, xpath: str = None, single: bool = False, timeout: float = 10): + def __init__(self, page, xpath=None, single=False, timeout=10): """ :param page: DrissionPage对象 :param xpath: xpath文本 @@ -885,8 +837,7 @@ class ElementsByXpath(object): self.single = single self.timeout = timeout - def __call__(self, ele_or_driver: Union[RemoteWebDriver, WebElement]) \ - -> Union[str, DriverElement, None, List[str or DriverElement]]: + def __call__(self, ele_or_driver): def get_nodes(node=None, xpath_txt=None, type_txt='7'): """用js通过xpath获取元素、节点或属性 @@ -982,7 +933,7 @@ class Select(object): self.inner_ele = ele self.select_ele = SeleniumSelect(ele.inner_ele) - def __call__(self, text_or_index: Union[str, int, list, tuple], timeout=None) -> bool: + def __call__(self, text_or_index, timeout=None): """选定下拉列表中子元素 \n :param text_or_index: 根据文本、值选或序号择选项,若允许多选,传入list或tuple可多选 :param timeout: 超时时间,不输入默认实用页面超时时间 @@ -992,17 +943,17 @@ class Select(object): return self.select(text_or_index, timeout=timeout) @property - def is_multi(self) -> bool: + def is_multi(self): """返回是否多选表单""" return self.select_ele.is_multiple @property - def options(self) -> List[DriverElement]: + def options(self): """返回所有选项元素组成的列表""" return self.inner_ele.eles('tag:option') @property - def selected_option(self) -> Union[DriverElement, None]: + def selected_option(self): """返回第一个被选中的option元素 \n :return: DriverElement对象或None """ @@ -1010,17 +961,17 @@ class Select(object): return None if ele is None else DriverElement(ele, self.inner_ele.page) @property - def selected_options(self) -> List[DriverElement]: + def selected_options(self): """返回所有被选中的option元素列表 \n :return: DriverElement对象组成的列表 """ return [x for x in self.options if x.is_selected()] - def clear(self) -> None: + def clear(self): """清除所有已选项""" self.select_ele.deselect_all() - def select(self, text_or_index: Union[str, int, list, tuple], timeout=None) -> bool: + def select(self, text_or_index, timeout=None): """选定下拉列表中子元素 \n :param text_or_index: 根据文本、值选或序号择选项,若允许多选,传入list或tuple可多选 :param timeout: 超时时间,不输入默认实用页面超时时间 @@ -1030,7 +981,7 @@ class Select(object): timeout = timeout if timeout is not None else self.inner_ele.page.timeout return self._select(text_or_index, i, False, timeout) - def select_by_value(self, value: Union[str, list, tuple], timeout=None) -> bool: + def select_by_value(self, value, timeout=None): """此方法用于根据value值选择项。当元素是多选列表时,可以接收list或tuple \n :param value: value属性值,传入list或tuple可选择多项 :param timeout: 超时时间,不输入默认实用页面超时时间 @@ -1039,7 +990,7 @@ class Select(object): timeout = timeout if timeout is not None else self.inner_ele.page.timeout return self._select(value, 'value', False, timeout) - def deselect(self, text_or_index: Union[str, int, list, tuple], timeout=None) -> bool: + def deselect(self, text_or_index, timeout=None): """取消选定下拉列表中子元素 \n :param text_or_index: 根据文本或序号取消择选项,若允许多选,传入list或tuple可取消多项 :param timeout: 超时时间,不输入默认实用页面超时时间 @@ -1049,7 +1000,7 @@ class Select(object): timeout = timeout if timeout is not None else self.inner_ele.page.timeout return self._select(text_or_index, i, True, timeout) - def deselect_by_value(self, value: Union[str, list, tuple], timeout=None) -> bool: + def deselect_by_value(self, value, timeout=None): """此方法用于根据value值取消选择项。当元素是多选列表时,可以接收list或tuple \n :param value: value属性值,传入list或tuple可取消多项 :param timeout: 超时时间,不输入默认实用页面超时时间 @@ -1058,7 +1009,7 @@ class Select(object): timeout = timeout if timeout is not None else self.inner_ele.page.timeout return self._select(value, 'value', True, timeout) - def invert(self) -> None: + def invert(self): """反选""" if not self.is_multi: raise NotImplementedError("只能对多项选框执行反选。") @@ -1066,11 +1017,7 @@ class Select(object): for i in self.options: i.click(by_js=True) - def _select(self, - text_value_index: Union[str, int, list, tuple] = None, - para_type: str = 'text', - deselect: bool = False, - timeout=None) -> bool: + def _select(self, text_value_index, para_type='text', deselect=False, timeout=None): """选定或取消选定下拉列表中子元素 \n :param text_value_index: 根据文本、值选或序号择选项,若允许多选,传入list或tuple可多选 :param para_type: 参数类型,可选 'text'、'value'、'index' @@ -1119,10 +1066,7 @@ class Select(object): else: raise TypeError('只能传入str、int、list和tuple类型。') - def _select_multi(self, - text_value_index: Union[list, tuple] = None, - para_type: str = 'text', - deselect: bool = False) -> bool: + def _select_multi(self, text_value_index=None, para_type='text', deselect=False) -> bool: """选定或取消选定下拉列表中多个子元素 \n :param text_value_index: 根据文本、值选或序号择选多项 :param para_type: 参数类型,可选 'text'、'value'、'index' @@ -1150,10 +1094,7 @@ class Select(object): class ElementWaiter(object): """等待元素在dom中某种状态,如删除、显示、隐藏""" - def __init__(self, - page_or_ele, - loc_or_ele: Union[str, tuple, DriverElement, WebElement], - timeout: float = None): + def __init__(self, page_or_ele, loc_or_ele, timeout=None): """等待元素在dom中某种状态,如删除、显示、隐藏 \n :param page_or_ele: 页面或父元素 :param loc_or_ele: 要等待的元素,可以是已有元素、定位符 @@ -1183,19 +1124,19 @@ class ElementWaiter(object): self.timeout = timeout if timeout is not None else page.timeout - def delete(self) -> bool: + def delete(self): """等待元素从dom删除""" return self._wait_ele('del') - def display(self) -> bool: + def display(self): """等待元素从dom显示""" return self._wait_ele('display') - def hidden(self) -> bool: + def hidden(self): """等待元素从dom隐藏""" return self._wait_ele('hidden') - def _wait_ele(self, mode: str) -> bool: + def _wait_ele(self, mode): """执行等待 :param mode: 等待模式 :return: 是否等待成功 @@ -1248,27 +1189,27 @@ class Scroll(object): self.t1 = 'window' self.t2 = 'document.documentElement' - def to_top(self) -> None: + def to_top(self): """滚动到顶端,水平位置不变""" self.driver.run_script(f'{self.t1}.scrollTo({self.t2}.scrollLeft,0);') - def to_bottom(self) -> None: + def to_bottom(self): """滚动到底端,水平位置不变""" self.driver.run_script(f'{self.t1}.scrollTo({self.t2}.scrollLeft,{self.t2}.scrollHeight);') - def to_half(self) -> None: + def to_half(self): """滚动到垂直中间位置,水平位置不变""" self.driver.run_script(f'{self.t1}.scrollTo({self.t2}.scrollLeft,{self.t2}.scrollHeight/2);') - def to_rightmost(self) -> None: + def to_rightmost(self): """滚动到最右边,垂直位置不变""" self.driver.run_script(f'{self.t1}.scrollTo({self.t2}.scrollWidth,{self.t2}.scrollTop);') - def to_leftmost(self) -> None: + def to_leftmost(self): """滚动到最左边,垂直位置不变""" self.driver.run_script(f'{self.t1}.scrollTo(0,{self.t2}.scrollTop);') - def to_location(self, x: int, y: int) -> None: + def to_location(self, x, y): """滚动到指定位置 \n :param x: 水平距离 :param y: 垂直距离 @@ -1276,7 +1217,7 @@ class Scroll(object): """ self.driver.run_script(f'{self.t1}.scrollTo({x},{y});') - def up(self, pixel: int = 300) -> None: + def up(self, pixel=300): """向上滚动若干像素,水平位置不变 \n :param pixel: 滚动的像素 :return: None @@ -1284,14 +1225,14 @@ class Scroll(object): pixel = -pixel self.driver.run_script(f'{self.t1}.scrollBy(0,{pixel});') - def down(self, pixel: int = 300) -> None: + def down(self, pixel=300): """向下滚动若干像素,水平位置不变 \n :param pixel: 滚动的像素 :return: None """ self.driver.run_script(f'{self.t1}.scrollBy(0,{pixel});') - def left(self, pixel: int = 300) -> None: + def left(self, pixel=300): """向左滚动若干像素,垂直位置不变 \n :param pixel: 滚动的像素 :return: None @@ -1299,7 +1240,7 @@ class Scroll(object): pixel = -pixel self.driver.run_script(f'{self.t1}.scrollBy({pixel},0);') - def right(self, pixel: int = 300) -> None: + def right(self, pixel=300): """向右滚动若干像素,垂直位置不变 \n :param pixel: 滚动的像素 :return: None diff --git a/DrissionPage/driver_element.pyi b/DrissionPage/driver_element.pyi index 9b50856..350812b 100644 --- a/DrissionPage/driver_element.pyi +++ b/DrissionPage/driver_element.pyi @@ -15,281 +15,207 @@ from .session_element import SessionElement class DriverElement(DrissionElement): """driver模式的元素对象,包装了一个WebElement对象,并封装了常用功能""" - def __init__(self, ele: WebElement, page=...): + def __init__(self, ele: WebElement, page:Union[DriverPage, MixPage]=...): self._inner_ele: WebElement = ... self._select: Select = ... self._scroll: Scroll = ... self.page: Union[DriverPage, MixPage] = ... - def __repr__(self) -> str: - ... + def __repr__(self) -> str: ... def __call__(self, loc_or_str: Union[Tuple[str, str], str], - timeout: float = ...) -> Union['DriverElement', str, None]: - ... + timeout: float = ...) -> Union['DriverElement', str, None]: ... # -----------------共有属性和方法------------------- @property - def inner_ele(self) -> WebElement: - ... + def inner_ele(self) -> WebElement: ... @property - def tag(self) -> str: - ... + def tag(self) -> str: ... @property - def html(self) -> str: - ... + def html(self) -> str: ... @property - def inner_html(self) -> str: - ... + def inner_html(self) -> str: ... @property - def attrs(self) -> dict: - ... + def attrs(self) -> dict: ... @property - def text(self) -> str: - ... + def text(self) -> str: ... @property - def raw_text(self) -> str: - ... + def raw_text(self) -> str: ... - def attr(self, attr: str) -> str: - ... + def attr(self, attr: str) -> str: ... def ele(self, loc_or_str: Union[Tuple[str, str], str], - timeout: float = ...) -> Union['DriverElement', str, None]: - ... + timeout: float = ...) -> Union['DriverElement', str, None]: ... def eles(self, loc_or_str: Union[Tuple[str, str], str], - timeout: float = ...) -> List[Union['DriverElement', str]]: - ... + timeout: float = ...) -> List[Union['DriverElement', str]]: ... - def s_ele(self, loc_or_str: Union[Tuple[str, str], str] = ...) -> Union[SessionElement, str, None]: - ... + def s_ele(self, loc_or_str: Union[Tuple[str, str], str] = ...) -> Union[SessionElement, str, None]: ... - def s_eles(self, loc_or_str: Union[Tuple[str, str], str] = ...) -> List[Union[SessionElement, str]]: - ... + def s_eles(self, loc_or_str: Union[Tuple[str, str], str] = ...) -> List[Union[SessionElement, str]]: ... def _ele(self, loc_or_str: Union[Tuple[str, str], str], timeout: float = ..., single: bool = ..., - relative: bool = ...) -> Union['DriverElement', str, None, List[Union['DriverElement', str]]]: - ... + relative: bool = ...) -> Union['DriverElement', str, None, List[Union['DriverElement', str]]]: ... - def _get_ele_path(self, mode) -> str: - ... + def _get_ele_path(self, mode) -> str: ... # -----------------driver独有属性和方法------------------- @property - def size(self) -> dict: - ... + def size(self) -> dict: ... @property - def location(self) -> dict: - ... + def location(self) -> dict: ... @property - def shadow_root(self) -> ShadowRootElement: - ... + def shadow_root(self) -> ShadowRootElement: ... @property - def sr(self) -> ShadowRootElement: - ... + def sr(self) -> ShadowRootElement: ... @property - def pseudo_before(self) -> str: - ... + def pseudo_before(self) -> str: ... @property - def pseudo_after(self) -> str: - ... + def pseudo_after(self) -> str: ... @property - def select(self) -> Select: - ... + def select(self) -> Select: ... @property - def scroll(self) -> Scroll: - ... + def scroll(self) -> Scroll: ... - def parent(self, level_or_loc: Union[tuple, str, int] = ...) -> Union['DriverElement', None]: - ... + def parent(self, level_or_loc: Union[tuple, str, int] = ...) -> Union['DriverElement', None]: ... def prev(self, index: int = ..., filter_loc: Union[tuple, str] = ..., - timeout: float = ...) -> Union['DriverElement', str, None]: - ... + timeout: float = ...) -> Union['DriverElement', str, None]: ... def next(self, index: int = ..., filter_loc: Union[tuple, str] = ..., - timeout: float = ...) -> Union['DriverElement', str, None]: - ... + timeout: float = ...) -> Union['DriverElement', str, None]: ... def before(self, index: int = ..., filter_loc: Union[tuple, str] = ..., - timeout: float = ...) -> Union['DriverElement', str, None]: - ... + timeout: float = ...) -> Union['DriverElement', str, None]: ... def after(self, index: int = ..., filter_loc: Union[tuple, str] = ..., - timeout: float = ...) -> Union['DriverElement', str, None]: - ... + timeout: float = ...) -> Union['DriverElement', str, None]: ... def prevs(self, filter_loc: Union[tuple, str] = ..., - timeout: float = ...) -> List[Union['DriverElement', str]]: - ... + timeout: float = ...) -> List[Union['DriverElement', str]]: ... def nexts(self, filter_loc: Union[tuple, str] = ..., - timeout: float = ...) -> List[Union['DriverElement', str]]: - ... + timeout: float = ...) -> List[Union['DriverElement', str]]: ... def befores(self, filter_loc: Union[tuple, str] = ..., - timeout: float = ...) -> List[Union['DriverElement', str]]: - ... + timeout: float = ...) -> List[Union['DriverElement', str]]: ... def afters(self, filter_loc: Union[tuple, str] = ..., - timeout: float = ...) -> List[Union['DriverElement', str]]: - ... + timeout: float = ...) -> List[Union['DriverElement', str]]: ... - def left(self, index: int = ..., filter_loc: Union[tuple, str] = ...) -> DriverElement: - ... + def left(self, index: int = ..., filter_loc: Union[tuple, str] = ...) -> DriverElement: ... - def right(self, index: int = ..., filter_loc: Union[tuple, str] = ...) -> 'DriverElement': - ... + def right(self, index: int = ..., filter_loc: Union[tuple, str] = ...) -> 'DriverElement': ... - def above(self, index: int = ..., filter_loc: Union[tuple, str] = ...) -> 'DriverElement': - ... + def above(self, index: int = ..., filter_loc: Union[tuple, str] = ...) -> 'DriverElement': ... - def below(self, index: int = ..., filter_loc: Union[tuple, str] = ...) -> 'DriverElement': - ... + def below(self, index: int = ..., filter_loc: Union[tuple, str] = ...) -> 'DriverElement': ... - def near(self, index: int = ..., filter_loc: Union[tuple, str] = ...) -> 'DriverElement': - ... + def near(self, index: int = ..., filter_loc: Union[tuple, str] = ...) -> 'DriverElement': ... - def lefts(self, filter_loc: Union[tuple, str] = ...) -> List['DriverElement']: - ... + def lefts(self, filter_loc: Union[tuple, str] = ...) -> List['DriverElement']: ... - def rights(self, filter_loc: Union[tuple, str] = ...) -> List['DriverElement']: - ... + def rights(self, filter_loc: Union[tuple, str] = ...) -> List['DriverElement']: ... - def aboves(self, filter_loc: Union[tuple, str] = ...) -> List['DriverElement']: - ... + def aboves(self, filter_loc: Union[tuple, str] = ...) -> List['DriverElement']: ... - def belows(self, filter_loc: Union[tuple, str] = ...) -> List['DriverElement']: - ... + def belows(self, filter_loc: Union[tuple, str] = ...) -> List['DriverElement']: ... - def nears(self, filter_loc: Union[tuple, str] = ...) -> List['DriverElement']: - ... + def nears(self, filter_loc: Union[tuple, str] = ...) -> List['DriverElement']: ... def wait_ele(self, loc_or_ele: Union[str, tuple, DrissionElement, WebElement], - timeout: float = ...) -> 'ElementWaiter': - ... + timeout: float = ...) -> 'ElementWaiter': ... - def style(self, style: str, pseudo_ele: str = ...) -> str: - ... + def style(self, style: str, pseudo_ele: str = ...) -> str: ... - def click(self, by_js: bool = ..., timeout: float = ...) -> bool: - ... + def click(self, by_js: bool = ..., timeout: float = ...) -> bool: ... def click_at(self, x: Union[int, str] = ..., y: Union[int, str] = ..., - by_js: bool = ...) -> None: - ... + by_js: bool = ...) -> None: ... - def r_click(self) -> None: - ... + def r_click(self) -> None: ... - def r_click_at(self, x: Union[int, str] = ..., y: Union[int, str] = ...) -> None: - ... + def r_click_at(self, x: Union[int, str] = ..., y: Union[int, str] = ...) -> None: ... def input(self, vals: Union[str, tuple], clear: bool = ..., insure: bool = ..., - timeout: float = ...) -> bool: - ... + timeout: float = ...) -> bool: ... - def run_script(self, script: str, *args) -> Any: - ... + def run_script(self, script: str, *args) -> Any: ... - def submit(self) -> Union[bool, None]: - ... + def submit(self) -> Union[bool, None]: ... - def clear(self, insure: bool = ...) -> Union[None, bool]: - ... + def clear(self, insure: bool = ...) -> Union[None, bool]: ... - def is_selected(self) -> bool: - ... + def is_selected(self) -> bool: ... - def is_enabled(self) -> bool: - ... + def is_enabled(self) -> bool: ... - def is_displayed(self) -> bool: - ... + def is_displayed(self) -> bool: ... - def is_valid(self) -> bool: - ... + def is_valid(self) -> bool: ... - def screenshot(self, path: str = ..., filename: str = ..., as_bytes: bool = ...) -> Union[str, bytes]: - ... + def screenshot(self, path: str = ..., filename: str = ..., as_bytes: bool = ...) -> Union[str, bytes]: ... - def prop(self, prop: str) -> str: - ... + def prop(self, prop: str) -> str: ... - def set_prop(self, prop: str, value: str) -> bool: - ... + def set_prop(self, prop: str, value: str) -> bool: ... - def set_attr(self, attr: str, value: str) -> bool: - ... + def set_attr(self, attr: str, value: str) -> bool: ... - def remove_attr(self, attr: str) -> bool: - """删除元素attribute属性 \n - :param attr: 属性名 - :return: 是否删除成功 - """ - try: - self.run_script(f'arguments[0].removeAttribute("{attr}");') - return True - except Exception: - return False + def remove_attr(self, attr: str) -> bool: ... - def drag(self, x: int, y: int, speed: int = ..., shake: bool = ...) -> None: - ... + def drag(self, x: int, y: int, speed: int = ..., shake: bool = ...) -> None: ... def drag_to(self, ele_or_loc: Union[tuple, WebElement, DrissionElement], speed: int = ..., - shake: bool = ...) -> None: - ... + shake: bool = ...) -> None: ... - def hover(self, x: int = ..., y: int = ...) -> None: - ... + def hover(self, x: int = ..., y: int = ...) -> None: ... def _get_relative_eles(self, mode: str, - loc: Union[tuple, str] = ...) -> Union[List['DriverElement'], 'DriverElement']: - ... + loc: Union[tuple, str] = ...) -> Union[List['DriverElement'], 'DriverElement']: ... -def make_driver_ele(page_or_ele, +def make_driver_ele(page_or_ele: Union[DriverPage, MixPage,DriverElement, ShadowRootElement], loc: Union[str, Tuple[str, str]], single: bool = ..., timeout: float = ...) -> Union[DriverElement, str, None, List[Union[DriverElement, str]]]: ... @@ -314,7 +240,7 @@ class Select(object): self.select_ele: SeleniumSelect = ... self.inner_ele: DriverElement = ... - def __call__(self, text_or_index: Union[str, int, list, tuple], timeout=...) -> bool: ... + def __call__(self, text_or_index: Union[str, int, list, tuple], timeout: float = ...) -> bool: ... @property def is_multi(self) -> bool: ... @@ -330,13 +256,13 @@ class Select(object): def clear(self) -> None: ... - def select(self, text_or_index: Union[str, int, list, tuple], timeout=...) -> bool: ... + def select(self, text_or_index: Union[str, int, list, tuple], timeout: float = ...) -> bool: ... - def select_by_value(self, value: Union[str, list, tuple], timeout=...) -> bool: ... + def select_by_value(self, value: Union[str, list, tuple], timeout: float = ...) -> bool: ... - def deselect(self, text_or_index: Union[str, int, list, tuple], timeout=...) -> bool: ... + def deselect(self, text_or_index: Union[str, int, list, tuple], timeout: float = ...) -> bool: ... - def deselect_by_value(self, value: Union[str, list, tuple], timeout=...) -> bool: ... + def deselect_by_value(self, value: Union[str, list, tuple], timeout: float = ...) -> bool: ... def invert(self) -> None: ... diff --git a/DrissionPage/driver_page.py b/DrissionPage/driver_page.py index 2f93d13..787fced 100644 --- a/DrissionPage/driver_page.py +++ b/DrissionPage/driver_page.py @@ -8,32 +8,28 @@ from glob import glob from os import sep from pathlib import Path from time import sleep, perf_counter -from typing import Union, List, Any, Tuple from selenium.common.exceptions import NoAlertPresentException -from selenium.webdriver.chrome.webdriver import WebDriver -from selenium.webdriver.remote.webdriver import WebDriver as RemoteWebDriver from selenium.webdriver.remote.webelement import WebElement from selenium.webdriver.support.wait import WebDriverWait from .base import BasePage from .common import get_usable_path from .driver_element import DriverElement, make_driver_ele, Scroll, ElementWaiter -from .session_element import make_session_ele, SessionElement +from .session_element import make_session_ele class DriverPage(BasePage): """DriverPage封装了页面操作的常用功能,使用selenium来获取、解析、操作网页""" - def __init__(self, driver: RemoteWebDriver, timeout: float = 10) -> None: + def __init__(self, driver, timeout=10): """初始化函数,接收一个WebDriver对象,用来操作网页""" super().__init__(timeout) self._driver = driver self._wait_object = None self._scroll = None - def __call__(self, loc_or_str: Union[Tuple[str, str], str, DriverElement, WebElement], - timeout: float = None) -> Union[DriverElement, str, None]: + def __call__(self, loc_or_str, timeout=None): """在内部查找元素 \n 例:ele = page('@id=ele_id') \n :param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串 @@ -44,7 +40,7 @@ class DriverPage(BasePage): # -----------------共有属性和方法------------------- @property - def url(self) -> Union[str, None]: + def url(self): """返回当前网页url""" if not self._driver or not self.driver.current_url.startswith('http'): return None @@ -52,21 +48,17 @@ class DriverPage(BasePage): return self.driver.current_url @property - def html(self) -> str: + def html(self): """返回页面的html文本""" return self.driver.find_element('xpath', "//*").get_attribute("outerHTML") @property - def json(self) -> dict: + def json(self): """当返回内容是json格式时,返回对应的字典""" from json import loads return loads(self('t:pre').text) - def get(self, - url: str, - show_errmsg: bool = False, - retry: int = None, - interval: float = None) -> Union[None, bool]: + def get(self, url, show_errmsg=False, retry=None, interval=None): """访问url \n :param url: 目标url :param show_errmsg: 是否显示和抛出异常 @@ -84,9 +76,7 @@ class DriverPage(BasePage): return self._url_available - def ele(self, - loc_or_ele: Union[Tuple[str, str], str, DriverElement, WebElement], - timeout: float = None) -> Union[DriverElement, str, None]: + def ele(self, loc_or_ele, timeout=None): """返回页面中符合条件的第一个元素 \n :param loc_or_ele: 元素的定位信息,可以是元素对象,loc元组,或查询字符串 :param timeout: 查找元素超时时间,默认与页面等待时间一致 @@ -94,9 +84,7 @@ class DriverPage(BasePage): """ return self._ele(loc_or_ele, timeout) - def eles(self, - loc_or_str: Union[Tuple[str, str], str], - timeout: float = None) -> List[Union[DriverElement, str]]: + def eles(self, loc_or_str, timeout=None): """返回页面中所有符合条件的元素 \n :param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串 :param timeout: 查找元素超时时间,默认与页面等待时间一致 @@ -104,7 +92,7 @@ class DriverPage(BasePage): """ return self._ele(loc_or_str, timeout, single=False) - def s_ele(self, loc_or_ele: Union[Tuple[str, str], str, DriverElement] = None) -> Union[SessionElement, str, None]: + def s_ele(self, loc_or_ele=None): """查找第一个符合条件的元素以SessionElement形式返回,处理复杂页面时效率很高 \n :param loc_or_ele: 元素的定位信息,可以是loc元组,或查询字符串 :return: SessionElement对象或属性、文本 @@ -114,17 +102,14 @@ class DriverPage(BasePage): else: return make_session_ele(self, loc_or_ele) - def s_eles(self, loc_or_str: Union[Tuple[str, str], str] = None) -> List[Union[SessionElement, str]]: + def s_eles(self, loc_or_str=None): """查找所有符合条件的元素以SessionElement列表形式返回 \n :param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串 :return: SessionElement对象组成的列表 """ return make_session_ele(self, loc_or_str, single=False) - def _ele(self, - loc_or_ele: Union[Tuple[str, str], str, DriverElement, WebElement], - timeout: float = None, - single: bool = True) -> Union[DriverElement, str, None, List[Union[DriverElement, str]]]: + def _ele(self, loc_or_ele, timeout=None, single=True): """返回页面中符合条件的元素,默认返回第一个 \n :param loc_or_ele: 元素的定位信息,可以是元素对象,loc元组,或查询字符串 :param timeout: 查找元素超时时间 @@ -147,7 +132,7 @@ class DriverPage(BasePage): else: raise ValueError('loc_or_str参数只能是tuple、str、DriverElement 或 WebElement类型。') - def get_cookies(self, as_dict: bool = False) -> Union[list, dict]: + def get_cookies(self, as_dict=False): """返回当前网站cookies""" if as_dict: return {cookie['name']: cookie['value'] for cookie in self.driver.get_cookies()} @@ -155,21 +140,17 @@ class DriverPage(BasePage): return self.driver.get_cookies() @property - def timeout(self) -> float: + def timeout(self): """返回查找元素时等待的秒数""" return self._timeout @timeout.setter - def timeout(self, second: float) -> None: + def timeout(self, second): """设置查找元素时等待的秒数""" self._timeout = second self._wait_object = None - def _d_connect(self, - to_url: str, - times: int = 0, - interval: float = 1, - show_errmsg: bool = False) -> Union[bool, None]: + def _d_connect(self, to_url, times=0, interval=1, show_errmsg=False): """尝试连接,重试若干次 \n :param to_url: 要访问的url :param times: 重试次数 @@ -205,11 +186,11 @@ class DriverPage(BasePage): # ----------------driver独有属性和方法----------------------- @property - def driver(self) -> WebDriver: + def driver(self): return self._driver @property - def wait_object(self) -> WebDriverWait: + def wait_object(self): """返回WebDriverWait对象,重用避免每次新建对象""" if self._wait_object is None: self._wait_object = WebDriverWait(self.driver, timeout=self.timeout) @@ -217,14 +198,14 @@ class DriverPage(BasePage): return self._wait_object @property - def timeouts(self) -> dict: + def timeouts(self): """返回三种超时时间,selenium4以上版本可用""" return {'implicit': self.timeout, 'pageLoad': self.driver.timeouts.page_load, 'script': self.driver.timeouts.script} @property - def tabs_count(self) -> int: + def tabs_count(self): """返回标签页数量""" try: return len(self.driver.window_handles) @@ -232,34 +213,34 @@ class DriverPage(BasePage): return 0 @property - def tab_handles(self) -> list: + def tab_handles(self): """返回所有标签页handle列表""" return self.driver.window_handles @property - def current_tab_index(self) -> int: + def current_tab_index(self): """返回当前标签页序号""" return self.driver.window_handles.index(self.driver.current_window_handle) @property - def current_tab_handle(self) -> str: + def current_tab_handle(self): """返回当前标签页handle""" return self.driver.current_window_handle @property - def active_ele(self) -> DriverElement: + def active_ele(self): """返回当前焦点所在元素""" return DriverElement(self.driver.switch_to.active_element, self) @property - def scroll(self) -> Scroll: + def scroll(self): """用于滚动滚动条的对象""" if self._scroll is None: self._scroll = Scroll(self) return self._scroll @property - def to_frame(self) -> 'ToFrame': + def to_frame(self): """用于跳转到frame的对象,调用其方法实现跳转 \n 示例: \n page.to_frame.by_loc('tag:iframe') - 通过传入frame的查询字符串定位 \n @@ -273,7 +254,7 @@ class DriverPage(BasePage): """ return ToFrame(self) - def set_timeouts(self, implicit: float = None, pageLoad: float = None, script: float = None) -> None: + def set_timeouts(self, implicit=None, pageLoad=None, script=None): """设置超时时间,单位为秒,selenium4以上版本有效 \n :param implicit: 查找元素超时时间 :param pageLoad: 页面加载超时时间 @@ -289,9 +270,7 @@ class DriverPage(BasePage): if script is not None: self.driver.set_script_timeout(script) - def wait_ele(self, - loc_or_ele: Union[str, tuple, DriverElement, WebElement], - timeout: float = None) -> 'ElementWaiter': + def wait_ele(self, loc_or_ele, timeout=None): """等待元素从dom删除、显示、隐藏 \n :param loc_or_ele: 可以是元素、查询字符串、loc元组 :param timeout: 等待超时时间 @@ -299,13 +278,13 @@ class DriverPage(BasePage): """ return ElementWaiter(self, loc_or_ele, timeout) - def check_page(self) -> Union[bool, None]: + def check_page(self): """检查页面是否符合预期 \n 由子类自行实现各页面的判定规则 """ return None - def run_script(self, script: str, *args) -> Any: + def run_script(self, script, *args): """执行js代码 \n :param script: js文本 :param args: 传入的参数 @@ -313,7 +292,7 @@ class DriverPage(BasePage): """ return self.driver.execute_script(script, *args) - def run_async_script(self, script: str, *args) -> Any: + def run_async_script(self, script, *args): """以异步方式执行js代码 \n :param script: js文本 :param args: 传入的参数 @@ -321,7 +300,7 @@ class DriverPage(BasePage): """ return self.driver.execute_async_script(script, *args) - def run_cdp(self, cmd: str, **cmd_args) -> Any: + def run_cdp(self, cmd, **cmd_args): """执行Chrome DevTools Protocol语句 :param cmd: 协议项目 :param cmd_args: 参数 @@ -329,7 +308,7 @@ class DriverPage(BasePage): """ return self.driver.execute_cdp_cmd(cmd, cmd_args) - def create_tab(self, url: str = '') -> None: + def create_tab(self, url=''): """新建并定位到一个标签页,该标签页在最后面 \n :param url: 新标签页跳转到的网址 :return: None @@ -338,27 +317,27 @@ class DriverPage(BasePage): if url: self.get(url) - def close_tabs(self, num_or_handles: Union[int, str, list, tuple] = None) -> None: + def close_tabs(self, num_or_handles=None): """关闭传入的标签页,默认关闭当前页。可传入多个 \n 注意:当程序使用的是接管的浏览器,获取到的 handle 顺序和视觉效果不一致,不能按序号关闭。 \n :param num_or_handles:要关闭的标签页序号或handle,可传入handle和序号组成的列表或元组,为None时关闭当前页 :return: None """ - tabs = (self.current_tab_handle,) if num_or_handles is None else _get_handles(self.tab_handles, num_or_handles) + tabs = (self.current_tab_handle,) if num_or_handles is None else get_handles(self.tab_handles, num_or_handles) for i in tabs: self.driver.switch_to.window(i) self.driver.close() self.to_tab(0) - def close_other_tabs(self, num_or_handles: Union[int, str, list, tuple] = None) -> None: + def close_other_tabs(self, num_or_handles=None): """关闭传入的标签页以外标签页,默认保留当前页。可传入多个 \n 注意:当程序使用的是接管的浏览器,获取到的 handle 顺序和视觉效果不一致,不能按序号关闭。 \n :param num_or_handles: 要保留的标签页序号或handle,可传入handle和序号组成的列表或元组,为None时保存当前页 :return: None """ all_tabs = self.driver.window_handles - reserve_tabs = {self.current_tab_handle} if num_or_handles is None else _get_handles(all_tabs, num_or_handles) + reserve_tabs = {self.current_tab_handle} if num_or_handles is None else get_handles(all_tabs, num_or_handles) for i in set(all_tabs) - reserve_tabs: self.driver.switch_to.window(i) @@ -366,7 +345,7 @@ class DriverPage(BasePage): self.to_tab(0) - def to_tab(self, num_or_handle: Union[int, str] = 0) -> None: + def to_tab(self, num_or_handle=0): """跳转到标签页 \n 注意:当程序使用的是接管的浏览器,获取到的 handle 顺序和视觉效果不一致 \n :param num_or_handle: 标签页序号或handle字符串,序号第一个为0,最后为-1 @@ -380,14 +359,14 @@ class DriverPage(BasePage): tab = self.driver.window_handles[tab] if isinstance(tab, int) else tab self.driver.switch_to.window(tab) - def set_ua_to_tab(self, ua: str) -> None: + def set_ua_to_tab(self, ua): """为当前tab设置user agent,只在当前tab有效 \n :param ua: user agent字符串 :return: None """ self.driver.execute_cdp_cmd("Network.setUserAgentOverride", {"userAgent": ua}) - def get_session_storage(self, item: str = None) -> Union[str, dict, None]: + def get_session_storage(self, item=None): """获取sessionStorage信息,不设置item则获取全部 \n :param item: 要获取的项,不设置则返回全部 :return: sessionStorage一个或所有项内容 @@ -395,7 +374,7 @@ class DriverPage(BasePage): js = f'return sessionStorage.getItem("{item}");' if item else 'return sessionStorage;' return self.run_script(js) - def get_local_storage(self, item: str = None) -> Union[str, dict, None]: + def get_local_storage(self, item=None): """获取localStorage信息,不设置item则获取全部 \n :param item: 要获取的项目,不设置则返回全部 :return: localStorage一个或所有项内容 @@ -403,7 +382,7 @@ class DriverPage(BasePage): js = f'return localStorage.getItem("{item}");' if item else 'return localStorage;' return self.run_script(js) - def set_session_storage(self, item: str, value: Union[str, bool]) -> None: + def set_session_storage(self, item, value): """设置或删除某项sessionStorage信息 \n :param item: 要设置的项 :param value: 项的值,设置为False时,删除该项 @@ -412,7 +391,7 @@ class DriverPage(BasePage): s = f'sessionStorage.removeItem("{item}");' if item is False else f'sessionStorage.setItem("{item}","{value}");' self.run_script(s) - def set_local_storage(self, item: str, value: Union[str, bool]) -> None: + def set_local_storage(self, item, value): """设置或删除某项localStorage信息 \n :param item: 要设置的项 :param value: 项的值,设置为False时,删除该项 @@ -421,11 +400,7 @@ class DriverPage(BasePage): s = f'localStorage.removeItem("{item}");' if item is False else f'localStorage.setItem("{item}","{value}");' self.run_script(s) - def clean_cache(self, - session_storage: bool = True, - local_storage: bool = True, - cache: bool = True, - cookies: bool = True) -> None: + def clean_cache(self, session_storage=True, local_storage=True, cache=True, cookies=True): """清除缓存,可选要清除的项 \n :param session_storage: 是否清除sessionStorage :param local_storage: 是否清除localStorage @@ -442,7 +417,7 @@ class DriverPage(BasePage): if cookies: self.run_cdp('Network.clearBrowserCookies') - def screenshot(self, path: str = None, filename: str = None, as_bytes: bool = False) -> Union[str, bytes]: + def screenshot(self, path=None, filename=None, as_bytes=False): """截取页面可见范围截图 \n :param path: 保存路径 :param filename: 图片文件名,不传入时以页面title命名 @@ -461,7 +436,7 @@ class DriverPage(BasePage): self.driver.save_screenshot(img_path) return img_path - def scroll_to_see(self, loc_or_ele: Union[str, tuple, WebElement, DriverElement]) -> None: + def scroll_to_see(self, loc_or_ele): """滚动页面直到元素可见 \n :param loc_or_ele: 元素的定位信息,可以是loc元组,或查询字符串(详见ele函数注释) :return: None @@ -469,23 +444,23 @@ class DriverPage(BasePage): ele = self.ele(loc_or_ele) ele.run_script("arguments[0].scrollIntoView();") - def refresh(self) -> None: + def refresh(self): """刷新当前页面""" self.driver.refresh() - def stop_loading(self) -> None: + def stop_loading(self): """强制停止页面加载""" self.run_cdp('Page.stopLoading') - def back(self) -> None: + def back(self): """在浏览历史中后退一步""" self.driver.back() - def forward(self) -> None: + def forward(self): """在浏览历史中前进一步""" self.driver.forward() - def set_window_size(self, width: int = None, height: int = None) -> None: + def set_window_size(self, width=None, height=None): """设置浏览器窗口大小,默认最大化,任一参数为0最小化 \n :param width: 浏览器窗口高 :param height: 浏览器窗口宽 @@ -505,14 +480,14 @@ class DriverPage(BasePage): new_y = height or self.driver.get_window_size()['height'] self.driver.set_window_size(new_x, new_y) - def chrome_downloading(self, download_path: str) -> list: + def chrome_downloading(self, download_path): """返回浏览器下载中的文件列表 \n :param download_path: 下载文件夹路径 :return: 文件列表 """ return glob(f'{download_path}{sep}*.crdownload') - def process_alert(self, ok: bool = True, send: str = None, timeout: float = None) -> Union[str, None]: + def process_alert(self, ok=True, send=None, timeout=None): """处理提示框 \n :param ok: True表示确认,False表示取消,其它值不会按按钮但依然返回文本值 :param send: 处理prompt提示框时可输入文本 @@ -551,10 +526,10 @@ class DriverPage(BasePage): class ToFrame(object): """用于处理焦点跳转到页面框架的类""" - def __init__(self, page: DriverPage): + def __init__(self, page): self.page = page - def __call__(self, condition: Union[int, str, tuple, WebElement, DriverElement] = 'main'): + def __call__(self, condition='main'): """跳转到(i)frame,可传入id、name、序号、元素对象、定位符 \n :param condition: (i)frame,可传入id、name、序号、元素对象、定位符 :return: 当前页面对象 @@ -570,12 +545,12 @@ class ToFrame(object): return self.page - def main(self) -> DriverPage: + def main(self): """焦点跳转到最高层级框架""" self.page.driver.switch_to.default_content() return self.page - def parent(self, level: int = 1) -> DriverPage: + def parent(self, level=1): """焦点跳转到上级框架,可指定上级层数 \n :param level: 上面第几层框架 :return: 框架所在页面对象 @@ -586,7 +561,7 @@ class ToFrame(object): self.page.driver.switch_to.parent_frame() return self.page - def by_id(self, id_: str) -> DriverPage: + def by_id(self, id_): """焦点跳转到id为该值的(i)frame \n :param id_: (i)frame的id属性值 :return: 框架所在页面对象 @@ -594,7 +569,7 @@ class ToFrame(object): self.page.driver.switch_to.frame(id_) return self.page - def by_name(self, name: str) -> DriverPage: + def by_name(self, name): """焦点跳转到name为该值的(i)frame \n :param name: (i)frame的name属性值 :return: 框架所在页面对象 @@ -602,7 +577,7 @@ class ToFrame(object): self.page.driver.switch_to.frame(name) return self.page - def by_index(self, index: int) -> DriverPage: + def by_index(self, index): """焦点跳转到页面中第几个(i)frame \n :param index: 页面中第几个(i)frame :return: 框架所在页面对象 @@ -610,7 +585,7 @@ class ToFrame(object): self.page.driver.switch_to.frame(index) return self.page - def by_loc(self, loc: Union[str, tuple]) -> DriverPage: + def by_loc(self, loc): """焦点跳转到根据定位符获取到的(i)frame \n :param loc: 定位符,支持selenium原生和DriverPage定位符 :return: 框架所在页面对象 @@ -618,7 +593,7 @@ class ToFrame(object): self.page.driver.switch_to.frame(self.page(loc).inner_ele) return self.page - def by_ele(self, ele: Union[DriverElement, WebElement]) -> DriverPage: + def by_ele(self, ele): """焦点跳转到传入的(i)frame元素对象 \n :param ele: (i)frame元素对象 :return: 框架所在页面对象 @@ -629,7 +604,7 @@ class ToFrame(object): return self.page -def _get_handles(handles: list, num_or_handles: Union[int, str, list, tuple]) -> set: +def get_handles(handles, num_or_handles): """返回指定标签页组成的set :param handles: handles列表 :param num_or_handles: 指定的标签页,可以是多个 diff --git a/DrissionPage/driver_page.pyi b/DrissionPage/driver_page.pyi index 4af7248..2e00662 100644 --- a/DrissionPage/driver_page.pyi +++ b/DrissionPage/driver_page.pyi @@ -1,20 +1,15 @@ # -*- coding:utf-8 -*- -from glob import glob -from os import sep -from pathlib import Path -from time import sleep, perf_counter from typing import Union, List, Any, Tuple -from selenium.common.exceptions import NoAlertPresentException from selenium.webdriver.chrome.webdriver import WebDriver from selenium.webdriver.remote.webdriver import WebDriver as RemoteWebDriver from selenium.webdriver.remote.webelement import WebElement from selenium.webdriver.support.wait import WebDriverWait from .base import BasePage -from .common import get_usable_path -from .driver_element import DriverElement, make_driver_ele, Scroll, ElementWaiter -from .session_element import make_session_ele, SessionElement +from .driver_element import DriverElement, Scroll, ElementWaiter +from .mix_page import MixPage +from .session_element import SessionElement class DriverPage(BasePage): @@ -171,7 +166,7 @@ class ToFrame(object): def __init__(self, page: DriverPage): self.page: DriverPage = ... - def __call__(self, condition: Union[int, str, tuple, WebElement, DriverElement] = ...): ... + def __call__(self, condition: Union[int, str, tuple, WebElement, DriverElement] = ...)->Union[DriverPage, MixPage]: ... def main(self) -> DriverPage: ... @@ -188,4 +183,4 @@ class ToFrame(object): def by_ele(self, ele: Union[DriverElement, WebElement]) -> DriverPage: ... -def _get_handles(handles: list, num_or_handles: Union[int, str, list, tuple]) -> set: ... +def get_handles(handles: list, num_or_handles: Union[int, str, list, tuple]) -> set: ... diff --git a/DrissionPage/mix_page.py b/DrissionPage/mix_page.py index f266ecd..c190aba 100644 --- a/DrissionPage/mix_page.py +++ b/DrissionPage/mix_page.py @@ -4,21 +4,9 @@ @Contact : g1879@qq.com @File : mix_page.py """ -from typing import Union, List, Tuple - -from DownloadKit import DownloadKit -from requests import Response, Session -from requests.cookies import RequestsCookieJar -from selenium.webdriver.chrome.options import Options -from selenium.webdriver.chrome.webdriver import WebDriver -from selenium.webdriver.remote.webelement import WebElement - from .base import BasePage -from .config import DriverOptions, SessionOptions from .drission import Drission -from .driver_element import DriverElement from .driver_page import DriverPage -from .session_element import SessionElement from .session_page import SessionPage @@ -30,12 +18,7 @@ class MixPage(SessionPage, DriverPage, BasePage): 调用某种模式独有的功能,会自动切换到该模式。 """ - def __init__(self, - mode: str = 'd', - drission: Union[Drission, str] = None, - timeout: float = None, - driver_options: Union[Options, DriverOptions, bool] = None, - session_options: Union[dict, SessionOptions, bool] = None) -> None: + def __init__(self, mode='d', drission=None, timeout=None, driver_options=None, session_options=None): """初始化函数 \n :param mode: 'd' 或 's',即driver模式和session模式 :param drission: Drission对象,不传入时会自动创建,有传入时driver_options和session_options参数无效 @@ -63,9 +46,7 @@ class MixPage(SessionPage, DriverPage, BasePage): except Exception: self.timeout = timeout if timeout is not None else 10 - def __call__(self, - loc_or_str: Union[Tuple[str, str], str, DriverElement, SessionElement, WebElement], - timeout: float = None) -> Union[DriverElement, SessionElement, str, None]: + def __call__(self, loc_or_str, timeout=None): """在内部查找元素 \n 例:ele = page('@id=ele_id') \n :param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串 @@ -79,7 +60,7 @@ class MixPage(SessionPage, DriverPage, BasePage): # -----------------共有属性和方法------------------- @property - def url(self) -> Union[str, None]: + def url(self): """返回当前url""" if self._mode == 'd': return self._drission.driver.current_url if self._driver else None @@ -87,7 +68,7 @@ class MixPage(SessionPage, DriverPage, BasePage): return self._session_url @property - def title(self) -> str: + def title(self): """返回网页title""" if self._mode == 's': return super().title @@ -95,7 +76,7 @@ class MixPage(SessionPage, DriverPage, BasePage): return super(SessionPage, self).title @property - def html(self) -> str: + def html(self): """返回页面html文本""" if self._mode == 's': return super().html @@ -103,19 +84,14 @@ class MixPage(SessionPage, DriverPage, BasePage): return super(SessionPage, self).html @property - def json(self) -> dict: + def json(self): """当返回内容是json格式时,返回对应的字典""" if self._mode == 's': return super().json elif self._mode == 'd': return super(SessionPage, self).json - def get(self, - url: str, - show_errmsg: bool = False, - retry: int = None, - interval: float = None, - **kwargs) -> Union[bool, None]: + def get(self, url, show_errmsg=False, retry=None, interval=None, **kwargs): """跳转到一个url \n :param url: 目标url :param show_errmsg: 是否显示和抛出异常 @@ -129,9 +105,7 @@ class MixPage(SessionPage, DriverPage, BasePage): elif self._mode == 's': return super().get(url, show_errmsg, retry, interval, **kwargs) - def ele(self, - loc_or_ele: Union[Tuple[str, str], str, DriverElement, SessionElement, WebElement], - timeout: float = None) -> Union[DriverElement, SessionElement, str, None]: + def ele(self, loc_or_ele, timeout=None): """返回第一个符合条件的元素、属性或节点文本 \n :param loc_or_ele: 元素的定位信息,可以是元素对象,loc元组,或查询字符串 :param timeout: 查找元素超时时间,默认与页面等待时间一致 @@ -142,9 +116,7 @@ class MixPage(SessionPage, DriverPage, BasePage): elif self._mode == 'd': return super(SessionPage, self).ele(loc_or_ele, timeout=timeout) - def eles(self, - loc_or_str: Union[Tuple[str, str], str], - timeout: float = None) -> List[Union[DriverElement, SessionElement, str]]: + def eles(self, loc_or_str, timeout=None): """返回页面中所有符合条件的元素、属性或节点文本 \n :param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串 :param timeout: 查找元素超时时间,默认与页面等待时间一致 @@ -155,8 +127,7 @@ class MixPage(SessionPage, DriverPage, BasePage): elif self._mode == 'd': return super(SessionPage, self).eles(loc_or_str, timeout=timeout) - def s_ele(self, loc_or_ele: Union[Tuple[str, str], str, DriverElement, SessionElement] = None) \ - -> Union[SessionElement, str, None]: + def s_ele(self, loc_or_ele=None): """查找第一个符合条件的元素以SessionElement形式返回,d模式处理复杂页面时效率很高 \n :param loc_or_ele: 元素的定位信息,可以是loc元组,或查询字符串 :return: SessionElement对象或属性、文本 @@ -166,7 +137,7 @@ class MixPage(SessionPage, DriverPage, BasePage): elif self._mode == 'd': return super(SessionPage, self).s_ele(loc_or_ele) - def s_eles(self, loc_or_str: Union[Tuple[str, str], str] = None) -> List[Union[SessionElement, str]]: + def s_eles(self, loc_or_str=None): """查找所有符合条件的元素以SessionElement形式返回,d模式处理复杂页面时效率很高 \n :param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串 :return: SessionElement对象或属性、文本组成的列表 @@ -176,11 +147,7 @@ class MixPage(SessionPage, DriverPage, BasePage): elif self._mode == 'd': return super(SessionPage, self).s_eles(loc_or_str) - def _ele(self, - loc_or_ele: Union[Tuple[str, str], str, DriverElement, SessionElement, WebElement], - timeout: float = None, single: bool = True) \ - -> Union[DriverElement, SessionElement, str, None, List[Union[SessionElement, str]], List[ - Union[DriverElement, str]]]: + def _ele(self, loc_or_ele, timeout=None, single=True): """返回页面中符合条件的元素、属性或节点文本,默认返回第一个 \n :param loc_or_ele: 元素的定位信息,可以是元素对象,loc元组,或查询字符串 :param timeout: 查找元素超时时间,d模式专用 @@ -192,7 +159,7 @@ class MixPage(SessionPage, DriverPage, BasePage): elif self._mode == 'd': return super(SessionPage, self)._ele(loc_or_ele, timeout=timeout, single=single) - def get_cookies(self, as_dict: bool = False, all_domains: bool = False) -> Union[dict, list]: + def get_cookies(self, as_dict=False, all_domains=False): """返回cookies \n :param as_dict: 是否以字典方式返回 :param all_domains: 是否返回所有域的cookies @@ -205,12 +172,12 @@ class MixPage(SessionPage, DriverPage, BasePage): # ----------------MixPage独有属性和方法----------------------- @property - def drission(self) -> Drission: + def drission(self): """返回当前使用的 Dirssion 对象""" return self._drission @property - def driver(self) -> WebDriver: + def driver(self): """返回 driver 对象,如没有则创建 \n 每次访问时切换到 d 模式,用于独有函数及外部调用 :return: WebDriver对象 @@ -219,27 +186,27 @@ class MixPage(SessionPage, DriverPage, BasePage): return self._drission.driver @property - def session(self) -> Session: + def session(self): """返回 Session 对象,如没有则创建""" return self._drission.session @property - def response(self) -> Response: + def response(self): """返回 s 模式获取到的 Response 对象,切换到 s 模式""" self.change_mode('s') return self._response @property - def mode(self) -> str: + def mode(self): """返回当前模式,'s'或'd' """ return self._mode @property - def _session_url(self) -> str: + def _session_url(self): """返回 session 保存的url""" return self._response.url if self._response else None - def change_mode(self, mode: str = None, go: bool = True, copy_cookies: bool = True) -> None: + def change_mode(self, mode=None, go=True, copy_cookies=True): """切换模式,接收's'或'd',除此以外的字符串会切换为 d 模式 \n 切换时会把当前模式的cookies复制到目标模式 \n 切换后,如果go是True,调用相应的get函数使访问的页面同步 \n @@ -277,7 +244,7 @@ class MixPage(SessionPage, DriverPage, BasePage): if go and self._drission.driver.current_url.startswith('http'): self.get(self._drission.driver.current_url) - def set_cookies(self, cookies: Union[RequestsCookieJar, list, tuple, str, dict], refresh: bool = True) -> None: + def set_cookies(self, cookies, refresh=True): """设置cookies \n :param cookies: cookies信息,可为CookieJar, list, tuple, str, dict :param refresh: 设置cookies后是否刷新页面 @@ -290,13 +257,13 @@ class MixPage(SessionPage, DriverPage, BasePage): if refresh: self.refresh() - def cookies_to_session(self, copy_user_agent: bool = False) -> None: + def cookies_to_session(self, copy_user_agent=False): """从driver复制cookies到session \n :param copy_user_agent : 是否复制user agent信息 """ self._drission.cookies_to_session(copy_user_agent) - def cookies_to_driver(self, url: str = None) -> None: + def cookies_to_driver(self, url=None): """从session复制cookies到driver \n chrome需要指定域才能接收cookies \n :param url: 目标域 @@ -305,7 +272,7 @@ class MixPage(SessionPage, DriverPage, BasePage): url = url or self._session_url self._drission.cookies_to_driver(url) - def check_page(self, by_requests: bool = False) -> Union[bool, None]: + def check_page(self, by_requests=False): """d模式时检查网页是否符合预期 \n 默认由response状态检查,可重载实现针对性检查 \n :param by_requests: 是否用内置response检查 @@ -320,25 +287,19 @@ class MixPage(SessionPage, DriverPage, BasePage): r = self._make_response(self.url, retry=0)[0] return r.ok if r else False - def close_driver(self) -> None: + def close_driver(self): """关闭driver及浏览器""" self._driver = None self.drission.close_driver(True) - def close_session(self) -> None: + def close_session(self): """关闭session""" self._session = None self._response = None self.drission.close_session() # ----------------重写SessionPage的函数----------------------- - def post(self, - url: str, - data: Union[dict, str] = None, - show_errmsg: bool = False, - retry: int = None, - interval: float = None, - **kwargs) -> bool: + def post(self, url, data=None, show_errmsg=False, retry=None, interval=None, **kwargs): """用post方式跳转到url,会切换到s模式 \n :param url: 目标url :param data: post方式时提交的数据 @@ -352,13 +313,13 @@ class MixPage(SessionPage, DriverPage, BasePage): return super().post(url, data, show_errmsg, retry, interval, **kwargs) @property - def download(self) -> DownloadKit: + def download(self): """返回下载器对象""" if self.mode == 'd': self.cookies_to_session() return super().download - def chrome_downloading(self, path: str = None) -> list: + def chrome_downloading(self, path=None): """返回浏览器下载中的文件列表 \n :param path: 下载文件夹路径,默认读取配置信息 :return: 正在下载的文件列表 @@ -373,10 +334,10 @@ class MixPage(SessionPage, DriverPage, BasePage): return super().chrome_downloading(path) # ----------------MixPage独有函数----------------------- - def hide_browser(self) -> None: + def hide_browser(self): """隐藏浏览器窗口""" self.drission.hide_browser() - def show_browser(self) -> None: + def show_browser(self): """显示浏览器窗口""" self.drission.show_browser() diff --git a/DrissionPage/mix_page.pyi b/DrissionPage/mix_page.pyi index 2c7da7b..4e98842 100644 --- a/DrissionPage/mix_page.pyi +++ b/DrissionPage/mix_page.pyi @@ -1,5 +1,5 @@ # -*- coding:utf-8 -*- -from typing import Union, List, Tuple +from typing import Union, List, Tuple, Any from DownloadKit import DownloadKit from requests import Response, Session @@ -52,11 +52,24 @@ class MixPage(SessionPage, DriverPage, BasePage): def json(self) -> dict: ... def get(self, - url: str, - show_errmsg: bool = ..., - retry: int = ..., - interval: float = ..., - **kwargs) -> Union[bool, None]: ... + url: str, + show_errmsg: bool | None = ..., + retry: int | None = ..., + interval: float | None = ..., + timeout: float | None = ..., + params: dict | None = ..., + data: Union[dict, str, None] = ..., + json: Union[dict, str, None] = ..., + headers: dict | None = ..., + cookies: Any | None = ..., + files: Any | None = ..., + auth: Any | None = ..., + allow_redirects: bool = ..., + proxies: dict | None = ..., + hooks: Any | None = ..., + stream: Any | None = ..., + verify: Any | None = ..., + cert: Any | None = ...) -> Union[bool, None]: ... def ele(self, loc_or_ele: Union[Tuple[str, str], str, DriverElement, SessionElement, WebElement], @@ -115,11 +128,23 @@ class MixPage(SessionPage, DriverPage, BasePage): # ----------------重写SessionPage的函数----------------------- def post(self, url: str, - data: Union[dict, str] = ..., - show_errmsg: bool = ..., - retry: int = ..., - interval: float = ..., - **kwargs) -> bool: ... + show_errmsg: bool | None = ..., + retry: int | None = ..., + interval: float | None = ..., + timeout: float | None = ..., + params: dict | None = ..., + data: Union[dict, str, None] = ..., + json: Union[dict, str, None] = ..., + headers: dict | None = ..., + cookies: Any | None = ..., + files: Any | None = ..., + auth: Any | None = ..., + allow_redirects: bool = ..., + proxies: dict | None = ..., + hooks: Any | None = ..., + stream: Any | None = ..., + verify: Any | None = ..., + cert: Any | None = ...) -> bool: ... @property def download(self) -> DownloadKit: ... diff --git a/DrissionPage/session_element.py b/DrissionPage/session_element.py index 7412796..714f16e 100644 --- a/DrissionPage/session_element.py +++ b/DrissionPage/session_element.py @@ -5,11 +5,9 @@ @File : session_element.py """ from re import match, DOTALL -from typing import Union, List, Tuple from lxml.etree import tostring from lxml.html import HtmlElement, fromstring -# from lxml.etree import Element as HtmlElement, fromstring from .base import DrissionElement, BasePage, BaseElement from .common import get_ele_txt, get_loc, make_absolute_link @@ -18,7 +16,7 @@ from .common import get_ele_txt, get_loc, make_absolute_link class SessionElement(DrissionElement): """session模式的元素对象,包装了一个lxml的Element对象,并封装了常用功能""" - def __init__(self, ele: HtmlElement, page=None): + def __init__(self, ele, page=None): """初始化对象 \n :param ele: 被包装的HtmlElement元素 :param page: 元素所在页面对象,如果是从 html 文本生成的元素,则为 None @@ -27,16 +25,14 @@ class SessionElement(DrissionElement): self._inner_ele = ele @property - def inner_ele(self) -> HtmlElement: + def inner_ele(self): return self._inner_ele - def __repr__(self) -> str: + def __repr__(self): attrs = [f"{attr}='{self.attrs[attr]}'" for attr in self.attrs] return f'' - def __call__(self, - loc_or_str: Union[Tuple[str, str], str], - timeout=None) -> Union['SessionElement', str, None]: + def __call__(self, loc_or_str, timeout=None): """在内部查找元素 \n 例:ele2 = ele1('@id=ele_id') \n :param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串 @@ -46,48 +42,45 @@ class SessionElement(DrissionElement): return self.ele(loc_or_str) @property - def tag(self) -> str: + def tag(self): """返回元素类型""" return self._inner_ele.tag @property - def html(self) -> str: + def html(self): """返回outerHTML文本""" html = tostring(self._inner_ele, method="html").decode() return html[:html.rfind('>') + 1] # tostring()会把跟紧元素的文本节点也带上,因此要去掉 @property - def inner_html(self) -> str: + def inner_html(self): """返回元素innerHTML文本""" r = match(r'<.*?>(.*)', self.html, flags=DOTALL) return '' if not r else r.group(1) @property - def attrs(self) -> dict: + def attrs(self): """返回元素所有属性及值""" return {attr: self.attr(attr) for attr, val in self.inner_ele.items()} @property - def text(self) -> str: + def text(self): """返回元素内所有文本""" return get_ele_txt(self) @property - def raw_text(self) -> str: + def raw_text(self): """返回未格式化处理的元素内文本""" return str(self._inner_ele.text_content()) - def parent(self, level_or_loc: Union[tuple, str, int] = 1) -> Union['SessionElement', None]: + def parent(self, level_or_loc=1): """返回上面某一级父元素,可指定层数或用查询语法定位 \n :param level_or_loc: 第几级父元素,或定位符 :return: 上级元素对象 """ return super().parent(level_or_loc) - def prev(self, - index: int = 1, - filter_loc: Union[tuple, str] = '', - timeout: float = 0) -> Union['SessionElement', str, None]: + def prev(self, index=1, filter_loc='', timeout=0): """返回前面的一个兄弟元素,可用查询语法筛选,可指定返回筛选结果的第几个 \n :param index: 前面第几个查询结果元素 :param filter_loc: 用于筛选元素的查询语法 @@ -96,10 +89,7 @@ class SessionElement(DrissionElement): """ return super().prev(index, filter_loc, timeout) - def next(self, - index: int = 1, - filter_loc: Union[tuple, str] = '', - timeout: float = 0) -> Union['SessionElement', str, None]: + def next(self, index=1, filter_loc='', timeout=0): """返回后面的一个兄弟元素,可用查询语法筛选,可指定返回筛选结果的第几个 \n :param index: 后面第几个查询结果元素 :param filter_loc: 用于筛选元素的查询语法 @@ -108,10 +98,7 @@ class SessionElement(DrissionElement): """ return super().next(index, filter_loc, timeout) - def before(self, - index: int = 1, - filter_loc: Union[tuple, str] = '', - timeout: float = None) -> Union['SessionElement', str, None]: + def before(self, index=1, filter_loc='', timeout=None): """返回前面的一个兄弟元素,可用查询语法筛选,可指定返回筛选结果的第几个 \n :param index: 前面第几个查询结果元素 :param filter_loc: 用于筛选元素的查询语法 @@ -120,10 +107,7 @@ class SessionElement(DrissionElement): """ return super().before(index, filter_loc, timeout) - def after(self, - index: int = 1, - filter_loc: Union[tuple, str] = '', - timeout: float = None) -> Union['SessionElement', str, None]: + def after(self, index=1, filter_loc='', timeout=None): """返回后面的一个兄弟元素,可用查询语法筛选,可指定返回筛选结果的第几个 \n :param index: 后面第几个查询结果元素 :param filter_loc: 用于筛选元素的查询语法 @@ -132,9 +116,7 @@ class SessionElement(DrissionElement): """ return super().after(index, filter_loc, timeout) - def prevs(self, - filter_loc: Union[tuple, str] = '', - timeout: float = 0) -> List[Union['SessionElement', str]]: + def prevs(self, filter_loc='', timeout=0): """返回前面全部兄弟元素或节点组成的列表,可用查询语法筛选 \n :param filter_loc: 用于筛选元素的查询语法 :param timeout: 查找元素的超时时间 @@ -142,9 +124,7 @@ class SessionElement(DrissionElement): """ return super().prevs(filter_loc, timeout) - def nexts(self, - filter_loc: Union[tuple, str] = '', - timeout: float = 0) -> List[Union['SessionElement', str]]: + def nexts(self, filter_loc='', timeout=0): """返回后面全部兄弟元素或节点组成的列表,可用查询语法筛选 \n :param filter_loc: 用于筛选元素的查询语法 :param timeout: 查找元素的超时时间 @@ -152,9 +132,7 @@ class SessionElement(DrissionElement): """ return super().nexts(filter_loc, timeout) - def befores(self, - filter_loc: Union[tuple, str] = '', - timeout: float = None) -> List[Union['SessionElement', str]]: + def befores(self, filter_loc='', timeout=None): """返回后面全部兄弟元素或节点组成的列表,可用查询语法筛选 \n :param filter_loc: 用于筛选元素的查询语法 :param timeout: 查找元素的超时时间 @@ -162,9 +140,7 @@ class SessionElement(DrissionElement): """ return super().befores(filter_loc, timeout) - def afters(self, - filter_loc: Union[tuple, str] = '', - timeout: float = None) -> List[Union['SessionElement', str]]: + def afters(self, filter_loc='', timeout=None): """返回前面全部兄弟元素或节点组成的列表,可用查询语法筛选 \n :param filter_loc: 用于筛选元素的查询语法 :param timeout: 查找元素的超时时间 @@ -172,7 +148,7 @@ class SessionElement(DrissionElement): """ return super().afters(filter_loc, timeout) - def attr(self, attr: str) -> Union[str, None]: + def attr(self, attr): """返回attribute属性值 \n :param attr: 属性名 :return: 属性值文本,没有该属性返回None @@ -205,9 +181,7 @@ class SessionElement(DrissionElement): else: return self.inner_ele.get(attr) - def ele(self, - loc_or_str: Union[Tuple[str, str], str], - timeout=None) -> Union['SessionElement', str, None]: + def ele(self, loc_or_str, timeout=None): """返回当前元素下级符合条件的第一个元素、属性或节点文本 \n :param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串 :param timeout: 不起实际作用,用于和DriverElement对应,便于无差别调用 @@ -215,9 +189,7 @@ class SessionElement(DrissionElement): """ return self._ele(loc_or_str) - def eles(self, - loc_or_str: Union[Tuple[str, str], str], - timeout=None) -> List[Union['SessionElement', str]]: + def eles(self, loc_or_str, timeout=None): """返回当前元素下级所有符合条件的子元素、属性或节点文本 \n :param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串 :param timeout: 不起实际作用,用于和DriverElement对应,便于无差别调用 @@ -225,27 +197,21 @@ class SessionElement(DrissionElement): """ return self._ele(loc_or_str, single=False) - def s_ele(self, - loc_or_str: Union[Tuple[str, str], str] = None) -> Union['SessionElement', str, None]: + def s_ele(self, loc_or_str=None): """返回当前元素下级符合条件的第一个元素、属性或节点文本 \n :param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串 :return: SessionElement对象或属性、文本 """ return self._ele(loc_or_str) - def s_eles(self, - loc_or_str: Union[Tuple[str, str], str] = None) -> List[Union['SessionElement', str]]: + def s_eles(self, loc_or_str=None): """返回当前元素下级所有符合条件的子元素、属性或节点文本 \n :param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串 :return: SessionElement对象或属性、文本组成的列表 """ return self._ele(loc_or_str, single=False) - def _ele(self, - loc_or_str: Union[Tuple[str, str], str], - timeout=None, - single: bool = True, - relative: bool = False) -> Union['SessionElement', str, None, List[Union['SessionElement', str]]]: + def _ele(self, loc_or_str, timeout=None, single=True, relative=False): """返回当前元素下级符合条件的子元素、属性或节点文本,默认返回第一个 \n :param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串 :param timeout: 不起实际作用,用于和父类对应 @@ -255,7 +221,7 @@ class SessionElement(DrissionElement): """ return make_session_ele(self, loc_or_str, single) - def _get_ele_path(self, mode) -> str: + def _get_ele_path(self, mode): """获取css路径或xpath路径 :param mode: 'css' 或 'xpath' :return: css路径或xpath路径 @@ -276,9 +242,7 @@ class SessionElement(DrissionElement): return f':root{path_str[1:]}' if mode == 'css' else path_str -def make_session_ele(html_or_ele: Union[str, BaseElement, BasePage], - loc: Union[str, Tuple[str, str]] = None, - single: bool = True) -> Union[SessionElement, str, None, List[Union[SessionElement, str]]]: +def make_session_ele(html_or_ele, loc=None, single=True): """从接收到的对象或html文本中查找元素,返回SessionElement对象 \n 如要直接从html生成SessionElement而不在下级查找,loc输入None即可 \n :param html_or_ele: html文本、BaseParser对象 diff --git a/DrissionPage/session_element.pyi b/DrissionPage/session_element.pyi index 3c523fd..885af19 100644 --- a/DrissionPage/session_element.pyi +++ b/DrissionPage/session_element.pyi @@ -10,7 +10,7 @@ from .session_page import SessionPage class SessionElement(DrissionElement): """session模式的元素对象,包装了一个lxml的Element对象,并封装了常用功能""" - def __init__(self, ele: HtmlElement, page=...): + def __init__(self, ele: HtmlElement, page: Union[SessionPage, None] = ...): self._inner_ele: HtmlElement = ... self.page: SessionPage = ... @@ -21,7 +21,7 @@ class SessionElement(DrissionElement): def __call__(self, loc_or_str: Union[Tuple[str, str], str], - timeout=...) -> Union['SessionElement', str, None]: ... + timeout: float = ...) -> Union['SessionElement', str, None]: ... @property def tag(self) -> str: ... diff --git a/DrissionPage/session_page.py b/DrissionPage/session_page.py index 494b2a3..d8313b3 100644 --- a/DrissionPage/session_page.py +++ b/DrissionPage/session_page.py @@ -1,37 +1,35 @@ # -*- coding:utf-8 -*- from re import search from time import sleep -from typing import Union, List, Tuple from urllib.parse import urlparse +from DownloadKit import DownloadKit from requests import Session, Response from requests.structures import CaseInsensitiveDict from tldextract import extract -from DownloadKit import DownloadKit from .base import BasePage -from .config import _cookie_to_dict, SessionOptions, _cookies_to_tuple +from .config import SessionOptions, cookies_to_tuple, cookie_to_dict from .session_element import SessionElement, make_session_ele class SessionPage(BasePage): """SessionPage封装了页面操作的常用功能,使用requests来获取、解析网页""" - def __init__(self, session_or_options: Union[Session, SessionOptions] = None, - timeout: float = 10): + def __init__(self, session_or_options=None, timeout=10): """初始化函数""" super().__init__(timeout) self._response = None self._create_session(session_or_options) - def _create_session(self, Session_or_Options) -> None: + def _create_session(self, Session_or_Options): if Session_or_Options is None or isinstance(Session_or_Options, SessionOptions): options = Session_or_Options or SessionOptions() self._set_session(options.as_dict()) elif isinstance(Session_or_Options, Session): self._session = Session_or_Options - def _set_session(self, data) -> None: + def _set_session(self, data): """根据传入字典对session进行设置 \n :param data: session配置字典 :return: None @@ -50,7 +48,7 @@ class SessionPage(BasePage): self._session.__setattr__(i, data[i]) def set_cookies(self, cookies): - cookies = _cookies_to_tuple(cookies) + cookies = cookies_to_tuple(cookies) for cookie in cookies: if cookie['value'] is None: cookie['value'] = '' @@ -64,9 +62,7 @@ class SessionPage(BasePage): self.session.cookies.set(cookie['name'], cookie['value'], **kwargs) - def __call__(self, - loc_or_str: Union[Tuple[str, str], str, SessionElement], - timeout=None) -> Union[SessionElement, str, None]: + def __call__(self, loc_or_str, timeout=None): """在内部查找元素 \n 例:ele2 = ele1('@id=ele_id') \n :param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串 @@ -77,17 +73,17 @@ class SessionPage(BasePage): # -----------------共有属性和方法------------------- @property - def url(self) -> str: + def url(self): """返回当前访问url""" return self._url @property - def html(self) -> str: + def html(self): """返回页面的html文本""" return self.response.text if self.response else '' @property - def json(self) -> dict: + def json(self): """当返回内容是json格式时,返回对应的字典""" return self.response.json() @@ -103,9 +99,7 @@ class SessionPage(BasePage): """ return self._s_connect(url, 'get', None, show_errmsg, retry, interval, **kwargs) - def ele(self, - loc_or_ele: Union[Tuple[str, str], str, SessionElement], - timeout: float = None) -> Union[SessionElement, str, None]: + def ele(self, loc_or_ele, timeout=None): """返回页面中符合条件的第一个元素、属性或节点文本 \n :param loc_or_ele: 元素的定位信息,可以是元素对象,loc元组,或查询字符串 :param timeout: 不起实际作用,用于和DriverElement对应,便于无差别调用 @@ -113,9 +107,7 @@ class SessionPage(BasePage): """ return self._ele(loc_or_ele) - def eles(self, - loc_or_str: Union[Tuple[str, str], str], - timeout: float = None) -> List[Union[SessionElement, str]]: + def eles(self, loc_or_str, timeout=None): """返回页面中所有符合条件的元素、属性或节点文本 \n :param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串 :param timeout: 不起实际作用,用于和DriverElement对应,便于无差别调用 @@ -123,24 +115,21 @@ class SessionPage(BasePage): """ return self._ele(loc_or_str, single=False) - def s_ele(self, loc_or_ele: Union[Tuple[str, str], str, SessionElement] = None) -> Union[SessionElement, str, None]: + def s_ele(self, loc_or_ele=None): """返回页面中符合条件的第一个元素、属性或节点文本 \n :param loc_or_ele: 元素的定位信息,可以是元素对象,loc元组,或查询字符串 :return: SessionElement对象或属性、文本 """ return make_session_ele(self.html) if loc_or_ele is None else self._ele(loc_or_ele) - def s_eles(self, loc_or_str: Union[Tuple[str, str], str] = None) -> List[Union[SessionElement, str]]: + def s_eles(self, loc_or_str=None): """返回页面中符合条件的所有元素、属性或节点文本 \n :param loc_or_str: 元素的定位信息,可以是元素对象,loc元组,或查询字符串 :return: SessionElement对象或属性、文本 """ return self._ele(loc_or_str, single=False) - def _ele(self, - loc_or_ele: Union[Tuple[str, str], str, SessionElement], - timeout: float = None, - single: bool = True) -> Union[SessionElement, str, None, List[Union[SessionElement, str]]]: + def _ele(self, loc_or_ele, timeout=None, single=True): """返回页面中符合条件的元素、属性或节点文本,默认返回第一个 \n :param loc_or_ele: 元素的定位信息,可以是元素对象,loc元组,或查询字符串 :param timeout: 不起实际作用,用于和父类对应 @@ -149,7 +138,7 @@ class SessionPage(BasePage): """ return loc_or_ele if isinstance(loc_or_ele, SessionElement) else make_session_ele(self, loc_or_ele, single) - def get_cookies(self, as_dict: bool = False, all_domains: bool = False) -> Union[dict, list]: + def get_cookies(self, as_dict=False, all_domains=False): """返回cookies \n :param as_dict: 是否以字典方式返回 :param all_domains: 是否返回所有域的cookies @@ -168,34 +157,28 @@ class SessionPage(BasePage): if as_dict: return {x.name: x.value for x in cookies} else: - return [_cookie_to_dict(cookie) for cookie in cookies] + return [cookie_to_dict(cookie) for cookie in cookies] # ----------------session独有属性和方法----------------------- @property - def session(self) -> Session: + def session(self): """返回session对象""" return self._session @property - def response(self) -> Response: + def response(self): """返回访问url得到的response对象""" return self._response @property - def download(self) -> DownloadKit: + def download(self): """返回下载器对象""" if not hasattr(self, '_download_kit'): self._download_kit = DownloadKit(session=self) return self._download_kit - def post(self, - url: str, - data: Union[dict, str] = None, - show_errmsg: bool = False, - retry: int = None, - interval: float = None, - **kwargs) -> bool: + def post(self, url, data=None, show_errmsg=False, retry=None, interval=None, **kwargs): """用post方式跳转到url \n :param url: 目标url :param data: 提交的数据 @@ -207,14 +190,7 @@ class SessionPage(BasePage): """ return self._s_connect(url, 'post', data, show_errmsg, retry, interval, **kwargs) - def _s_connect(self, - url: str, - mode: str, - data: Union[dict, str] = None, - show_errmsg: bool = False, - retry: int = None, - interval: float = None, - **kwargs) -> bool: + def _s_connect(self, url, mode, data=None, show_errmsg=False, retry=None, interval=None, **kwargs): """执行get或post连接 \n :param url: 目标url :param mode: 'get' 或 'post' @@ -242,14 +218,7 @@ class SessionPage(BasePage): return self._url_available - def _make_response(self, - url: str, - mode: str = 'get', - data: Union[dict, str] = None, - retry: int = None, - interval: float = None, - show_errmsg: bool = False, - **kwargs) -> tuple: + def _make_response(self, url, mode='get', data=None, retry=None, interval=None, show_errmsg=False, **kwargs): """生成Response对象 \n :param url: 目标url :param mode: 'get' 或 'post' @@ -268,12 +237,12 @@ class SessionPage(BasePage): parsed_url = urlparse(url) hostname = parsed_url.hostname scheme = parsed_url.scheme - if not _check_headers(kwargs, self.session.headers, 'Referer'): + if not check_headers(kwargs, self.session.headers, 'Referer'): kwargs['headers']['Referer'] = self.url if self.url else f'{scheme}://{hostname}' if 'Host' not in kwargs['headers']: kwargs['headers']['Host'] = hostname - if not _check_headers(kwargs, self.session.headers, 'timeout'): + if not check_headers(kwargs, self.session.headers, 'timeout'): kwargs['timeout'] = self.timeout if 'allow_redirects' not in kwargs: @@ -290,7 +259,7 @@ class SessionPage(BasePage): r = self.session.post(url, data=data, **kwargs) if r: - return _set_charset(r), 'Success' + return set_charset(r), 'Success' except Exception as e: err = e @@ -317,12 +286,12 @@ class SessionPage(BasePage): return r, f'状态码:{r.status_code}' -def _check_headers(kwargs, headers: Union[dict, CaseInsensitiveDict], arg: str) -> bool: +def check_headers(kwargs, headers, arg) -> bool: """检查kwargs或headers中是否有arg所示属性""" return arg in kwargs['headers'] or arg in headers -def _set_charset(response) -> Response: +def set_charset(response) -> Response: """设置Response对象的编码""" # 在headers中获取编码 content_type = response.headers.get('content-type', '').lower() diff --git a/DrissionPage/session_page.pyi b/DrissionPage/session_page.pyi index d8028c4..d072f00 100644 --- a/DrissionPage/session_page.pyi +++ b/DrissionPage/session_page.pyi @@ -3,6 +3,7 @@ from typing import Any, Union, Tuple, List from DownloadKit import DownloadKit from requests import Session, Response from requests.cookies import RequestsCookieJar +from requests.structures import CaseInsensitiveDict from .base import BasePage from .session_element import SessionElement @@ -22,8 +23,7 @@ class SessionPage(BasePage): self.retry_times: int = ... self.retry_interval: float = ... - def _create_session(self, - Session_or_Options: Union[Session, SessionOptions]) -> None: ... + def _create_session(self, Session_or_Options: Union[Session, SessionOptions]) -> None: ... def _set_session(self, data: dict) -> None: ... @@ -31,7 +31,7 @@ class SessionPage(BasePage): def __call__(self, loc_or_str: Union[Tuple[str, str], str, SessionElement], - timeout=...) -> Union[SessionElement, str, None]: ... + timeout: float = ...) -> Union[SessionElement, str, None]: ... # -----------------共有属性和方法------------------- @@ -114,7 +114,7 @@ class SessionPage(BasePage): hooks: Any | None = ..., stream: Any | None = ..., verify: Any | None = ..., - cert: Any | None = ..., ) -> bool: ... + cert: Any | None = ...) -> bool: ... def _s_connect(self, url: str, @@ -133,3 +133,10 @@ class SessionPage(BasePage): interval: float = ..., show_errmsg: bool = ..., **kwargs) -> tuple: ... + + +def check_headers(kwargs: Union[dict, CaseInsensitiveDict], headers: Union[dict, CaseInsensitiveDict], + arg: str) -> bool: ... + + +def set_charset(response: Response) -> Response: ... diff --git a/DrissionPage/shadow_root_element.py b/DrissionPage/shadow_root_element.py index 1301449..0d5b650 100644 --- a/DrissionPage/shadow_root_element.py +++ b/DrissionPage/shadow_root_element.py @@ -5,34 +5,32 @@ @File : shadow_root_element.py """ from time import perf_counter -from typing import Union, Any, Tuple, List +from typing import Union from selenium.webdriver.remote.webelement import WebElement from .base import BaseElement from .common import get_loc -from .driver_element import make_driver_ele, DriverElement +from .driver_element import make_driver_ele from .session_element import make_session_ele, SessionElement class ShadowRootElement(BaseElement): """ShadowRootElement是用于处理ShadowRoot的类,使用方法和DriverElement基本一致""" - def __init__(self, inner_ele: WebElement, parent_ele: DriverElement): + def __init__(self, inner_ele, parent_ele): super().__init__(parent_ele.page) self.parent_ele = parent_ele self._inner_ele = inner_ele @property - def inner_ele(self) -> WebElement: + def inner_ele(self): return self._inner_ele - def __repr__(self) -> str: + def __repr__(self): return f'' - def __call__(self, - loc_or_str: Union[Tuple[str, str], str], - timeout: float = None) -> Union[DriverElement, str, None]: + def __call__(self, loc_or_str, timeout=None): """在内部查找元素 \n 例:ele2 = ele1('@id=ele_id') \n :param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串 @@ -42,21 +40,21 @@ class ShadowRootElement(BaseElement): return self.ele(loc_or_str, timeout) @property - def tag(self) -> str: + def tag(self): """元素标签名""" return 'shadow-root' @property - def html(self) -> str: + def html(self): return f'{self.inner_html}' @property - def inner_html(self) -> str: + def inner_html(self): """返回内部的html文本""" shadow_root = WebElement(self.page.driver, self.inner_ele._id) return shadow_root.get_attribute('innerHTML') - def parent(self, level_or_loc: Union[str, int] = 1) -> DriverElement: + def parent(self, level_or_loc=1): """返回上面某一级父元素,可指定层数或用查询语法定位 \n :param level_or_loc: 第几级父元素,或定位符 :return: DriverElement对象 @@ -77,9 +75,7 @@ class ShadowRootElement(BaseElement): return self.parent_ele.ele(loc, timeout=0) - def next(self, - index: int = 1, - filter_loc: Union[tuple, str] = '') -> Union[DriverElement, str, None]: + def next(self, index=1, filter_loc=''): """返回后面的一个兄弟元素,可用查询语法筛选,可指定返回筛选结果的第几个 \n :param index: 第几个查询结果元素 :param filter_loc: 用于筛选元素的查询语法 @@ -88,9 +84,7 @@ class ShadowRootElement(BaseElement): nodes = self.nexts(filter_loc=filter_loc) return nodes[index - 1] if nodes else None - def before(self, - index: int = 1, - filter_loc: Union[tuple, str] = '') -> Union[DriverElement, str, None]: + def before(self, index=1, filter_loc=''): """返回前面的一个兄弟元素,可用查询语法筛选,可指定返回筛选结果的第几个 \n :param index: 前面第几个查询结果元素 :param filter_loc: 用于筛选元素的查询语法 @@ -99,8 +93,7 @@ class ShadowRootElement(BaseElement): nodes = self.befores(filter_loc=filter_loc) return nodes[index - 1] if nodes else None - def after(self, index: int = 1, - filter_loc: Union[tuple, str] = '') -> Union[DriverElement, str, None]: + def after(self, index=1, filter_loc=''): """返回后面的一个兄弟元素,可用查询语法筛选,可指定返回筛选结果的第几个 \n :param index: 后面第几个查询结果元素 :param filter_loc: 用于筛选元素的查询语法 @@ -109,7 +102,7 @@ class ShadowRootElement(BaseElement): nodes = self.afters(filter_loc=filter_loc) return nodes[index - 1] if nodes else None - def nexts(self, filter_loc: Union[tuple, str] = '') -> List[Union[DriverElement, str]]: + def nexts(self, filter_loc=''): """返回后面所有兄弟元素或节点组成的列表 \n :param filter_loc: 用于筛选元素的查询语法 :return: DriverElement对象组成的列表 @@ -122,7 +115,7 @@ class ShadowRootElement(BaseElement): xpath = f'xpath:./{loc}' return self.parent_ele.eles(xpath, timeout=0.1) - def befores(self, filter_loc: Union[tuple, str] = '') -> List[Union[DriverElement, str]]: + def befores(self, filter_loc=''): """返回后面全部兄弟元素或节点组成的列表,可用查询语法筛选 \n :param filter_loc: 用于筛选元素的查询语法 :return: 本元素前面的元素或节点组成的列表 @@ -135,7 +128,7 @@ class ShadowRootElement(BaseElement): xpath = f'xpath:./preceding::{loc}' return self.parent_ele.eles(xpath, timeout=0.1) - def afters(self, filter_loc: Union[tuple, str] = '') -> List[Union[DriverElement, str]]: + def afters(self, filter_loc=''): """返回前面全部兄弟元素或节点组成的列表,可用查询语法筛选 \n :param filter_loc: 用于筛选元素的查询语法 :return: 本元素后面的元素或节点组成的列表 @@ -145,9 +138,7 @@ class ShadowRootElement(BaseElement): xpath = f'xpath:./following::{loc}' return eles1 + self.parent_ele.eles(xpath, timeout=0.1) - def ele(self, - loc_or_str: Union[Tuple[str, str], str], - timeout: float = None) -> Union[DriverElement, str, None]: + def ele(self, loc_or_str, timeout=None): """返回当前元素下级符合条件的第一个元素,默认返回 \n :param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串 :param timeout: 查找元素超时时间,默认与元素所在页面等待时间一致 @@ -155,9 +146,7 @@ class ShadowRootElement(BaseElement): """ return self._ele(loc_or_str, timeout) - def eles(self, - loc_or_str: Union[Tuple[str, str], str], - timeout: float = None) -> List[Union[DriverElement, str]]: + def eles(self, loc_or_str, timeout=None): """返回当前元素下级所有符合条件的子元素 \n :param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串 :param timeout: 查找元素超时时间,默认与元素所在页面等待时间一致 @@ -165,25 +154,21 @@ class ShadowRootElement(BaseElement): """ return self._ele(loc_or_str, timeout=timeout, single=False) - def s_ele(self, loc_or_ele=None) -> Union[SessionElement, str, None]: + def s_ele(self, loc_or_str=None) -> Union[SessionElement, str, None]: """查找第一个符合条件的元素以SessionElement形式返回,处理复杂页面时效率很高 \n - :param loc_or_ele: 元素的定位信息,可以是loc元组,或查询字符串 + :param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串 :return: SessionElement对象或属性、文本 """ - return make_session_ele(self, loc_or_ele) + return make_session_ele(self, loc_or_str) - def s_eles(self, loc_or_ele) -> List[Union[SessionElement, str]]: + def s_eles(self, loc_or_str): """查找所有符合条件的元素以SessionElement列表形式返回,处理复杂页面时效率很高 \n - :param loc_or_ele: 元素的定位信息,可以是loc元组,或查询字符串 + :param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串 :return: SessionElement对象或属性、文本 """ - return make_session_ele(self, loc_or_ele, single=False) + return make_session_ele(self, loc_or_str, single=False) - def _ele(self, - loc_or_str: Union[Tuple[str, str], str], - timeout: float = None, - single: bool = True, - relative: bool = False) -> Union[DriverElement, str, None, List[Union[DriverElement, str]]]: + def _ele(self, loc_or_str, timeout=None, single=True, relative=False): """返回当前元素下级符合条件的子元素,默认返回第一个 \n :param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串 :param timeout: 查找元素超时时间 @@ -212,7 +197,7 @@ class ShadowRootElement(BaseElement): else: return [make_driver_ele(self, f'css:{css}', True, timeout) for css in css_paths] - def run_script(self, script: str, *args) -> Any: + def run_script(self, script, *args): """执行js代码,传入自己为第一个参数 \n :param script: js文本 :param args: 传入的参数 @@ -221,11 +206,11 @@ class ShadowRootElement(BaseElement): shadow_root = WebElement(self.page.driver, self.inner_ele._id) return shadow_root.parent.execute_script(script, shadow_root, *args) - def is_enabled(self) -> bool: + def is_enabled(self): """是否可用""" return self.inner_ele.is_enabled() - def is_valid(self) -> bool: + def is_valid(self): """用于判断元素是否还能用,应对页面跳转元素不能用的情况""" try: self.is_enabled() diff --git a/DrissionPage/shadow_root_element.pyi b/DrissionPage/shadow_root_element.pyi index 06ea68c..158668c 100644 --- a/DrissionPage/shadow_root_element.pyi +++ b/DrissionPage/shadow_root_element.pyi @@ -63,9 +63,9 @@ class ShadowRootElement(BaseElement): loc_or_str: Union[Tuple[str, str], str], timeout: float = ...) -> List[Union[DriverElement, str]]: ... - def s_ele(self, loc_or_ele=...) -> Union[SessionElement, str, None]: ... + def s_ele(self, loc_or_str: Union[Tuple[str, str], str]=...) -> Union[SessionElement, str, None]: ... - def s_eles(self, loc_or_ele) -> List[Union[SessionElement, str]]: ... + def s_eles(self, loc_or_str: Union[Tuple[str, str], str]) -> List[Union[SessionElement, str]]: ... def _ele(self, loc_or_str: Union[Tuple[str, str], str], diff --git a/DrissionPage/web_page.py b/DrissionPage/web_page.py index 304f829..6cf8d8a 100644 --- a/DrissionPage/web_page.py +++ b/DrissionPage/web_page.py @@ -1,17 +1,13 @@ # -*- coding:utf-8 -*- from time import sleep -from typing import Union, Tuple, List -from DownloadKit import DownloadKit -from requests import Session, Response +from requests import Session from tldextract import extract -from .chromium_base import ChromiumBase, ChromiumFrame from .base import BasePage -from .chromium_element import ChromiumElement # , ChromiumBase +from .chromium_base import ChromiumBase from .chromium_page import ChromiumPage -from .config import DriverOptions, SessionOptions, _cookies_to_tuple -from .session_element import SessionElement +from .config import DriverOptions, SessionOptions, cookies_to_tuple from .session_page import SessionPage from .tab import Tab @@ -19,12 +15,7 @@ from .tab import Tab class WebPage(SessionPage, ChromiumPage, BasePage): """整合浏览器和request的页面类""" - def __init__(self, - mode: str = 'd', - timeout: float = 10, - tab_id: str = None, - driver_or_options: Union[Tab, DriverOptions, bool] = None, - session_or_options: Union[Session, SessionOptions, bool] = None) -> None: + def __init__(self, mode='d', timeout=10, tab_id=None, driver_or_options=None, session_or_options=None): """初始化函数 \n :param mode: 'd' 或 's',即driver模式和session模式 :param timeout: 超时时间,d模式时为寻找元素时间,s模式时为连接时间,默认10秒 @@ -49,9 +40,7 @@ class WebPage(SessionPage, ChromiumPage, BasePage): if self._mode == 'd': self._driver - def __call__(self, - loc_or_str: Union[Tuple[str, str], str, ChromiumElement, SessionElement], - timeout: float = None) -> Union[ChromiumElement, SessionElement, ChromiumFrame, None]: + def __call__(self, loc_or_str, timeout=None): """在内部查找元素 \n 例:ele = page('@id=ele_id') \n :param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串 @@ -65,7 +54,7 @@ class WebPage(SessionPage, ChromiumPage, BasePage): # -----------------共有属性和方法------------------- @property - def url(self) -> Union[str, None]: + def url(self): """返回当前url""" if self._mode == 'd': return super(SessionPage, self).url if self._tab_obj else None @@ -73,7 +62,7 @@ class WebPage(SessionPage, ChromiumPage, BasePage): return self._session_url @property - def html(self) -> str: + def html(self): """返回页面html文本""" if self._mode == 's': return super().html @@ -81,7 +70,7 @@ class WebPage(SessionPage, ChromiumPage, BasePage): return super(SessionPage, self).html if self._has_driver else '' @property - def json(self) -> dict: + def json(self): """当返回内容是json格式时,返回对应的字典""" if self._mode == 's': return super().json @@ -89,13 +78,13 @@ class WebPage(SessionPage, ChromiumPage, BasePage): return super(SessionPage, self).json @property - def response(self) -> Response: + def response(self): """返回 s 模式获取到的 Response 对象,切换到 s 模式""" self.change_mode('s') return self._response @property - def mode(self) -> str: + def mode(self): """返回当前模式,'s'或'd' """ return self._mode @@ -107,7 +96,7 @@ class WebPage(SessionPage, ChromiumPage, BasePage): return super(SessionPage, self).get_cookies() @property - def session(self) -> Session: + def session(self): """返回Session对象,如未初始化则按配置信息创建""" if self._session is None: self._set_session(self._session_options) @@ -118,12 +107,12 @@ class WebPage(SessionPage, ChromiumPage, BasePage): return self._session @property - def driver(self) -> Tab: + def driver(self): """返回纯粹的Tab对象""" return self._tab_obj @property - def _wait_driver(self) -> Tab: + def _wait_driver(self): """返回用于控制浏览器的Tab对象,会先等待页面加载完毕""" while self._is_loading: sleep(.1) @@ -131,7 +120,7 @@ class WebPage(SessionPage, ChromiumPage, BasePage): return self._driver @property - def _driver(self) -> Tab: + def _driver(self): """返回纯粹的Tab对象,调用时切换到d模式,并连接浏览器""" self.change_mode('d') if self._tab_obj is None: @@ -143,17 +132,11 @@ class WebPage(SessionPage, ChromiumPage, BasePage): self._tab_obj = tab @property - def _session_url(self) -> str: + def _session_url(self): """返回 session 保存的url""" return self._response.url if self._response else None - def get(self, - url: str, - show_errmsg: bool = False, - retry: int = None, - interval: float = None, - timeout: float = None, - **kwargs) -> Union[bool, None]: + def get(self, url, show_errmsg=False, retry=None, interval=None, timeout=None, **kwargs): """跳转到一个url \n :param url: 目标url :param show_errmsg: 是否显示和抛出异常 @@ -168,9 +151,7 @@ class WebPage(SessionPage, ChromiumPage, BasePage): elif self._mode == 's': return super().get(url, show_errmsg, retry, interval, timeout, **kwargs) - def ele(self, - loc_or_ele: Union[Tuple[str, str], str, ChromiumElement, SessionElement], - timeout: float = None) -> Union[ChromiumElement, SessionElement, ChromiumFrame, str, None]: + def ele(self, loc_or_ele, timeout=None): """返回第一个符合条件的元素、属性或节点文本 \n :param loc_or_ele: 元素的定位信息,可以是元素对象,loc元组,或查询字符串 :param timeout: 查找元素超时时间,默认与页面等待时间一致 @@ -181,9 +162,7 @@ class WebPage(SessionPage, ChromiumPage, BasePage): elif self._mode == 'd': return super(SessionPage, self).ele(loc_or_ele, timeout=timeout) - def eles(self, - loc_or_str: Union[Tuple[str, str], str], - timeout: float = None) -> List[Union[ChromiumElement, SessionElement, ChromiumFrame, str]]: + def eles(self, loc_or_str, timeout=None): """返回页面中所有符合条件的元素、属性或节点文本 \n :param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串 :param timeout: 查找元素超时时间,默认与页面等待时间一致 @@ -194,8 +173,7 @@ class WebPage(SessionPage, ChromiumPage, BasePage): elif self._mode == 'd': return super(SessionPage, self).eles(loc_or_str, timeout=timeout) - def s_ele(self, loc_or_ele: Union[Tuple[str, str], str, ChromiumElement, SessionElement] = None) \ - -> Union[SessionElement, str, None]: + def s_ele(self, loc_or_ele=None): """查找第一个符合条件的元素以SessionElement形式返回,d模式处理复杂页面时效率很高 \n :param loc_or_ele: 元素的定位信息,可以是loc元组,或查询字符串 :return: SessionElement对象或属性、文本 @@ -205,7 +183,7 @@ class WebPage(SessionPage, ChromiumPage, BasePage): elif self._mode == 'd': return super(SessionPage, self).s_ele(loc_or_ele) - def s_eles(self, loc_or_str: Union[Tuple[str, str], str] = None) -> List[Union[SessionElement, str]]: + def s_eles(self, loc_or_str=None): """查找所有符合条件的元素以SessionElement形式返回,d模式处理复杂页面时效率很高 \n :param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串 :return: SessionElement对象或属性、文本组成的列表 @@ -215,7 +193,7 @@ class WebPage(SessionPage, ChromiumPage, BasePage): elif self._mode == 'd': return super(SessionPage, self).s_eles(loc_or_str) - def change_mode(self, mode: str = None, go: bool = True, copy_cookies: bool = True) -> None: + def change_mode(self, mode=None, go=True, copy_cookies=True): """切换模式,接收's'或'd',除此以外的字符串会切换为 d 模式 \n 切换时会把当前模式的cookies复制到目标模式 \n 切换后,如果go是True,调用相应的get函数使访问的页面同步 \n @@ -258,7 +236,7 @@ class WebPage(SessionPage, ChromiumPage, BasePage): if url.startswith('http'): self.get(url) - def cookies_to_session(self, copy_user_agent: bool = True) -> None: + def cookies_to_session(self, copy_user_agent=True): """把driver对象的cookies复制到session对象 \n :param copy_user_agent: 是否复制ua信息 :return: None @@ -269,7 +247,7 @@ class WebPage(SessionPage, ChromiumPage, BasePage): self.set_cookies(self._get_driver_cookies(as_dict=True), set_session=True) - def cookies_to_driver(self) -> None: + def cookies_to_driver(self): """把session对象的cookies复制到driver对象""" ex_url = extract(self._session_url) domain = f'{ex_url.domain}.{ex_url.suffix}' @@ -282,7 +260,7 @@ class WebPage(SessionPage, ChromiumPage, BasePage): cookies.append(cookie) self.set_cookies(cookies, set_driver=True) - def get_cookies(self, as_dict: bool = False, all_domains: bool = False) -> Union[dict, list]: + def get_cookies(self, as_dict=False, all_domains=False): """返回cookies \n :param as_dict: 是否以字典方式返回 :param all_domains: 是否返回所有域的cookies @@ -293,18 +271,17 @@ class WebPage(SessionPage, ChromiumPage, BasePage): elif self._mode == 'd': return self._get_driver_cookies(as_dict) - def _get_driver_cookies(self, as_dict: bool = False): + def _get_driver_cookies(self, as_dict=False): cookies = self._tab_obj.Network.getCookies()['cookies'] - # cookies = super(WebPage, self)._wait_driver.Network.getCookies()['cookies'] if as_dict: return {cookie['name']: cookie['value'] for cookie in cookies} else: return cookies - def set_cookies(self, cookies, set_session: bool = False, set_driver: bool = False): + def set_cookies(self, cookies, set_session=False, set_driver=False): # 添加cookie到driver if set_driver: - cookies = _cookies_to_tuple(cookies) + cookies = cookies_to_tuple(cookies) result_cookies = [] for cookie in cookies: if not cookie.get('domain', None): @@ -330,7 +307,7 @@ class WebPage(SessionPage, ChromiumPage, BasePage): # elif self._mode == 'd': # super(SessionPage, self).set_headers(headers) - def check_page(self, by_requests: bool = False) -> Union[bool, None]: + def check_page(self, by_requests=False): """d模式时检查网页是否符合预期 \n 默认由response状态检查,可重载实现针对性检查 \n :param by_requests: 是否用内置response检查 @@ -345,7 +322,7 @@ class WebPage(SessionPage, ChromiumPage, BasePage): r = self._make_response(self.url, retry=0)[0] return r.ok if r else False - def close_driver(self) -> None: + def close_driver(self): """关闭driver及浏览器""" if self._has_driver: self.change_mode('s') @@ -355,7 +332,7 @@ class WebPage(SessionPage, ChromiumPage, BasePage): pass self._has_driver = None - def close_session(self) -> None: + def close_session(self): """关闭session""" if self._has_session: self.change_mode('d') @@ -365,13 +342,7 @@ class WebPage(SessionPage, ChromiumPage, BasePage): self._has_session = None # ----------------重写SessionPage的函数----------------------- - def post(self, - url: str, - data: Union[dict, str] = None, - show_errmsg: bool = False, - retry: int = None, - interval: float = None, - **kwargs) -> bool: + def post(self, url: str, data=None, show_errmsg=False, retry=None, interval=None, **kwargs): """用post方式跳转到url,会切换到s模式 \n :param url: 目标url :param data: post方式时提交的数据 @@ -385,17 +356,13 @@ class WebPage(SessionPage, ChromiumPage, BasePage): return super().post(url, data, show_errmsg, retry, interval, **kwargs) @property - def download(self) -> DownloadKit: + def download(self): """返回下载器对象""" if self.mode == 'd': self.cookies_to_session() return super().download - def _ele(self, - loc_or_ele: Union[Tuple[str, str], str, ChromiumElement, SessionElement], - timeout: float = None, single: bool = True, relative: bool = False) \ - -> Union[ChromiumElement, SessionElement, ChromiumFrame, str, None, List[Union[SessionElement, str]], List[ - Union[ChromiumElement, str, ChromiumFrame]]]: + def _ele(self, loc_or_ele, timeout=None, single=True, relative=False): """返回页面中符合条件的元素、属性或节点文本,默认返回第一个 \n :param loc_or_ele: 元素的定位信息,可以是元素对象,loc元组,或查询字符串 :param timeout: 查找元素超时时间,d模式专用 @@ -446,7 +413,7 @@ class WebPage(SessionPage, ChromiumPage, BasePage): else: raise TypeError('session_or_options参数只能接收Session, dict, SessionOptions或False。') - def quit(self) -> None: + def quit(self): """关闭浏览器,关闭session""" if self._has_session: self._session.close() diff --git a/DrissionPage/web_page.pyi b/DrissionPage/web_page.pyi index fe2aa98..075a74e 100644 --- a/DrissionPage/web_page.pyi +++ b/DrissionPage/web_page.pyi @@ -51,7 +51,7 @@ class WebPage(SessionPage, ChromiumPage, BasePage): def mode(self) -> str: ... @property - def cookies(self): ... + def cookies(self)->Union[dict, list]: ... @property def session(self) -> Session: ... @@ -65,8 +65,8 @@ class WebPage(SessionPage, ChromiumPage, BasePage): @property def _driver(self) -> Tab: ... - # @_driver.setter - # def _driver(self, tab): ... + @_driver.setter + def _driver(self, tab:Tab): ... @property def _session_url(self) -> str: ... @@ -112,9 +112,9 @@ class WebPage(SessionPage, ChromiumPage, BasePage): def get_cookies(self, as_dict: bool = ..., all_domains: bool = ...) -> Union[dict, list]: ... - def _get_driver_cookies(self, as_dict: bool = ...): ... + def _get_driver_cookies(self, as_dict: bool = ...)->dict: ... - def set_cookies(self, cookies, set_session: bool = ..., set_driver: bool = ...): ... + def set_cookies(self, cookies, set_session: bool = ..., set_driver: bool = ...) -> None: ... def check_page(self, by_requests: bool = ...) -> Union[bool, None]: ... @@ -152,8 +152,8 @@ class WebPage(SessionPage, ChromiumPage, BasePage): -> Union[ChromiumElement, SessionElement, ChromiumFrame, str, None, List[Union[SessionElement, str]], List[ Union[ChromiumElement, str, ChromiumFrame]]]: ... - def _set_driver_options(self, Tab_or_Options): ... + def _set_driver_options(self, Tab_or_Options:Union[Tab, DriverOptions]) -> None: ... - def _set_session_options(self, Session_or_Options): ... + def _set_session_options(self, Session_or_Options:Union[Session, SessionOptions]) -> None: ... def quit(self) -> None: ...