diff --git a/DrissionPage/_base/browser.py b/DrissionPage/_base/browser.py index d13a833..b10237a 100644 --- a/DrissionPage/_base/browser.py +++ b/DrissionPage/_base/browser.py @@ -24,6 +24,7 @@ from .._pages.chromium_base import Timeout from .._pages.tabs import ChromiumTab, MixTab from .._units.downloader import DownloadManager from .._units.setter import BrowserSetter +from .._units.states import BrowserStates from .._units.waiter import BrowserWaiter from ..errors import BrowserConnectError, CDPError from ..errors import PageDisconnectedError @@ -64,6 +65,7 @@ class Chromium(object): self._set = None self._wait = None + self._states = None self._timeouts = Timeout(**self._chromium_options.timeouts) self._load_mode = self._chromium_options.load_mode self._download_path = str(Path(self._chromium_options.download_path).absolute()) @@ -72,8 +74,8 @@ class Chromium(object): self.address = self._chromium_options.address self._driver = BrowserDriver(self.id, 'browser', self.address, self) - if (not self._chromium_options._ua_set and self._is_headless != self._chromium_options.is_headless) or ( - self._is_exists and self._chromium_options._new_env): + if ((not self._chromium_options._ua_set and self._is_headless != self._chromium_options.is_headless) + or (self._is_exists and self._chromium_options._new_env)): self.quit(3, True) connect_browser(self._chromium_options) s = Session() @@ -137,6 +139,12 @@ class Chromium(object): self._set = BrowserSetter(self) return self._set + @property + def states(self): + if self._states is None: + self._states = BrowserStates(self) + return self._states + @property def wait(self): if self._wait is None: @@ -183,12 +191,13 @@ class Chromium(object): if tab: kwargs['browserContextId'] = tab - try: - tab = self._run_cdp('Target.createTarget', **kwargs)['targetId'] - except CDPError: - data = ('a', {'href': url or 'https://#', 'target': '_new' if new_window else '_blank'}) - tab = self.get_mix_tab() if isinstance(obj, MixTab) else self.get_tab() - return tab.add_ele(data).click.for_new_tab(by_js=True) + if self.states.is_incognito(): + return _new_tab_by_js(self, url, obj, new_window) + else: + try: + tab = self._run_cdp('Target.createTarget', **kwargs)['targetId'] + except CDPError: + return _new_tab_by_js(self, url, obj, new_window) while tab not in self._drivers: sleep(.1) @@ -198,13 +207,19 @@ class Chromium(object): return tab def get_tab(self, id_or_num=None, title=None, url=None, tab_type='page', as_id=False): - return self._get_tab(id_or_num=id_or_num, title=title, url=url, tab_type=tab_type, as_id=as_id) + t = self._get_tab(id_or_num=id_or_num, title=title, url=url, tab_type=tab_type, as_id=as_id) + if t._type == 'MixTab': + raise RuntimeError('该标签页已有MixTab版本,如需多对象公用请用Settings设置singleton_tab_obj为False。') + return t def get_tabs(self, title=None, url=None, tab_type='page', as_id=False): return self._get_tabs(title=title, url=url, tab_type=tab_type, as_id=as_id) def get_mix_tab(self, id_or_num=None, title=None, url=None, tab_type='page', as_id=False): - return self._get_tab(id_or_num=id_or_num, title=title, url=url, tab_type=tab_type, mix=True, as_id=as_id) + t = self._get_tab(id_or_num=id_or_num, title=title, url=url, tab_type=tab_type, mix=True, as_id=as_id) + if t._type != 'MixTab': + raise RuntimeError('该标签页已有非MixTab版本,如需多对象公用请用Settings设置singleton_tab_obj为False。') + return t def get_mix_tabs(self, title=None, url=None, tab_type='page', as_id=False): return self._get_tabs(title=title, url=url, tab_type=tab_type, mix=True, as_id=as_id) @@ -298,6 +313,13 @@ class Chromium(object): self._driver.set_callback('Target.targetDestroyed', self._onTargetDestroyed) self._driver.set_callback('Target.targetCreated', self._onTargetCreated) + def clear_cache(self, cache=True, cookies=True): + if cache: + self.latest_tab.run_cdp('Network.clearBrowserCache') + + if cookies: + self._run_cdp('Storage.clearCookies') + def quit(self, timeout=5, force=False, del_data=False): try: self._run_cdp('Browser.close') @@ -454,3 +476,13 @@ def run_browser(chromium_options): except: raise BrowserConnectError('\n浏览器连接失败,请确认浏览器是否启动。') return is_headless, browser_id, is_exists + + +def _new_tab_by_js(browser:Chromium, url, obj, new_window): + tab = browser.get_mix_tab() if isinstance(obj, MixTab) else browser.get_tab() + url = f'"{url}"' if url else '' + new = 'target="_new"' if new_window else 'target="_blank"' + tid = browser.latest_tab.tab_id + tab.run_js(f'window.open({url}, {new})') + tid = browser.wait.new_tab(curr_tab=tid) + return browser.get_mix_tab(tid) if isinstance(obj, MixTab) else browser.get_tab(tid) diff --git a/DrissionPage/_base/browser.pyi b/DrissionPage/_base/browser.pyi index 59a211a..6369d44 100644 --- a/DrissionPage/_base/browser.pyi +++ b/DrissionPage/_base/browser.pyi @@ -16,6 +16,7 @@ from .._pages.chromium_base import Timeout from .._pages.tabs import ChromiumTab, MixTab from .._units.downloader import DownloadManager from .._units.setter import BrowserSetter +from .._units.states import BrowserStates from .._units.waiter import BrowserWaiter @@ -29,6 +30,9 @@ class Chromium(object): retry_times: int = ... retry_interval: float = ... + _set: Optional[BrowserSetter] = ... + _wait: Optional[BrowserWaiter] = ... + _states: Optional[BrowserStates] = ... _chromium_options: ChromiumOptions = ... _session_options: SessionOptions = ... _driver: BrowserDriver = ... @@ -37,8 +41,6 @@ class Chromium(object): _all_drivers: Dict[str, Set[Driver]] = ... _process_id: Optional[int] = ... _dl_mgr: DownloadManager = ... - _set: Optional[BrowserSetter] = ... - _wait: Optional[BrowserWaiter] = ... _timeouts: Timeout = ... _load_mode: str = ... _download_path: str = ... @@ -97,6 +99,11 @@ class Chromium(object): """返回用于设置的对象""" ... + @property + def states(self) -> BrowserStates: + """返回用于获取状态的对象""" + ... + @property def wait(self) -> BrowserWaiter: """返回用于等待的对象""" @@ -285,6 +292,14 @@ class Chromium(object): """断开重连""" ... + def clear_cache(self, cache: bool = True, cookies: bool = True) -> None: + """清除缓存,可选要清除的项 + :param cache: 是否清除cache + :param cookies: 是否清除cookies + :return: None + """ + ... + def quit(self, timeout: float = 5, force: bool = False, del_data: bool = False) -> None: """关闭浏览器 :param timeout: 等待浏览器关闭超时时间(秒) diff --git a/DrissionPage/_units/states.py b/DrissionPage/_units/states.py index fd9d162..7cff31c 100644 --- a/DrissionPage/_units/states.py +++ b/DrissionPage/_units/states.py @@ -92,6 +92,7 @@ class ShadowRootStates(object): class BrowserStates(object): def __init__(self, browser): self._browser = browser + self._incognito = None def is_alive(self): return self._browser._driver.is_running @@ -102,6 +103,11 @@ class BrowserStates(object): def is_existed(self): return self._browser._is_exists + def is_incognito(self): + if self._incognito is None: + self._incognito = str(self._browser._run_cdp('Browser.getHistograms')).count('Incognito') > 1 + return self._incognito + class PageStates(object): """Page对象、Tab对象使用""" diff --git a/DrissionPage/_units/states.pyi b/DrissionPage/_units/states.pyi index 0edca88..988cede 100644 --- a/DrissionPage/_units/states.pyi +++ b/DrissionPage/_units/states.pyi @@ -95,6 +95,7 @@ class ShadowRootStates(object): class BrowserStates(object): _browser: Chromium = ... + _incognito: Optional[bool] = ... def __init__(self, browser: Chromium): """ @@ -114,6 +115,10 @@ class BrowserStates(object): """返回浏览器是否接管的""" ... + def is_incognito(self): + """返回浏览器是否无痕模式""" + ... + class PageStates(object): _owner: ChromiumBase = ...