diff --git a/DrissionPage/__init__.py b/DrissionPage/__init__.py index 9ac42a2..0e9b576 100644 --- a/DrissionPage/__init__.py +++ b/DrissionPage/__init__.py @@ -14,4 +14,4 @@ from ._configs.chromium_options import ChromiumOptions from ._configs.session_options import SessionOptions __all__ = ['ChromiumPage', 'ChromiumOptions', 'SessionOptions', 'SessionPage', 'WebPage', '__version__'] -__version__ = '4.0.4.12' +__version__ = '4.0.4.13' diff --git a/DrissionPage/_base/browser.py b/DrissionPage/_base/browser.py index 4bee1aa..0826b37 100644 --- a/DrissionPage/_base/browser.py +++ b/DrissionPage/_base/browser.py @@ -72,7 +72,9 @@ class Browser(object): :param owner: 使用该驱动的对象 :return: Driver对象 """ - d = self._drivers.pop(tab_id, Driver(tab_id, 'page', self.address)) + d = self._drivers.pop(tab_id, None) + if not d: + d = Driver(tab_id, 'page', self.address) d.owner = owner self._all_drivers.setdefault(tab_id, set()).add(d) return d @@ -188,6 +190,30 @@ class Browser(object): """ return self.run_cdp('Browser.getWindowForTarget', targetId=tab_id or self.id)['bounds'] + def new_tab(self, new_window=False, background=False, new_context=False): + """新建一个标签页 + :param new_window: 是否在新窗口打开标签页 + :param background: 是否不激活新标签页,如new_window为True则无效 + :param new_context: 是否创建新的上下文 + :return: 新标签页id + """ + bid = None + if new_context: + bid = self.run_cdp('Target.createBrowserContext')['browserContextId'] + + kwargs = {'url': ''} + if new_window: + kwargs['newWindow'] = True + if background: + kwargs['background'] = True + if bid: + kwargs['browserContextId'] = bid + + tid = self.run_cdp('Target.createTarget', **kwargs)['targetId'] + while tid not in self._drivers: + sleep(.1) + return tid + def reconnect(self): """断开重连""" self._driver.stop() diff --git a/DrissionPage/_base/browser.pyi b/DrissionPage/_base/browser.pyi index 7f80b57..170f88b 100644 --- a/DrissionPage/_base/browser.pyi +++ b/DrissionPage/_base/browser.pyi @@ -56,6 +56,8 @@ class Browser(object): def get_window_bounds(self, tab_id: str = None) -> dict: ... + def new_tab(self, new_window: bool = False, background: bool = False, new_context: bool = False) -> str: ... + def reconnect(self) -> None: ... def connect_to_page(self) -> None: ... diff --git a/DrissionPage/_pages/chromium_page.py b/DrissionPage/_pages/chromium_page.py index cce94b6..0a77acb 100644 --- a/DrissionPage/_pages/chromium_page.py +++ b/DrissionPage/_pages/chromium_page.py @@ -14,6 +14,7 @@ from requests import get from .._base.browser import Browser from .._configs.chromium_options import ChromiumOptions from .._functions.browser import connect_browser +from .._functions.settings import Settings from .._functions.tools import PortFinder from .._pages.chromium_base import ChromiumBase, get_mhtml, get_pdf, Timeout from .._pages.chromium_tab import ChromiumTab @@ -130,8 +131,9 @@ class ChromiumPage(ChromiumBase): @property def latest_tab(self): - """返回最新的标签页对象,最新标签页指最后创建或最后被激活的""" - return self.get_tab(self.tab_ids[0]) + """返回最新的标签页,最新标签页指最后创建或最后被激活的 + 当Settings.singleton_tab_obj==True时返回Tab对象,否则返回tab id""" + return self.get_tab(self.tab_ids[0], as_id=not Settings.singleton_tab_obj) @property def process_id(self): @@ -163,7 +165,12 @@ class ChromiumPage(ChromiumBase): elif isinstance(id_or_num, int): id_or_num = self.tab_ids[id_or_num - 1 if id_or_num > 0 else id_or_num] elif isinstance(id_or_num, ChromiumTab): - return id_or_num.tab_id if as_id else id_or_num + if as_id: + return id_or_num.tab_id + elif Settings.singleton_tab_obj: + return id_or_num + else: + return self.get_tab(id_or_num.tab_id) elif title == url == tab_type is None: id_or_num = self.tab_id @@ -202,32 +209,11 @@ class ChromiumPage(ChromiumBase): :param new_context: 是否创建新的上下文 :return: 新标签页对象 """ - tab = ChromiumTab(self, tab_id=self._new_tab(new_window, background, new_context)) + tab = ChromiumTab(self, tab_id=self.browser.new_tab(new_window, background, new_context)) if url: tab.get(url) return tab - def _new_tab(self, new_window=False, background=False, new_context=False): - """新建一个标签页 - :param new_window: 是否在新窗口打开标签页 - :param background: 是否不激活新标签页,如new_window为True则无效 - :param new_context: 是否创建新的上下文 - :return: 新标签页对象 - """ - bid = None - if new_context: - bid = self.browser.run_cdp('Target.createBrowserContext')['browserContextId'] - - kwargs = {'url': ''} - if new_window: - kwargs['newWindow'] = True - if background: - kwargs['background'] = True - if bid: - kwargs['browserContextId'] = bid - - return self.browser.run_cdp('Target.createTarget', **kwargs)['targetId'] - def close(self): """关闭Page管理的标签页""" self.close_tabs(self.tab_id) diff --git a/DrissionPage/_pages/chromium_page.pyi b/DrissionPage/_pages/chromium_page.pyi index ad5cda9..8806b77 100644 --- a/DrissionPage/_pages/chromium_page.pyi +++ b/DrissionPage/_pages/chromium_page.pyi @@ -57,7 +57,7 @@ class ChromiumPage(ChromiumBase): def wait(self) -> PageWaiter: ... @property - def latest_tab(self) -> Union[ChromiumTab, ChromiumPage]: ... + def latest_tab(self) -> Union[ChromiumTab, ChromiumPage, str]: ... @property def process_id(self) -> Optional[int]: ... @@ -102,8 +102,6 @@ class ChromiumPage(ChromiumBase): def new_tab(self, url: str = None, new_window: bool = False, background: bool = False, new_context: bool = False) -> ChromiumTab: ... - def _new_tab(self, new_window: bool = False, background: bool = False, new_context: bool = False) -> str: ... - def close(self) -> None: ... def close_tabs(self, tabs_or_ids: Union[str, ChromiumTab, List[Union[str, ChromiumTab]], diff --git a/DrissionPage/_pages/web_page.py b/DrissionPage/_pages/web_page.py index 59d7330..969b0cf 100644 --- a/DrissionPage/_pages/web_page.py +++ b/DrissionPage/_pages/web_page.py @@ -362,7 +362,7 @@ class WebPage(SessionPage, ChromiumPage, BasePage): :param new_context: 是否创建新的上下文 :return: 新标签页对象 """ - tab = WebPageTab(self, tab_id=self._new_tab(new_window, background, new_context)) + tab = WebPageTab(self, tab_id=self.browser.new_tab(new_window, background, new_context)) if url: tab.get(url) return tab diff --git a/DrissionPage/_units/actions.py b/DrissionPage/_units/actions.py index f99ee70..6fcf2f0 100644 --- a/DrissionPage/_units/actions.py +++ b/DrissionPage/_units/actions.py @@ -298,10 +298,13 @@ class Actions: input_text_or_keys(self.owner, text) return self - def wait(self, second): - """等待若干秒""" - sleep(second) - return self + def wait(self, second, scope=None): + """等待若干秒,如传入两个参数,等待时间为这两个数间的一个随机数 + :param second: 秒数 + :param scope: 随机数范围 + :return: None + """ + self.owner.wait(second=second, scope=scope) def _get_key_data(self, key, action): """获取用于发送的按键信息 diff --git a/DrissionPage/_units/actions.pyi b/DrissionPage/_units/actions.pyi index c070799..4904a35 100644 --- a/DrissionPage/_units/actions.pyi +++ b/DrissionPage/_units/actions.pyi @@ -100,7 +100,7 @@ class Actions: def input(self, text: Any) -> Actions: ... - def wait(self, second: float) -> Actions: ... + def wait(self, second: float, scope: float = None) -> Actions: ... def _get_key_data(self, key: str, action: str) -> dict: ... diff --git a/DrissionPage/_units/waiter.py b/DrissionPage/_units/waiter.py index 5d9a44d..077d8dd 100644 --- a/DrissionPage/_units/waiter.py +++ b/DrissionPage/_units/waiter.py @@ -11,13 +11,7 @@ from .._functions.settings import Settings from ..errors import WaitTimeoutError, NoRectError -class BaseWaiter(object): - def __init__(self, page_or_ele): - """ - :param page_or_ele: 页面对象或元素对象 - """ - self._driver = page_or_ele - +class OriginWaiter(object): def __call__(self, second, scope=None): """等待若干秒,如传入两个参数,等待时间为这两个数间的一个随机数 :param second: 秒数 @@ -30,6 +24,14 @@ class BaseWaiter(object): from random import uniform sleep(uniform(second, scope)) + +class BaseWaiter(OriginWaiter): + def __init__(self, page_or_ele): + """ + :param page_or_ele: 页面对象或元素对象 + """ + self._driver = page_or_ele + def ele_deleted(self, loc_or_ele, timeout=None, raise_err=None): """等待元素从DOM中删除 :param loc_or_ele: 要等待的元素,可以是已有元素、定位符 @@ -125,7 +127,7 @@ class BaseWaiter(object): :return: 成功返回任务对象,失败返回False """ if not self._driver.browser._dl_mgr._running: - raise RuntimeError('使用下载管理功能前需显式设置下载路径(使用set.download_path()方法、配置对象或ini文件均可)。') + raise RuntimeError('此功能需显式设置下载路径(使用set.download_path()方法、配置对象或ini文件均可)。') self._driver.browser._dl_mgr.set_flag(self._driver.tab_id, False if cancel_it else True) if timeout is None: timeout = self._driver.timeout @@ -240,7 +242,7 @@ class TabWaiter(BaseWaiter): :return: 是否等待成功 """ if not self._driver.browser._dl_mgr._running: - raise RuntimeError('使用下载管理功能前需显式设置下载路径(使用set.download_path()方法、配置对象或ini文件均可)。') + raise RuntimeError('此功能需显式设置下载路径(使用set.download_path()方法、配置对象或ini文件均可)。') if not timeout: while self._driver.browser._dl_mgr.get_tab_missions(self._driver.tab_id): sleep(.5) @@ -282,9 +284,9 @@ class PageWaiter(TabWaiter): timeout = timeout if timeout is not None else self._driver.timeout end_time = perf_counter() + timeout while perf_counter() < end_time: - latest_tab = self._driver.latest_tab - if self._driver.tab_id != latest_tab: - return latest_tab + latest_tid = self._driver.tab_ids[0] + if self._driver.tab_id != latest_tid: + return latest_tid sleep(.01) if raise_err is True or Settings.raise_when_wait_failed is True: @@ -299,7 +301,7 @@ class PageWaiter(TabWaiter): :return: 是否等待成功 """ if not self._driver.browser._dl_mgr._running: - raise RuntimeError('使用下载管理功能前需显式设置下载路径(使用set.download_path()方法、配置对象或ini文件均可)。') + raise RuntimeError('此功能需显式设置下载路径(使用set.download_path()方法、配置对象或ini文件均可)。') if not timeout: while self._driver.browser._dl_mgr._missions: sleep(.5) @@ -321,7 +323,7 @@ class PageWaiter(TabWaiter): return True -class ElementWaiter(object): +class ElementWaiter(OriginWaiter): """等待元素在dom中某种状态,如删除、显示、隐藏""" def __init__(self, owner, ele): @@ -332,18 +334,6 @@ class ElementWaiter(object): self._owner = owner self._ele = ele - def __call__(self, second, scope=None): - """等待若干秒,如传入两个参数,等待时间为这两个数间的一个随机数 - :param second: 秒数 - :param scope: 随机数范围 - :return: None - """ - if scope is None: - sleep(second) - else: - from random import uniform - sleep(uniform(second, scope)) - def deleted(self, timeout=None, raise_err=None): """等待元素从dom删除 :param timeout: 超时时间,为None使用元素所在页面timeout属性 @@ -457,7 +447,7 @@ class ElementWaiter(object): :param raise_err: 等待失败时是否报错,为None时根据Settings设置 :return: 是否等待成功 """ - return self._wait_state('has_rect', True, timeout, raise_err, err_text='等待元素拥有大小及位置属性失败(等待{}秒)。') + return self._wait_state('has_rect', True, timeout, raise_err, err_text='等待元素拥有大小及位置失败(等{}秒)。') def _wait_state(self, attr, mode=False, timeout=None, raise_err=None, err_text=None): """等待元素某个元素状态到达指定状态 diff --git a/DrissionPage/_units/waiter.pyi b/DrissionPage/_units/waiter.pyi index 6c63f45..2f435eb 100644 --- a/DrissionPage/_units/waiter.pyi +++ b/DrissionPage/_units/waiter.pyi @@ -14,7 +14,11 @@ from .._pages.chromium_frame import ChromiumFrame from .._pages.chromium_page import ChromiumPage -class BaseWaiter(object): +class OriginWaiter(object): + def __call__(self, second: float, scope: float = None) -> None: ... + + +class BaseWaiter(OriginWaiter): def __init__(self, page: ChromiumBase): self._driver: ChromiumBase = ... @@ -73,7 +77,7 @@ class PageWaiter(TabWaiter): def all_downloads_done(self, timeout: float = None, cancel_if_timeout: bool = True) -> bool: ... -class ElementWaiter(object): +class ElementWaiter(OriginWaiter): def __init__(self, owner: ChromiumBase, ele: ChromiumElement): self._ele: ChromiumElement = ... self._owner: ChromiumBase = ...