添加Browser类,调整架构,未完成

This commit is contained in:
g1879 2023-10-18 17:52:27 +08:00
parent 0ff5b47a4c
commit de6691adc2
16 changed files with 278 additions and 144 deletions

137
DrissionPage/browser.py Normal file
View File

@ -0,0 +1,137 @@
# -*- coding:utf-8 -*-
from time import sleep
from .browser_download_manager import BrowserDownloadManager
from .chromium_driver import BrowserDriver
class Browser(object):
BROWSERS = {}
def __new__(cls, browser_id, page):
"""
:param browser_id: BrowserDriver对象
:param page: ChromiumPage对象
"""
if browser_id in cls.BROWSERS:
return cls.BROWSERS[browser_id]
return object.__new__(cls)
def __init__(self, browser_id, page):
"""
:param page: BrowserDriver对象
:param page: ChromiumPage对象
"""
if hasattr(self, '_created'):
return
self._created = True
Browser.BROWSERS[browser_id] = self
self.page = page
self.address = page.address
self._driver = BrowserDriver(browser_id, 'browser', page.address)
self.id = browser_id
self._frames = {}
self._process_id = None
r = self.run_cdp('SystemInfo.getProcessInfo')
for i in r.get('processInfo', []):
if i['type'] == 'browser':
self._process_id = i['id']
break
self._dl_mgr = BrowserDownloadManager(self)
self.run_cdp('Target.setDiscoverTargets')
self._driver.set_listener('Target.targetDestroyed', self._onTargetDestroyed)
def _onTargetDestroyed(self, **kwargs):
"""标签页关闭时执行"""
tab_id = kwargs['targetId']
self._dl_mgr.clear_tab_info(tab_id)
for k, i in self._frames.items():
if i == tab_id:
self._frames.pop(k)
def run_cdp(self, cmd, **cmd_args):
"""执行Chrome DevTools Protocol语句
:param cmd: 协议项目
:param cmd_args: 参数
:return: 执行的结果
"""
return self._driver.call_method(cmd, **cmd_args)
@property
def driver(self):
return self._driver
@property
def tabs_count(self):
"""返回标签页数量"""
return len(self.tabs)
@property
def tabs(self):
"""返回所有标签页id组成的列表"""
j = self._driver.get(f'http://{self.address}/json').json() # 不要改用cdp
return [i['id'] for i in j if i['type'] == 'page']
@property
def process_id(self):
"""返回浏览器进程id"""
return self._process_id
def find_tabs(self, title=None, url=None, tab_type=None, single=True):
"""查找符合条件的tab返回它们的id组成的列表
:param title: 要匹配title的文本
:param url: 要匹配url的文本
:param tab_type: tab类型可用列表输入多个
:param single: 是否返回首个结果的id为False返回所有信息
:return: tab id或tab dict
"""
tabs = self._driver.get(f'http://{self.address}/json').json() # 不要改用cdp
if isinstance(tab_type, str):
tab_type = {tab_type}
elif isinstance(tab_type, (list, tuple, set)):
tab_type = set(tab_type)
elif tab_type is not None:
raise TypeError('tab_type只能是set、list、tuple、str、None。')
r = [i for i in tabs if ((title is None or title in i['title']) and (url is None or url in i['url'])
and (tab_type is None or i['type'] in tab_type))]
return r[0]['id'] if r and single else r
def close_tab(self, tab_id):
"""关闭标签页
:param tab_id: 标签页id
:return: None
"""
self._driver.get(f'http://{self.address}/json/close/{tab_id}')
def activate_tab(self, tab_id):
"""使标签页变为活动状态
:param tab_id: 标签页id
:return: None
"""
self._driver.get(f'http://{self.address}/json/activate/{tab_id}')
def get_window_bounds(self):
"""返回浏览器窗口位置和大小信息"""
return self.run_cdp('Browser.getWindowForTarget', targetId=self.id)['bounds']
def quit(self):
"""关闭浏览器"""
self.run_cdp('Browser.close')
self.driver.stop()
if self.process_id:
from os import popen
from platform import system
txt = f'tasklist | findstr {self.process_id}' if system().lower() == 'windows' \
else f'ps -ef | grep {self.process_id}'
while True:
p = popen(txt)
if f' {self.process_id} ' not in p.read():
break
sleep(.2)

48
DrissionPage/browser.pyi Normal file
View File

@ -0,0 +1,48 @@
# -*- coding:utf-8 -*-
from typing import List, Optional, Union
from .browser_download_manager import BrowserDownloadManager
from .chromium_page import ChromiumPage
from .chromium_driver import BrowserDriver
class Browser(object):
BROWSERS: dict = ...
page: ChromiumPage = ...
_driver: BrowserDriver = ...
id: str = ...
address: str = ...
_frames: dict = ...
_process_id: Optional[int] = ...
_dl_mgr: BrowserDownloadManager = ...
def __new__(cls, browser_id: str, page: ChromiumPage): ...
def __init__(self, browser_id: str, page: ChromiumPage): ...
def run_cdp(self, cmd, **cmd_args) -> dict: ...
@property
def driver(self) -> BrowserDriver: ...
@property
def tabs_count(self) -> int: ...
@property
def tabs(self) -> List[str]: ...
@property
def process_id(self) -> Optional[int]: ...
def find_tabs(self, title: str = None, url: str = None,
tab_type: Union[str, list, tuple] = None, single: bool = True) -> Union[str, List[str]]: ...
def close_tab(self, tab_id: str) -> None: ...
def activate_tab(self, tab_id: str) -> None: ...
def get_window_bounds(self) -> dict: ...
def _onTargetDestroyed(self, **kwargs): ...
def quit(self) -> None: ...

View File

@ -8,42 +8,26 @@ from .commons.tools import get_usable_path
class BrowserDownloadManager(object): class BrowserDownloadManager(object):
BROWSERS = {}
def __new__(cls, page): def __init__(self, browser):
""" """
:param page: ChromiumPage对象 :param browser: Browser对象
""" """
if page.browser_driver.id in cls.BROWSERS: self._browser = browser
return cls.BROWSERS[page.browser_driver.id] self._page = browser.page
return object.__new__(cls)
def __init__(self, page):
"""
:param page: ChromiumPage对象
"""
if hasattr(self, '_created'):
return
self._created = True
self._page = page
self._when_download_file_exists = 'rename' self._when_download_file_exists = 'rename'
t = TabDownloadSettings(page.tab_id) t = TabDownloadSettings(self._page.tab_id)
t.path = page.download_path t.path = self._page.download_path
self._tabs_settings = {page.tab_id: t} # {tab_id: TabDownloadSettings}
self._missions = {} # {guid: DownloadMission} self._missions = {} # {guid: DownloadMission}
self._tabs_settings = {self._page.tab_id: t} # {tab_id: TabDownloadSettings}
self._tab_missions = {} # {tab_id: DownloadMission} self._tab_missions = {} # {tab_id: DownloadMission}
self._guid_and_tab = {} # 记录guid在哪个tab
self._flags = {} # {tab_id: [bool, DownloadMission]} self._flags = {} # {tab_id: [bool, DownloadMission]}
self._page.browser_driver.set_listener('Browser.downloadProgress', self._onDownloadProgress) self._browser.driver.set_listener('Browser.downloadProgress', self._onDownloadProgress)
self._page.browser_driver.set_listener('Browser.downloadWillBegin', self._onDownloadWillBegin) self._browser.driver.set_listener('Browser.downloadWillBegin', self._onDownloadWillBegin)
self._page.browser_driver.call_method('Browser.setDownloadBehavior', self._browser.run_cdp('Browser.setDownloadBehavior', downloadPath=self._page.download_path,
downloadPath=self._page.download_path, behavior='allowAndName', eventsEnabled=True)
behavior='allowAndName', eventsEnabled=True)
BrowserDownloadManager.BROWSERS[page.browser_driver.id] = self
@property @property
def missions(self): def missions(self):
@ -58,9 +42,8 @@ class BrowserDownloadManager(object):
""" """
self._tabs_settings.setdefault(tab_id, TabDownloadSettings(tab_id)).path = str(Path(path).absolute()) self._tabs_settings.setdefault(tab_id, TabDownloadSettings(tab_id)).path = str(Path(path).absolute())
if tab_id == self._page.tab_id: if tab_id == self._page.tab_id:
self._page.browser_driver.call_method('Browser.setDownloadBehavior', self._browser.run_cdp('Browser.setDownloadBehavior', downloadPath=str(Path(path).absolute()),
downloadPath=str(Path(path).absolute()), behavior='allowAndName', eventsEnabled=True)
behavior='allowAndName', eventsEnabled=True)
def set_rename(self, tab_id, rename): def set_rename(self, tab_id, rename):
"""设置某个tab的重命名文件名 """设置某个tab的重命名文件名
@ -100,14 +83,6 @@ class BrowserDownloadManager(object):
""" """
return self._tab_missions.get(tab_id, []) return self._tab_missions.get(tab_id, [])
def set_mission(self, tab_id, guid):
"""绑定tab和下载任务信息
:param tab_id: tab id
:param guid: 下载任务id
:return: None
"""
self._guid_and_tab[guid] = tab_id
def set_done(self, mission, state, final_path=None): def set_done(self, mission, state, final_path=None):
"""设置任务结束 """设置任务结束
:param mission: 任务对象 :param mission: 任务对象
@ -121,6 +96,7 @@ class BrowserDownloadManager(object):
if mission.tab_id in self._tab_missions and mission.id in self._tab_missions[mission.tab_id]: if mission.tab_id in self._tab_missions and mission.id in self._tab_missions[mission.tab_id]:
self._tab_missions[mission.tab_id].remove(mission.id) self._tab_missions[mission.tab_id].remove(mission.id)
self._missions.pop(mission.id) self._missions.pop(mission.id)
mission._is_done = True
def cancel(self, mission): def cancel(self, mission):
"""取消任务 """取消任务
@ -128,7 +104,7 @@ class BrowserDownloadManager(object):
:return: None :return: None
""" """
mission.state = 'canceled' mission.state = 'canceled'
self._page.browser_driver.call_method('Browser.cancelDownload', guid=mission.id) self._browser.run_cdp('Browser.cancelDownload', guid=mission.id)
if mission.final_path: if mission.final_path:
Path(mission.final_path).unlink(True) Path(mission.final_path).unlink(True)
@ -138,12 +114,22 @@ class BrowserDownloadManager(object):
:return: None :return: None
""" """
mission.state = 'skipped' mission.state = 'skipped'
self._page.browser_driver.call_method('Browser.cancelDownload', guid=mission.id) self._browser.run_cdp('Browser.cancelDownload', guid=mission.id)
def clear_tab_info(self, tab_id):
"""当tab关闭时清除有关信息
:param tab_id: 标签页id
:return: None
"""
self._tabs_settings.pop(tab_id)
self._tab_missions.pop(tab_id)
self._flags.pop(tab_id)
TabDownloadSettings.TABS.pop(tab_id)
def _onDownloadWillBegin(self, **kwargs): def _onDownloadWillBegin(self, **kwargs):
"""用于获取弹出新标签页触发的下载任务""" """用于获取弹出新标签页触发的下载任务"""
guid = kwargs['guid'] guid = kwargs['guid']
tab_id = self._page._frames.get(kwargs['frameId'], self._page.tab_id) tab_id = self._browser._frames.get(kwargs['frameId'], self._page.tab_id)
settings = TabDownloadSettings(tab_id) settings = TabDownloadSettings(tab_id)
if settings.rename: if settings.rename:
@ -249,6 +235,7 @@ class DownloadMission(object):
self.received_bytes = 0 self.received_bytes = 0
self.final_path = None self.final_path = None
self.save_path = save_path self.save_path = save_path
self._is_done = False
def __repr__(self): def __repr__(self):
return f'<DownloadMission {id(self)} {self.rate}>' return f'<DownloadMission {id(self)} {self.rate}>'
@ -261,7 +248,7 @@ class DownloadMission(object):
@property @property
def is_done(self): def is_done(self):
"""返回任务是否在运行中""" """返回任务是否在运行中"""
return self.state in ('completed', 'skipped', 'canceled') return self._is_done
def cancel(self): def cancel(self):
"""取消该任务,如任务已完成,删除已下载的文件""" """取消该任务,如任务已完成,删除已下载的文件"""

View File

@ -1,21 +1,19 @@
from pathlib import Path from pathlib import Path
from typing import Dict, Optional, Union from typing import Dict, Optional, Union
from chromium_page import ChromiumPage from .browser import Browser
from .chromium_page import ChromiumPage
class BrowserDownloadManager(object): class BrowserDownloadManager(object):
BROWSERS: Dict[str, BrowserDownloadManager] = ... _browser: Browser = ...
_page: ChromiumPage = ... _page: ChromiumPage = ...
_missions: Dict[str, DownloadMission] = ... _missions: Dict[str, DownloadMission] = ...
_tab_missions: dict = ... _tab_missions: dict = ...
_tabs_settings: Dict[str, TabDownloadSettings] = ... _tabs_settings: Dict[str, TabDownloadSettings] = ...
_guid_and_tab: Dict[str, str] = ...
_flags: dict = ... _flags: dict = ...
def __new__(cls, page: ChromiumPage): ... def __init__(self, browser: Browser): ...
def __init__(self, page: ChromiumPage): ...
@property @property
def missions(self) -> Dict[str, DownloadMission]: ... def missions(self) -> Dict[str, DownloadMission]: ...
@ -32,14 +30,14 @@ class BrowserDownloadManager(object):
def get_tab_missions(self, tab_id: str) -> list: ... def get_tab_missions(self, tab_id: str) -> list: ...
def set_mission(self, tab_id: str, guid: str) -> None: ...
def set_done(self, mission: DownloadMission, state: str, final_path: str = None) -> None: ... def set_done(self, mission: DownloadMission, state: str, final_path: str = None) -> None: ...
def cancel(self, mission: DownloadMission) -> None: ... def cancel(self, mission: DownloadMission) -> None: ...
def skip(self, mission: DownloadMission) -> None: ... def skip(self, mission: DownloadMission) -> None: ...
def clear_tab_info(self, tab_id: str) -> None: ...
def _onDownloadWillBegin(self, **kwargs) -> None: ... def _onDownloadWillBegin(self, **kwargs) -> None: ...
def _onDownloadProgress(self, **kwargs) -> None: ... def _onDownloadProgress(self, **kwargs) -> None: ...
@ -68,6 +66,7 @@ class DownloadMission(object):
received_bytes: int = ... received_bytes: int = ...
final_path: Optional[str] = ... final_path: Optional[str] = ...
save_path: str = ... save_path: str = ...
_is_done: bool = ...
def __init__(self, mgr: BrowserDownloadManager, tab_id: str, _id: str, path: str, name: str, url: str, def __init__(self, mgr: BrowserDownloadManager, tab_id: str, _id: str, path: str, name: str, url: str,
save_path: str): ... save_path: str): ...

View File

@ -11,7 +11,7 @@ from re import findall
from threading import Thread from threading import Thread
from time import perf_counter, sleep, time from time import perf_counter, sleep, time
from requests import Session from requests import get
from .action_chains import ActionChains from .action_chains import ActionChains
from .base import BasePage from .base import BasePage
@ -79,9 +79,8 @@ class ChromiumBase(BasePage):
""" """
self._chromium_init() self._chromium_init()
if not tab_id: if not tab_id:
u = f'http://{self.address}/json' json = get(f'http://{self.address}/json', headers={'Connection': 'close'}).json()
json = self._control_session.get(u).json()
self._control_session.get(u, headers={'Connection': 'close'})
tab_id = [i['id'] for i in json if i['type'] == 'page'] tab_id = [i['id'] for i in json if i['type'] == 'page']
if not tab_id: if not tab_id:
raise BrowserConnectError('浏览器连接失败,可能是浏览器版本原因。') raise BrowserConnectError('浏览器连接失败,可能是浏览器版本原因。')
@ -92,9 +91,6 @@ class ChromiumBase(BasePage):
def _chromium_init(self): def _chromium_init(self):
"""浏览器初始设置""" """浏览器初始设置"""
self._control_session = Session()
self._control_session.keep_alive = False
self._control_session.proxies = {'http': None, 'https': None}
self._first_run = True self._first_run = True
self._is_reading = False self._is_reading = False
self._upload_list = None self._upload_list = None
@ -277,9 +273,13 @@ class ChromiumBase(BasePage):
return self.ele(loc_or_str, timeout) return self.ele(loc_or_str, timeout)
@property @property
def browser(self): def main(self):
return self._page return self._page
@property
def browser(self):
return self._browser
@property @property
def driver(self): def driver(self):
"""返回用于控制浏览器的ChromiumDriver对象""" """返回用于控制浏览器的ChromiumDriver对象"""

View File

@ -7,8 +7,8 @@ from pathlib import Path
from typing import Union, Tuple, List, Any from typing import Union, Tuple, List, Any
from DataRecorder import Recorder from DataRecorder import Recorder
from requests import Session
from .browser import Browser
from .action_chains import ActionChains from .action_chains import ActionChains
from .base import BasePage from .base import BasePage
from .chromium_driver import ChromiumDriver from .chromium_driver import ChromiumDriver
@ -27,8 +27,8 @@ class ChromiumBase(BasePage):
address: Union[str, int], address: Union[str, int],
tab_id: str = None, tab_id: str = None,
timeout: float = None): timeout: float = None):
self._browser: Browser = ...
self._page: ChromiumPage = ... self._page: ChromiumPage = ...
self._control_session: Session = ...
self.address: str = ... self.address: str = ...
self._tab_obj: ChromiumDriver = ... self._tab_obj: ChromiumDriver = ...
self._is_reading: bool = ... self._is_reading: bool = ...
@ -47,10 +47,6 @@ class ChromiumBase(BasePage):
self._screencast: Screencast = ... self._screencast: Screencast = ...
self._actions: ActionChains = ... self._actions: ActionChains = ...
self._listener: NetworkListener = ... self._listener: NetworkListener = ...
# self._wait_download_flag: bool = ...
# self._download_rename: str = ...
# self._when_download_file_exists: str = ...
# self._download_missions: set = ...
def _connect_browser(self, tab_id: str = None) -> None: ... def _connect_browser(self, tab_id: str = None) -> None: ...
@ -88,7 +84,10 @@ class ChromiumBase(BasePage):
timeout: float = None) -> ChromiumElement: ... timeout: float = None) -> ChromiumElement: ...
@property @property
def browser(self) -> ChromiumPage: ... def main(self) -> ChromiumPage: ...
@property
def browser(self) -> Browser: ...
@property @property
def title(self) -> str: ... def title(self) -> str: ...

View File

@ -7,6 +7,7 @@ from json import dumps, loads
from queue import Queue, Empty from queue import Queue, Empty
from threading import Thread, Event from threading import Thread, Event
from requests import get
from websocket import WebSocketTimeoutException, WebSocketException, WebSocketConnectionClosedException, \ from websocket import WebSocketTimeoutException, WebSocketException, WebSocketConnectionClosedException, \
create_connection create_connection
@ -223,3 +224,6 @@ class BrowserDriver(ChromiumDriver):
def __repr__(self): def __repr__(self):
return f"<BrowserDriver {self.id}>" return f"<BrowserDriver {self.id}>"
def get(self, url):
return get(url, headers={'Connection': 'close'})

View File

@ -8,6 +8,8 @@ from re import search
from threading import Thread from threading import Thread
from time import sleep, perf_counter from time import sleep, perf_counter
from requests import get
from .chromium_base import ChromiumBase, ChromiumPageScroll from .chromium_base import ChromiumBase, ChromiumPageScroll
from .chromium_element import ChromiumElement from .chromium_element import ChromiumElement
from .errors import ContextLossError from .errors import ContextLossError
@ -24,8 +26,10 @@ class ChromiumFrame(ChromiumBase):
page_type = str(type(page)) page_type = str(type(page))
if 'ChromiumPage' in page_type or 'WebPage' in page_type: if 'ChromiumPage' in page_type or 'WebPage' in page_type:
self._page = self._target_page = self.tab = page self._page = self._target_page = self.tab = page
self._browser = page.browser
else: # Tab、Frame else: # Tab、Frame
self._page = page.page self._page = page.page
self._browser = self._page.browser
self._target_page = page self._target_page = page
self.tab = page.tab if 'ChromiumFrame' in page_type else page self.tab = page.tab if 'ChromiumFrame' in page_type else page
@ -87,9 +91,7 @@ class ChromiumFrame(ChromiumBase):
try: try:
super()._driver_init(tab_id) super()._driver_init(tab_id)
except: except:
u = f'http://{self.address}/json' get(f'http://{self.address}/json', headers={'Connection': 'close'})
self._control_session.get(u)
self._control_session.get(u, headers={'Connection': 'close'})
super()._driver_init(tab_id) super()._driver_init(tab_id)
def _reload(self): def _reload(self):

View File

@ -6,9 +6,11 @@
from pathlib import Path from pathlib import Path
from time import perf_counter, sleep from time import perf_counter, sleep
from .browser_download_manager import BrowserDownloadManager from requests import get
from .browser import Browser
from .chromium_base import ChromiumBase, Timeout from .chromium_base import ChromiumBase, Timeout
from .chromium_driver import ChromiumDriver, BrowserDriver from .chromium_driver import ChromiumDriver
from .chromium_tab import ChromiumTab from .chromium_tab import ChromiumTab
from .commons.browser import connect_browser from .commons.browser import connect_browser
from .configs.chromium_options import ChromiumOptions from .configs.chromium_options import ChromiumOptions
@ -27,9 +29,7 @@ class ChromiumPage(ChromiumBase):
:param timeout: 超时时间 :param timeout: 超时时间
""" """
self._page = self self._page = self
self._frames = {}
super().__init__(addr_driver_opts, tab_id) super().__init__(addr_driver_opts, tab_id)
self._dl_mgr = BrowserDownloadManager(self)
self.set.timeouts(implicit=timeout) self.set.timeouts(implicit=timeout)
def _set_start_options(self, addr_driver_opts, none): def _set_start_options(self, addr_driver_opts, none):
@ -79,9 +79,7 @@ class ChromiumPage(ChromiumBase):
if not self._tab_obj: # 不是传入driver的情况 if not self._tab_obj: # 不是传入driver的情况
connect_browser(self._driver_options) connect_browser(self._driver_options)
if not tab_id: if not tab_id:
u = f'http://{self.address}/json' json = get(f'http://{self.address}/json', headers={'Connection': 'close'}).json()
json = self._control_session.get(u).json()
self._control_session.get(u, headers={'Connection': 'close'})
tab_id = [i['id'] for i in json if i['type'] == 'page'] tab_id = [i['id'] for i in json if i['type'] == 'page']
if not tab_id: if not tab_id:
raise BrowserConnectError('浏览器连接失败,可能是浏览器版本原因。') raise BrowserConnectError('浏览器连接失败,可能是浏览器版本原因。')
@ -95,10 +93,8 @@ class ChromiumPage(ChromiumBase):
def _page_init(self): def _page_init(self):
"""浏览器相关设置""" """浏览器相关设置"""
u = f'http://{self.address}/json/version' ws = get(f'http://{self.address}/json/version', headers={'Connection': 'close'}).json()['webSocketDebuggerUrl']
ws = self._control_session.get(u).json()['webSocketDebuggerUrl'] self._browser = Browser(ws.split('/')[-1], self)
self._control_session.get(u, headers={'Connection': 'close'})
self._browser_driver = BrowserDriver(ws.split('/')[-1], 'browser', self.address)
self._alert = Alert() self._alert = Alert()
self._tab_obj.set_listener('Page.javascriptDialogOpening', self._on_alert_open) self._tab_obj.set_listener('Page.javascriptDialogOpening', self._on_alert_open)
@ -107,32 +103,20 @@ class ChromiumPage(ChromiumBase):
self._rect = None self._rect = None
self._main_tab = self.tab_id self._main_tab = self.tab_id
self._process_id = None
r = self.browser_driver.call_method('SystemInfo.getProcessInfo')
if 'processInfo' not in r:
return None
for i in r['processInfo']:
if i['type'] == 'browser':
self._process_id = i['id']
break
@property @property
def browser_driver(self): def browser(self):
"""返回用于控制浏览器cdp的driver""" """返回用于控制浏览器cdp的driver"""
return self._browser_driver return self._browser
@property @property
def tabs_count(self): def tabs_count(self):
"""返回标签页数量""" """返回标签页数量"""
return len(self.tabs) return self.browser.tabs_count
@property @property
def tabs(self): def tabs(self):
"""返回所有标签页id组成的列表""" """返回所有标签页id组成的列表"""
u = f'http://{self.address}/json' return self.browser.tabs
j = self._control_session.get(u).json() # 不要改用cdp
self._control_session.get(u, headers={'Connection': 'close'})
return [i['id'] for i in j if i['type'] == 'page']
@property @property
def main_tab(self): def main_tab(self):
@ -146,7 +130,7 @@ class ChromiumPage(ChromiumBase):
@property @property
def process_id(self): def process_id(self):
"""返回浏览器进程id""" """返回浏览器进程id"""
return self._process_id return self.browser.process_id
@property @property
def set(self): def set(self):
@ -183,19 +167,7 @@ class ChromiumPage(ChromiumBase):
:param single: 是否返回首个结果的id为False返回所有信息 :param single: 是否返回首个结果的id为False返回所有信息
:return: tab id或tab dict :return: tab id或tab dict
""" """
u = f'http://{self.address}/json' return self._browser.find_tabs(title, url, tab_type, single)
tabs = self._control_session.get(u).json() # 不要改用cdp
self._control_session.get(u, headers={'Connection': 'close'})
if isinstance(tab_type, str):
tab_type = {tab_type}
elif isinstance(tab_type, (list, tuple, set)):
tab_type = set(tab_type)
elif tab_type is not None:
raise TypeError('tab_type只能是set、list、tuple、str、None。')
r = [i for i in tabs if ((title is None or title in i['title']) and (url is None or url in i['url'])
and (tab_type is None or i['type'] in tab_type))]
return r[0]['id'] if r and single else r
def _new_tab(self, url=None, switch_to=False): def _new_tab(self, url=None, switch_to=False):
"""新建一个标签页,该标签页在最后面 """新建一个标签页,该标签页在最后面
@ -265,7 +237,7 @@ class ChromiumPage(ChromiumBase):
tab_id = self.latest_tab tab_id = self.latest_tab
if activate: if activate:
self._control_session.get(f'http://{self.address}/json/activate/{tab_id}') self.browser.activate_tab(tab_id)
if tab_id == self.tab_id: if tab_id == self.tab_id:
return return
@ -305,7 +277,7 @@ class ChromiumPage(ChromiumBase):
self.driver.stop() self.driver.stop()
for tab in tabs: for tab in tabs:
self._control_session.get(f'http://{self.address}/json/close/{tab}') self.browser.close_tab(tab)
while len(self.tabs) != end_len: while len(self.tabs) != end_len:
sleep(.1) sleep(.1)
@ -345,19 +317,7 @@ class ChromiumPage(ChromiumBase):
def quit(self): def quit(self):
"""关闭浏览器""" """关闭浏览器"""
self._tab_obj.call_method('Browser.close') self.browser.quit()
self._tab_obj.stop()
if self.process_id:
from os import popen
from platform import system
txt = f'tasklist | findstr {self.process_id}' if system().lower() == 'windows' \
else f'ps -ef | grep {self.process_id}'
while True:
p = popen(txt)
if f' {self.process_id} ' not in p.read():
break
sleep(.2)
def _on_alert_close(self, **kwargs): def _on_alert_close(self, **kwargs):
"""alert关闭时触发的方法""" """alert关闭时触发的方法"""
@ -448,7 +408,7 @@ class ChromiumTabRect(object):
def _get_browser_rect(self): def _get_browser_rect(self):
"""获取浏览器范围信息""" """获取浏览器范围信息"""
return self._page.browser_driver.call_method('Browser.getWindowForTarget', targetId=self._page.tab_id)['bounds'] return self._page.browser.get_window_bounds()
class Alert(object): class Alert(object):

View File

@ -3,9 +3,9 @@
@Author : g1879 @Author : g1879
@Contact : g1879@qq.com @Contact : g1879@qq.com
""" """
from typing import Union, Tuple, List from typing import Union, Tuple, List, Optional
from .browser_download_manager import BrowserDownloadManager from .browser import Browser
from .chromium_base import ChromiumBase from .chromium_base import ChromiumBase
from .chromium_driver import ChromiumDriver from .chromium_driver import ChromiumDriver
from .chromium_tab import ChromiumTab from .chromium_tab import ChromiumTab
@ -21,13 +21,10 @@ class ChromiumPage(ChromiumBase):
tab_id: str = None, tab_id: str = None,
timeout: float = None): timeout: float = None):
self._driver_options: ChromiumOptions = ... self._driver_options: ChromiumOptions = ...
self._process_id: str = ...
self._dl_mgr: BrowserDownloadManager = ...
self._main_tab: str = ... self._main_tab: str = ...
self._alert: Alert = ... self._alert: Alert = ...
self._browser_driver: ChromiumDriver = ... self._browser: Browser = ...
self._rect: ChromiumTabRect = ... self._rect: ChromiumTabRect = ...
self._frames: dict = ...
def _connect_browser(self, def _connect_browser(self,
addr_driver_opts: Union[str, ChromiumDriver] = None, addr_driver_opts: Union[str, ChromiumDriver] = None,
@ -38,7 +35,7 @@ class ChromiumPage(ChromiumBase):
def _page_init(self) -> None: ... def _page_init(self) -> None: ...
@property @property
def browser_driver(self) -> ChromiumDriver: ... def browser(self) -> Browser: ...
@property @property
def tabs_count(self) -> int: ... def tabs_count(self) -> int: ...
@ -59,7 +56,7 @@ class ChromiumPage(ChromiumBase):
def latest_tab(self) -> str: ... def latest_tab(self) -> str: ...
@property @property
def process_id(self) -> Union[None, int]: ... def process_id(self) -> Optional[int]: ...
@property @property
def set(self) -> ChromiumPageSetter: ... def set(self) -> ChromiumPageSetter: ...

View File

@ -22,6 +22,7 @@ class ChromiumTab(ChromiumBase):
:param tab_id: 要控制的标签页id不指定默认为激活的 :param tab_id: 要控制的标签页id不指定默认为激活的
""" """
self._page = page self._page = page
self._browser = page.browser
super().__init__(page.address, tab_id, page.timeout) super().__init__(page.address, tab_id, page.timeout)
def _set_runtime_settings(self): def _set_runtime_settings(self):
@ -68,6 +69,7 @@ class WebPageTab(SessionPage, ChromiumTab):
:param tab_id: 要控制的标签页id :param tab_id: 要控制的标签页id
""" """
self._page = page self._page = page
self._browser = page.browser
self.address = page.address self.address = page.address
self._debug = page._debug self._debug = page._debug
self._debug_recorder = page._debug_recorder self._debug_recorder = page._debug_recorder

View File

@ -7,7 +7,7 @@ from typing import Union, Tuple, Any, List
from requests import Session, Response from requests import Session, Response
from waiter import ChromiumTabWaiter from .browser import Browser
from .chromium_base import ChromiumBase from .chromium_base import ChromiumBase
from .chromium_element import ChromiumElement from .chromium_element import ChromiumElement
from .chromium_frame import ChromiumFrame from .chromium_frame import ChromiumFrame
@ -16,6 +16,7 @@ from .session_element import SessionElement
from .session_page import SessionPage from .session_page import SessionPage
from .setter import TabSetter from .setter import TabSetter
from .setter import WebPageTabSetter from .setter import WebPageTabSetter
from .waiter import ChromiumTabWaiter
from .web_page import WebPage from .web_page import WebPage
@ -23,6 +24,7 @@ class ChromiumTab(ChromiumBase):
def __init__(self, page: ChromiumPage, tab_id: str = None): def __init__(self, page: ChromiumPage, tab_id: str = None):
self._page: ChromiumPage = ... self._page: ChromiumPage = ...
self._browser: Browser = ...
def _set_runtime_settings(self) -> None: ... def _set_runtime_settings(self) -> None: ...
@ -44,6 +46,7 @@ class ChromiumTab(ChromiumBase):
class WebPageTab(SessionPage, ChromiumTab): class WebPageTab(SessionPage, ChromiumTab):
def __init__(self, page: WebPage, tab_id: str): def __init__(self, page: WebPage, tab_id: str):
self._page: WebPage = ... self._page: WebPage = ...
self._browser: Browser = ...
self._mode: str = ... self._mode: str = ...
self._has_driver = ... self._has_driver = ...
self._has_session = ... self._has_session = ...

View File

@ -155,9 +155,7 @@ def test_connect(ip, port):
end_time = perf_counter() + 30 end_time = perf_counter() + 30
while perf_counter() < end_time: while perf_counter() < end_time:
try: try:
u = f'http://{ip}:{port}/json' tabs = requests_get(f'http://{ip}:{port}/json', timeout=10, headers={'Connection': 'close'}, proxies={'http': None, 'https': None}).json()
tabs = requests_get(u, timeout=10, proxies={'http': None, 'https': None}).json()
requests_get(u, headers={'Connection': 'close'}, proxies={'http': None, 'https': None})
for tab in tabs: for tab in tabs:
if tab['type'] == 'page': if tab['type'] == 'page':
return return

View File

@ -175,7 +175,7 @@ class ChromiumPageSetter(TabSetter):
tab_or_id = self._page.tab_id tab_or_id = self._page.tab_id
elif not isinstance(tab_or_id, str): # 传入Tab对象 elif not isinstance(tab_or_id, str): # 传入Tab对象
tab_or_id = tab_or_id.tab_id tab_or_id = tab_or_id.tab_id
self._page._control_session.get(f'http://{self._page.address}/json/activate/{tab_or_id}') self._page.browser.activate_tab(tab_or_id)
class SessionPageSetter(object): class SessionPageSetter(object):

View File

@ -237,20 +237,20 @@ class ChromiumPageWaiter(ChromiumTabWaiter):
:return: 是否等待成功 :return: 是否等待成功
""" """
if not timeout: if not timeout:
while self._driver._dl_mgr._missions: while self._driver.browser._dl_mgr._missions:
sleep(.5) sleep(.5)
return True return True
else: else:
end_time = perf_counter() + timeout end_time = perf_counter() + timeout
while end_time > perf_counter(): while end_time > perf_counter():
if not self._driver._dl_mgr._missions: if not self._driver.browser._dl_mgr._missions:
return True return True
sleep(.5) sleep(.5)
if self._driver._dl_mgr._missions: if self._driver.browser._dl_mgr._missions:
if cancel_if_timeout: if cancel_if_timeout:
for m in list(self._driver._dl_mgr._missions.values()): for m in list(self._driver.browser._dl_mgr._missions.values()):
m.cancel() m.cancel()
return False return False
else: else:

View File

@ -6,7 +6,6 @@
from requests import Session from requests import Session
from .base import BasePage from .base import BasePage
from .browser_download_manager import BrowserDownloadManager
from .chromium_base import ChromiumBase, Timeout from .chromium_base import ChromiumBase, Timeout
from .chromium_driver import ChromiumDriver from .chromium_driver import ChromiumDriver
from .chromium_page import ChromiumPage from .chromium_page import ChromiumPage
@ -53,7 +52,6 @@ class WebPage(SessionPage, ChromiumPage, BasePage):
self._set_runtime_settings() self._set_runtime_settings()
self._connect_browser() self._connect_browser()
self._create_session() self._create_session()
self._dl_mgr = BrowserDownloadManager(self)
self.set.timeouts(implicit=timeout) self.set.timeouts(implicit=timeout)
def _set_start_options(self, dr_opt, se_opt): def _set_start_options(self, dr_opt, se_opt):