diff --git a/DrissionPage/__init__.py b/DrissionPage/__init__.py index e3d2e75..f97d719 100644 --- a/DrissionPage/__init__.py +++ b/DrissionPage/__init__.py @@ -14,4 +14,4 @@ from ._pages.chromium_page import ChromiumPage from ._pages.mix_page import MixPage from ._pages.mix_page import MixPage as WebPage -__version__ = '4.1.0.0b4' +__version__ = '4.1.0.0b7' diff --git a/DrissionPage/_base/base.py b/DrissionPage/_base/base.py index 2dd52e4..8fa9a7c 100644 --- a/DrissionPage/_base/base.py +++ b/DrissionPage/_base/base.py @@ -13,6 +13,7 @@ from urllib.parse import quote from DownloadKit import DownloadKit from .._elements.none_element import NoneElement +from .._functions.elements import get_frame from .._functions.locator import get_loc from .._functions.settings import Settings from .._functions.web import format_html @@ -70,6 +71,16 @@ class BaseElement(BaseParser): def nexts(self): pass + def get_frame(self, loc_or_ind, timeout=None): + """获取元素中一个frame对象 + :param loc_or_ind: 定位符、iframe序号,序号从1开始,可传入负数获取倒数第几个 + :param timeout: 查找元素超时时间(秒) + :return: ChromiumFrame对象 + """ + if not isinstance(loc_or_ind, (int, str, tuple)): + raise TypeError('loc_or_ind参数是定位符或序号。') + return get_frame(self, loc_ind_ele=loc_or_ind, timeout=timeout) + def _ele(self, locator, timeout=None, index=1, relative=False, raise_err=None, method=None): """调用获取元素的方法 :param locator: 定位符 diff --git a/DrissionPage/_elements/chromium_element.py b/DrissionPage/_elements/chromium_element.py index 08f22fb..5409f46 100644 --- a/DrissionPage/_elements/chromium_element.py +++ b/DrissionPage/_elements/chromium_element.py @@ -823,7 +823,7 @@ class ChromiumElement(DrissionElement): self._run_js('this.focus();') def hover(self, offset_x=None, offset_y=None): - """鼠标悬停,可接受偏移量,偏移量相对于元素左上角坐标。不传入x或y值时悬停在元素中点 + """鼠标悬停,可接受偏移量,偏移量相对于元素左上角坐标。不传入offset_x和offset_y值时悬停在元素中点 :param offset_x: 相对元素左上角坐标的x轴偏移量 :param offset_y: 相对元素左上角坐标的y轴偏移量 :return: None diff --git a/DrissionPage/_functions/elements.py b/DrissionPage/_functions/elements.py index d77d359..cccb8da 100644 --- a/DrissionPage/_functions/elements.py +++ b/DrissionPage/_functions/elements.py @@ -7,6 +7,7 @@ """ from time import perf_counter +from .locator import is_loc from .._elements.none_element import NoneElement @@ -402,6 +403,50 @@ def get_eles(locators, owner, any_one=False, first_ele=True, timeout=10): return res +def get_frame(owner, loc_ind_ele, timeout=None): + """获取页面中一个frame对象 + :param owner: 要在其中查找元素的对象 + :param loc_ind_ele: 定位符、iframe序号、ChromiumFrame对象,序号从1开始,可传入负数获取倒数第几个 + :param timeout: 查找元素超时时间(秒) + :return: ChromiumFrame对象 + """ + if isinstance(loc_ind_ele, str): + if not is_loc(loc_ind_ele): + xpath = f'xpath://*[(name()="iframe" or name()="frame") and ' \ + f'(@name="{loc_ind_ele}" or @id="{loc_ind_ele}")]' + else: + xpath = loc_ind_ele + ele = owner._ele(xpath, timeout=timeout) + if ele and ele._type != 'ChromiumFrame': + raise TypeError('该定位符不是指向frame元素。') + r = ele + + elif isinstance(loc_ind_ele, tuple): + ele = owner._ele(loc_ind_ele, timeout=timeout) + if ele and ele._type != 'ChromiumFrame': + raise TypeError('该定位符不是指向frame元素。') + r = ele + + elif isinstance(loc_ind_ele, int): + if loc_ind_ele == 0: + loc_ind_ele = 1 + elif loc_ind_ele < 0: + loc_ind_ele = f'last()+{loc_ind_ele}+1' + xpath = f'xpath:(//*[name()="frame" or name()="iframe"])[{loc_ind_ele}]' + r = owner._ele(xpath, timeout=timeout) + + elif loc_ind_ele._type == 'ChromiumFrame': + r = loc_ind_ele + + else: + raise TypeError('必须传入定位符、iframe序号、id、name、ChromiumFrame对象其中之一。') + + if isinstance(r, NoneElement): + r.method = 'get_frame()' + r.args = {'loc_ind_ele': loc_ind_ele} + return r + + def _get_attr_all(src_list, aim_list, name, value, method, equal=True): if equal: for i in src_list: diff --git a/DrissionPage/_functions/elements.pyi b/DrissionPage/_functions/elements.pyi index 41562e5..69f08a6 100644 --- a/DrissionPage/_functions/elements.pyi +++ b/DrissionPage/_functions/elements.pyi @@ -10,6 +10,7 @@ from typing import Union, List, Optional, Iterable from .._base.base import BaseParser from .._elements.chromium_element import ChromiumElement from .._elements.session_element import SessionElement +from .._pages.chromium_frame import ChromiumFrame def get_eles(locators: Union[List[str], tuple], @@ -19,6 +20,11 @@ def get_eles(locators: Union[List[str], tuple], timeout: float = 10) -> dict: ... +def get_frame(owner: BaseParser, + loc_ind_ele: Union[str, int, tuple, ChromiumFrame, ChromiumElement], + timeout: float = None) -> ChromiumFrame: ... + + class SessionElementsList(list): _page = ... diff --git a/DrissionPage/_functions/tools.py b/DrissionPage/_functions/tools.py index e9a4be5..29ffb9e 100644 --- a/DrissionPage/_functions/tools.py +++ b/DrissionPage/_functions/tools.py @@ -19,7 +19,7 @@ from ..errors import (ContextLostError, ElementLostError, CDPError, PageDisconne class PortFinder(object): used_port = set() - prev_time = None + prev_time = 0 lock = Lock() checked_paths = set() @@ -43,9 +43,8 @@ class PortFinder(object): """ from random import randint with PortFinder.lock: - if PortFinder.prev_time and perf_counter() - PortFinder.prev_time > 30: + if PortFinder.prev_time and perf_counter() - PortFinder.prev_time > 60: PortFinder.used_port.clear() - PortFinder.prev_time = perf_counter() if scope in (True, None): scope = (9600, 59600) max_times = scope[1] - scope[0] @@ -61,6 +60,8 @@ class PortFinder(object): rmtree(path) except: continue + PortFinder.used_port.add(port) + PortFinder.prev_time = perf_counter() return port, str(path) raise OSError('未找到可用端口。') diff --git a/DrissionPage/_pages/chromium_base.py b/DrissionPage/_pages/chromium_base.py index 008d35a..229ff0f 100644 --- a/DrissionPage/_pages/chromium_base.py +++ b/DrissionPage/_pages/chromium_base.py @@ -19,8 +19,8 @@ from .._elements.chromium_element import run_js, make_chromium_eles from .._elements.none_element import NoneElement from .._elements.session_element import make_session_ele from .._functions.cookies import CookiesList -from .._functions.elements import SessionElementsList -from .._functions.locator import get_loc, is_loc +from .._functions.elements import SessionElementsList, get_frame +from .._functions.locator import get_loc from .._functions.settings import Settings from .._functions.tools import raise_error from .._functions.web import location_in_viewport @@ -770,41 +770,7 @@ class ChromiumBase(BasePage): :param timeout: 查找元素超时时间(秒) :return: ChromiumFrame对象 """ - if isinstance(loc_ind_ele, str): - if not is_loc(loc_ind_ele): - xpath = f'xpath://*[(name()="iframe" or name()="frame") and ' \ - f'(@name="{loc_ind_ele}" or @id="{loc_ind_ele}")]' - else: - xpath = loc_ind_ele - ele = self._ele(xpath, timeout=timeout) - if ele and ele._type != 'ChromiumFrame': - raise TypeError('该定位符不是指向frame元素。') - r = ele - - elif isinstance(loc_ind_ele, tuple): - ele = self._ele(loc_ind_ele, timeout=timeout) - if ele and ele._type != 'ChromiumFrame': - raise TypeError('该定位符不是指向frame元素。') - r = ele - - elif isinstance(loc_ind_ele, int): - if loc_ind_ele == 0: - loc_ind_ele = 1 - elif loc_ind_ele < 0: - loc_ind_ele = f'last()+{loc_ind_ele}+1' - xpath = f'xpath:(//*[name()="frame" or name()="iframe"])[{loc_ind_ele}]' - r = self._ele(xpath, timeout=timeout) - - elif loc_ind_ele._type == 'ChromiumFrame': - r = loc_ind_ele - - else: - raise TypeError('必须传入定位符、iframe序号、id、name、ChromiumFrame对象其中之一。') - - if isinstance(r, NoneElement): - r.method = 'get_frame()' - r.args = {'loc_ind_ele': loc_ind_ele} - return r + return get_frame(self, loc_ind_ele=loc_ind_ele, timeout=timeout) def get_frames(self, locator=None, timeout=None): """获取所有符合条件的frame对象 diff --git a/DrissionPage/_units/actions.py b/DrissionPage/_units/actions.py index e920529..c92aa90 100644 --- a/DrissionPage/_units/actions.py +++ b/DrissionPage/_units/actions.py @@ -25,7 +25,7 @@ class Actions: self.curr_y = 0 self._holding = 'left' - def move_to(self, ele_or_loc, offset_x=0, offset_y=0, duration=.5): + def move_to(self, ele_or_loc, offset_x=None, offset_y=None, duration=.5): """鼠标移动到元素中点,或页面上的某个绝对坐标。可设置偏移量 当带偏移量时,偏移量相对于元素左上角坐标 :param ele_or_loc: 元素对象、绝对坐标或文本定位符,坐标为tuple(int, int)形式 @@ -35,6 +35,11 @@ class Actions: :return: self """ is_loc = False + mid_point = offset_x == offset_y is None + if offset_x is None: + offset_x = 0 + if offset_y is None: + offset_y = 0 if isinstance(ele_or_loc, (tuple, list)): is_loc = True lx = ele_or_loc[0] + offset_x @@ -42,7 +47,7 @@ class Actions: elif isinstance(ele_or_loc, str) or ele_or_loc._type == 'ChromiumElement': ele_or_loc = self.owner(ele_or_loc) self.owner.scroll.to_see(ele_or_loc) - x, y = ele_or_loc.rect.location if offset_x or offset_y else ele_or_loc.rect.midpoint + x, y = ele_or_loc.rect.midpoint if mid_point else ele_or_loc.rect.location lx = x + offset_x ly = y + offset_y else: @@ -58,8 +63,7 @@ class Actions: if is_loc: cx, cy = location_to_client(self.owner, lx, ly) else: - x, y = ele_or_loc.rect.viewport_location if offset_x or offset_y \ - else ele_or_loc.rect.viewport_midpoint + x, y = ele_or_loc.rect.viewport_midpoint if mid_point else ele_or_loc.rect.viewport_location cx = x + offset_x cy = y + offset_y diff --git a/DrissionPage/_units/waiter.pyi b/DrissionPage/_units/waiter.pyi index 8eb4849..69e33de 100644 --- a/DrissionPage/_units/waiter.pyi +++ b/DrissionPage/_units/waiter.pyi @@ -42,8 +42,6 @@ class BrowserWaiter(OriginWaiter): class BaseWaiter(OriginWaiter): _owner: ChromiumBase = ... - def __call__(self, second: float, scope: float = None) -> ChromiumBase: ... - def ele_deleted(self, loc_or_ele: Union[str, tuple, ChromiumElement], timeout: float = None,