diff --git a/DrissionPage/browser.py b/DrissionPage/browser.py index 36e522e..387ba2d 100644 --- a/DrissionPage/browser.py +++ b/DrissionPage/browser.py @@ -8,18 +8,20 @@ from .chromium_driver import BrowserDriver class Browser(object): BROWSERS = {} - def __new__(cls, browser_id, page): + def __new__(cls, address, browser_id, page): """ - :param browser_id: BrowserDriver对象 + :param address: 浏览器地址 + :param browser_id: 浏览器id :param page: ChromiumPage对象 """ if browser_id in cls.BROWSERS: return cls.BROWSERS[browser_id] return object.__new__(cls) - def __init__(self, browser_id, page): + def __init__(self, address, browser_id, page): """ - :param page: BrowserDriver对象 + :param address: 浏览器地址 + :param browser_id: 浏览器id :param page: ChromiumPage对象 """ if hasattr(self, '_created'): @@ -28,10 +30,11 @@ class Browser(object): Browser.BROWSERS[browser_id] = self self.page = page - self.address = page.address - self._driver = BrowserDriver(browser_id, 'browser', page.address) + self.address = address + self._driver = BrowserDriver(browser_id, 'browser', address) self.id = browser_id self._frames = {} + self._connected = False self._process_id = None r = self.run_cdp('SystemInfo.getProcessInfo') @@ -40,8 +43,6 @@ class Browser(object): self._process_id = i['id'] break - self._dl_mgr = BrowserDownloadManager(self) - self.run_cdp('Target.setDiscoverTargets') self._driver.set_listener('Target.targetDestroyed', self._onTargetDestroyed) @@ -53,6 +54,12 @@ class Browser(object): if i == tab_id: self._frames.pop(k) + def connect_to_page(self): + """执行与page相关的逻辑""" + if not self._connected: + self._dl_mgr = BrowserDownloadManager(self) + self._connected = True + def run_cdp(self, cmd, **cmd_args): """执行Chrome DevTools Protocol语句 :param cmd: 协议项目 diff --git a/DrissionPage/browser.pyi b/DrissionPage/browser.pyi index 91e9fdb..e4672ef 100644 --- a/DrissionPage/browser.pyi +++ b/DrissionPage/browser.pyi @@ -15,10 +15,11 @@ class Browser(object): _frames: dict = ... _process_id: Optional[int] = ... _dl_mgr: BrowserDownloadManager = ... + _connected: bool = ... - def __new__(cls, browser_id: str, page: ChromiumPage): ... + def __new__(cls, address: str, browser_id: str, page: ChromiumPage): ... - def __init__(self, browser_id: str, page: ChromiumPage): ... + def __init__(self, address: str, browser_id: str, page: ChromiumPage): ... def run_cdp(self, cmd, **cmd_args) -> dict: ... @@ -43,6 +44,8 @@ class Browser(object): def get_window_bounds(self) -> dict: ... + def connect_to_page(self) -> None: ... + def _onTargetDestroyed(self, **kwargs): ... def quit(self) -> None: ... diff --git a/DrissionPage/chromium_base.py b/DrissionPage/chromium_base.py index 8c10d16..a0c5c2b 100644 --- a/DrissionPage/chromium_base.py +++ b/DrissionPage/chromium_base.py @@ -43,7 +43,7 @@ class ChromiumBase(BasePage): self._root_id = None # object id self._debug = False self._debug_recorder = None - self._tab_obj = None + self._driver = None self._set = None self._screencast = None self._actions = None @@ -103,18 +103,18 @@ class ChromiumBase(BasePage): :return: None """ self._is_loading = True - self._tab_obj = ChromiumDriver(tab_id=tab_id, tab_type='page', address=self.address) + self._driver = ChromiumDriver(tab_id=tab_id, tab_type='page', address=self.address) - self._tab_obj.call_method('DOM.enable') - self._tab_obj.call_method('Page.enable') + self._driver.call_method('DOM.enable') + self._driver.call_method('Page.enable') - self._tab_obj.set_listener('Page.frameStoppedLoading', self._onFrameStoppedLoading) - self._tab_obj.set_listener('Page.frameStartedLoading', self._onFrameStartedLoading) - self._tab_obj.set_listener('DOM.documentUpdated', self._onDocumentUpdated) - self._tab_obj.set_listener('Page.loadEventFired', self._onLoadEventFired) - self._tab_obj.set_listener('Page.frameNavigated', self._onFrameNavigated) - self._tab_obj.set_listener('Page.frameAttached', self._onFrameAttached) - self._tab_obj.set_listener('Page.frameDetached', self._onFrameDetached) + self._driver.set_listener('Page.frameStoppedLoading', self._onFrameStoppedLoading) + self._driver.set_listener('Page.frameStartedLoading', self._onFrameStartedLoading) + self._driver.set_listener('DOM.documentUpdated', self._onDocumentUpdated) + self._driver.set_listener('Page.loadEventFired', self._onLoadEventFired) + self._driver.set_listener('Page.frameNavigated', self._onFrameNavigated) + self._driver.set_listener('Page.frameAttached', self._onFrameAttached) + self._driver.set_listener('Page.frameDetached', self._onFrameDetached) def _get_document(self): """刷新cdp使用的document数据""" @@ -283,9 +283,9 @@ class ChromiumBase(BasePage): @property def driver(self): """返回用于控制浏览器的ChromiumDriver对象""" - if self._tab_obj is None: + if self._driver is None: raise RuntimeError('浏览器已关闭或链接已断开。') - return self._tab_obj + return self._driver @property def is_loading(self): @@ -738,7 +738,7 @@ class ChromiumBase(BasePage): raise TypeError('必须传入定位符、iframe序号、id、name、ChromiumFrame对象其中之一。') def get_frames(self, loc=None, timeout=None): - """获取所有符号条件的frame对象 + """获取所有符合条件的frame对象 :param loc: 定位符,为None时返回所有 :param timeout: 查找超时时间 :return: ChromiumFrame对象组成的列表 diff --git a/DrissionPage/chromium_base.pyi b/DrissionPage/chromium_base.pyi index 22ac5df..9f6d569 100644 --- a/DrissionPage/chromium_base.pyi +++ b/DrissionPage/chromium_base.pyi @@ -30,7 +30,7 @@ class ChromiumBase(BasePage): self._browser: Browser = ... self._page: ChromiumPage = ... self.address: str = ... - self._tab_obj: ChromiumDriver = ... + self._driver: ChromiumDriver = ... self._is_reading: bool = ... self._timeouts: Timeout = ... self._first_run: bool = ... diff --git a/DrissionPage/chromium_frame.py b/DrissionPage/chromium_frame.py index 6ad88f8..ca42c25 100644 --- a/DrissionPage/chromium_frame.py +++ b/DrissionPage/chromium_frame.py @@ -110,7 +110,7 @@ class ChromiumFrame(ChromiumBase): self._debug = debug else: self._is_diff_domain = True - self._tab_obj.stop() + self._driver.stop() super().__init__(self.address, self.frame_id, self._target_page.timeout) obj_id = super().run_js('document;', as_expr=True)['objectId'] self.doc_ele = ChromiumElement(self, obj_id=obj_id) @@ -118,7 +118,7 @@ class ChromiumFrame(ChromiumBase): def _check_ok(self): """用于应付同域异域之间跳转导致元素丢失问题""" - if self._tab_obj._stopped.is_set(): + if self._driver._stopped.is_set(): self._reload() try: diff --git a/DrissionPage/chromium_page.py b/DrissionPage/chromium_page.py index ab1ece7..9358f99 100644 --- a/DrissionPage/chromium_page.py +++ b/DrissionPage/chromium_page.py @@ -10,11 +10,9 @@ from requests import get from .browser import Browser from .chromium_base import ChromiumBase, Timeout -from .chromium_driver import ChromiumDriver from .chromium_tab import ChromiumTab from .commons.browser import connect_browser from .configs.chromium_options import ChromiumOptions -from .errors import BrowserConnectError from .setter import ChromiumPageSetter from .waiter import ChromiumPageWaiter @@ -22,41 +20,48 @@ from .waiter import ChromiumPageWaiter class ChromiumPage(ChromiumBase): """用于管理浏览器的类""" - def __init__(self, addr_driver_opts=None, tab_id=None, timeout=None): + def __init__(self, addr_or_opts=None, tab_id=None, timeout=None, addr_driver_opts=None): """ - :param addr_driver_opts: 浏览器地址:端口、ChromiumDriver对象或ChromiumOptions对象 + :param addr_or_opts: 浏览器地址:端口或ChromiumOptions对象 :param tab_id: 要控制的标签页id,不指定默认为激活的 :param timeout: 超时时间 """ + if addr_driver_opts: + addr_or_opts = addr_driver_opts self._page = self - super().__init__(addr_driver_opts, tab_id) + address = self._handle_options(addr_or_opts) + self._run_browser() + super().__init__(address, tab_id) self.set.timeouts(implicit=timeout) + self._page_init() - def _set_start_options(self, addr_driver_opts, none): + def _handle_options(self, addr_or_opts): """设置浏览器启动属性 - :param addr_driver_opts: 'ip:port'、ChromiumOptions - :param none: 用于后代继承 - :return: None + :param addr_or_opts: 'ip:port'、ChromiumOptions + :return: 返回浏览器地址 """ - if not addr_driver_opts or isinstance(addr_driver_opts, ChromiumOptions): - self._driver_options = addr_driver_opts or ChromiumOptions(addr_driver_opts) + if not addr_or_opts: + self._driver_options = ChromiumOptions(addr_or_opts) + + elif isinstance(addr_or_opts, ChromiumOptions): + self._driver_options = addr_or_opts # 接收浏览器地址和端口 - elif isinstance(addr_driver_opts, str): + elif isinstance(addr_or_opts, str): self._driver_options = ChromiumOptions() - self._driver_options.debugger_address = addr_driver_opts - - # 接收传递过来的ChromiumDriver,浏览器 - elif isinstance(addr_driver_opts, ChromiumDriver): - self._driver_options = ChromiumOptions(read_file=False) - self._driver_options.debugger_address = addr_driver_opts.address - self._tab_obj = addr_driver_opts + self._driver_options.debugger_address = addr_or_opts else: - raise TypeError('只能接收ChromiumDriver或ChromiumOptions类型参数。') + raise TypeError('只能接收ip:port格式或ChromiumOptions类型参数。') - self.address = self._driver_options.debugger_address.replace('localhost', - '127.0.0.1').lstrip('http://').lstrip('https://') + return self._driver_options.debugger_address + + def _run_browser(self): + """连接浏览器""" + connect_browser(self._driver_options) + ws = get(f'http://{self._driver_options.debugger_address}/json/version', + headers={'Connection': 'close'}).json()['webSocketDebuggerUrl'] + self._browser = Browser(self._driver_options.debugger_address, ws.split('/')[-1], self) def _set_runtime_settings(self): """设置运行时用到的属性""" @@ -69,40 +74,17 @@ class ChromiumPage(ChromiumBase): self._page_load_strategy = self._driver_options.page_load_strategy self._download_path = str(Path(self._driver_options.download_path).absolute()) - def _connect_browser(self, tab_id=None): - """连接浏览器,在第一次时运行 - :param tab_id: 要控制的标签页id,不指定默认为激活的 - :return: None - """ - self._chromium_init() - - if not self._tab_obj: # 不是传入driver的情况 - connect_browser(self._driver_options) - if not tab_id: - json = get(f'http://{self.address}/json', headers={'Connection': 'close'}).json() - tab_id = [i['id'] for i in json if i['type'] == 'page'] - if not tab_id: - raise BrowserConnectError('浏览器连接失败,可能是浏览器版本原因。') - tab_id = tab_id[0] - - self._driver_init(tab_id) - - self._page_init() - self._get_document() - self._first_run = False - def _page_init(self): """浏览器相关设置""" - ws = get(f'http://{self.address}/json/version', headers={'Connection': 'close'}).json()['webSocketDebuggerUrl'] - self._browser = Browser(ws.split('/')[-1], self) - self._alert = Alert() - self._tab_obj.set_listener('Page.javascriptDialogOpening', self._on_alert_open) - self._tab_obj.set_listener('Page.javascriptDialogClosed', self._on_alert_close) + self._driver.set_listener('Page.javascriptDialogOpening', self._on_alert_open) + self._driver.set_listener('Page.javascriptDialogClosed', self._on_alert_close) self._rect = None self._main_tab = self.tab_id + self._browser.connect_to_page() + @property def browser(self): """返回用于控制浏览器cdp的driver""" @@ -327,7 +309,7 @@ class ChromiumPage(ChromiumBase): self._alert.defaultPrompt = None self._alert.response_accept = kwargs.get('result') self._alert.response_text = kwargs['userInput'] - self._tab_obj.has_alert = False + self._driver.has_alert = False def _on_alert_open(self, **kwargs): """alert出现时触发的方法""" @@ -337,7 +319,7 @@ class ChromiumPage(ChromiumBase): self._alert.defaultPrompt = kwargs.get('defaultPrompt', None) self._alert.response_accept = None self._alert.response_text = None - self._tab_obj.has_alert = True + self._driver.has_alert = True class ChromiumTabRect(object): diff --git a/DrissionPage/chromium_page.pyi b/DrissionPage/chromium_page.pyi index 90f9f09..5b05ae5 100644 --- a/DrissionPage/chromium_page.pyi +++ b/DrissionPage/chromium_page.pyi @@ -7,7 +7,6 @@ from typing import Union, Tuple, List, Optional from .browser import Browser from .chromium_base import ChromiumBase -from .chromium_driver import ChromiumDriver from .chromium_tab import ChromiumTab from .configs.chromium_options import ChromiumOptions from .setter import ChromiumPageSetter @@ -17,7 +16,7 @@ from .waiter import ChromiumPageWaiter class ChromiumPage(ChromiumBase): def __init__(self, - addr_driver_opts: Union[str, int, ChromiumOptions, ChromiumDriver] = None, + addr_or_opts: Union[str, int, ChromiumOptions] = None, tab_id: str = None, timeout: float = None): self._driver_options: ChromiumOptions = ... @@ -26,11 +25,9 @@ class ChromiumPage(ChromiumBase): self._browser: Browser = ... self._rect: ChromiumTabRect = ... - def _connect_browser(self, - addr_driver_opts: Union[str, ChromiumDriver] = None, - tab_id: str = None) -> None: ... + def _handle_options(self, addr_or_opts) -> str: ... - def _set_start_options(self, addr_driver_opts: Union[str, ChromiumDriver], none) -> None: ... + def _run_browser(self) -> None: ... def _page_init(self) -> None: ... diff --git a/DrissionPage/chromium_tab.py b/DrissionPage/chromium_tab.py index 449a0a7..9b1d60d 100644 --- a/DrissionPage/chromium_tab.py +++ b/DrissionPage/chromium_tab.py @@ -109,7 +109,7 @@ class WebPageTab(SessionPage, ChromiumTab): @property def _browser_url(self): """返回浏览器当前url""" - return super(SessionPage, self).url if self._tab_obj else None + return super(SessionPage, self).url if self._driver else None @property def title(self): @@ -279,7 +279,7 @@ class WebPageTab(SessionPage, ChromiumTab): # s模式转d模式 if self._mode == 'd': - if self._tab_obj is None: + if self._driver is None: self._connect_browser(self.page._driver_options) self._url = None if not self._has_driver else super(SessionPage, self).url diff --git a/DrissionPage/web_page.py b/DrissionPage/web_page.py index 15e179c..426eae5 100644 --- a/DrissionPage/web_page.py +++ b/DrissionPage/web_page.py @@ -39,7 +39,7 @@ class WebPage(SessionPage, ChromiumPage, BasePage): self.address = None self._session = None - self._tab_obj = None + self._driver = None self._driver_options = None self._session_options = None self._response = None @@ -62,7 +62,7 @@ class WebPage(SessionPage, ChromiumPage, BasePage): """ # 浏览器配置 if isinstance(dr_opt, ChromiumDriver): - self._tab_obj = dr_opt + self._driver = dr_opt self._driver_options = ChromiumOptions() self._driver_options.debugger_address = dr_opt.address dr_opt = False @@ -141,7 +141,7 @@ class WebPage(SessionPage, ChromiumPage, BasePage): @property def _browser_url(self): """返回浏览器当前url""" - return super(SessionPage, self).url if self._tab_obj else None + return super(SessionPage, self).url if self._driver else None @property def title(self): @@ -311,7 +311,7 @@ class WebPage(SessionPage, ChromiumPage, BasePage): # s模式转d模式 if self._mode == 'd': - if self._tab_obj is None: + if self._driver is None: self._connect_browser(self._driver_options) self._url = None if not self._has_driver else super(SessionPage, self).url @@ -393,8 +393,8 @@ class WebPage(SessionPage, ChromiumPage, BasePage): self.driver.call_method('Browser.close') except Exception: pass - self._tab_obj.stop() - self._tab_obj = None + self._driver.stop() + self._driver = None self._has_driver = None def close_session(self): @@ -430,5 +430,5 @@ class WebPage(SessionPage, ChromiumPage, BasePage): self._has_session = None if self._has_driver: super(SessionPage, self).quit() - self._tab_obj = None + self._driver = None self._has_driver = None diff --git a/DrissionPage/web_page.pyi b/DrissionPage/web_page.pyi index 15f30f8..460f2cf 100644 --- a/DrissionPage/web_page.pyi +++ b/DrissionPage/web_page.pyi @@ -36,7 +36,7 @@ class WebPage(SessionPage, ChromiumPage, BasePage): self._driver_options: Union[ChromiumOptions, None] = ... self._DownloadKit: DownloadKit = ... self._download_path: str = ... - self._tab_obj: ChromiumDriver = ... + self._driver: ChromiumDriver = ... self._frames: dict = ... def __call__(self,