diff --git a/DrissionPage/_configs/chromium_options.py b/DrissionPage/_configs/chromium_options.py index b35c8f9..cdcf8c8 100644 --- a/DrissionPage/_configs/chromium_options.py +++ b/DrissionPage/_configs/chromium_options.py @@ -31,11 +31,11 @@ class ChromiumOptions(object): self._download_path = om.paths.get('download_path', '') self._arguments = options.get('arguments', []) - self._binary_location = options.get('binary_location', '') + self._browser_path = options.get('browser_path', '') self._extensions = options.get('extensions', []) - self._prefs = options.get('experimental_options', {}).get('prefs', {}) + self._prefs = options.get('prefs', {}) self._debugger_address = options.get('debugger_address', None) - self._page_load_strategy = options.get('page_load_strategy', 'normal') + self._load_mode = options.get('load_mode', 'normal') self._proxy = om.proxies.get('http', None) self._system_user_path = options.get('system_user_path', False) self._existing_only = options.get('is_existing_only', False) @@ -64,14 +64,14 @@ class ChromiumOptions(object): return self.ini_path = None - self._binary_location = "chrome" + self._browser_path = "chrome" self._arguments = [] self._download_path = '' self._extensions = [] self._prefs = {} self._timeouts = {'implicit': 10, 'pageLoad': 30, 'script': 30} self._debugger_address = '127.0.0.1:9222' - self._page_load_strategy = 'normal' + self._load_mode = 'normal' self._proxy = None self._auto_port = False self._system_user_path = False @@ -85,7 +85,7 @@ class ChromiumOptions(object): @property def browser_path(self): """浏览器启动文件路径""" - return self._binary_location + return self._browser_path @property def user_data_path(self): @@ -98,9 +98,9 @@ class ChromiumOptions(object): return self._user @property - def page_load_strategy(self): + def load_mode(self): """返回页面加载策略,'normal', 'eager', 'none'""" - return self._page_load_strategy + return self._load_mode @property def timeouts(self): @@ -246,7 +246,7 @@ class ChromiumOptions(object): self._user = user return self - def set_headless(self, on_off=True): + def headless(self, on_off=True): """设置是否隐藏浏览器界面 :param on_off: 开或关 :return: 当前对象 @@ -254,7 +254,7 @@ class ChromiumOptions(object): on_off = 'new' if on_off else 'false' return self.set_argument('--headless', on_off) - def set_no_imgs(self, on_off=True): + def no_imgs(self, on_off=True): """设置是否加载图片 :param on_off: 开或关 :return: 当前对象 @@ -262,7 +262,7 @@ class ChromiumOptions(object): on_off = None if on_off else False return self.set_argument('--blink-settings=imagesEnabled=false', on_off) - def set_no_js(self, on_off=True): + def no_js(self, on_off=True): """设置是否禁用js :param on_off: 开或关 :return: 当前对象 @@ -270,7 +270,7 @@ class ChromiumOptions(object): on_off = None if on_off else False return self.set_argument('--disable-javascript', on_off) - def set_mute(self, on_off=True): + def mute(self, on_off=True): """设置是否静音 :param on_off: 开或关 :return: 当前对象 @@ -278,6 +278,14 @@ class ChromiumOptions(object): on_off = None if on_off else False return self.set_argument('--mute-audio', on_off) + def ignore_certificate_errors(self, on_off=True): + """设置是否忽略证书错误 + :param on_off: 开或关 + :return: 当前对象 + """ + on_off = None if on_off else False + return self.set_argument('--ignore-certificate-errors', on_off) + def set_user_agent(self, user_agent): """设置user agent :param user_agent: user agent文本 @@ -293,8 +301,8 @@ class ChromiumOptions(object): self._proxy = proxy return self.set_argument('--proxy-server', proxy) - def set_page_load_strategy(self, value): - """设置page_load_strategy,可接收 'normal', 'eager', 'none' + def set_load_mode(self, value): + """设置load_mode,可接收 'normal', 'eager', 'none' normal:默认情况下使用, 等待所有资源下载完成 eager:DOM访问已准备就绪, 但其他资源 (如图像) 可能仍在加载中 none:完全不阻塞 @@ -302,8 +310,8 @@ class ChromiumOptions(object): :return: 当前对象 """ if value not in ('normal', 'eager', 'none'): - raise ValueError("只能选择'normal', 'eager', 'none'。") - self._page_load_strategy = value.lower() + raise ValueError("只能选择 'normal', 'eager', 'none'。") + self._load_mode = value.lower() return self def set_paths(self, browser_path=None, local_port=None, debugger_address=None, download_path=None, @@ -360,7 +368,7 @@ class ChromiumOptions(object): :param path: 浏览器路径 :return: 当前对象 """ - self._binary_location = str(path) + self._browser_path = str(path) self._auto_port = False return self @@ -445,7 +453,7 @@ class ChromiumOptions(object): om = OptionsManager(self.ini_path or str(Path(__file__).parent / 'configs.ini')) # 设置chrome_options - attrs = ('debugger_address', 'binary_location', 'arguments', 'extensions', 'user', 'page_load_strategy', + attrs = ('debugger_address', 'browser_path', 'arguments', 'extensions', 'user', 'load_mode', 'auto_port', 'system_user_path', 'existing_only') for i in attrs: om.set_item('chrome_options', i, self.__getattribute__(f'_{i}')) @@ -459,9 +467,7 @@ class ChromiumOptions(object): om.set_item('timeouts', 'page_load', self._timeouts['pageLoad']) om.set_item('timeouts', 'script', self._timeouts['script']) # 设置prefs - eo = om.chrome_options.get('experimental_options', {}) - eo['prefs'] = self._prefs - om.set_item('chrome_options', 'experimental_options', eo) + om.set_item('chrome_options', 'prefs', self._prefs) path = str(path) om.save(path) @@ -472,6 +478,43 @@ class ChromiumOptions(object): """保存当前配置到默认ini文件""" return self.save('default') + # ---------------即将废弃-------------- + + def set_page_load_strategy(self, value): + return self.set_load_mode(value) + + def set_headless(self, on_off=True): + """设置是否隐藏浏览器界面 + :param on_off: 开或关 + :return: 当前对象 + """ + on_off = 'new' if on_off else 'false' + return self.set_argument('--headless', on_off) + + def set_no_imgs(self, on_off=True): + """设置是否加载图片 + :param on_off: 开或关 + :return: 当前对象 + """ + on_off = None if on_off else False + return self.set_argument('--blink-settings=imagesEnabled=false', on_off) + + def set_no_js(self, on_off=True): + """设置是否禁用js + :param on_off: 开或关 + :return: 当前对象 + """ + on_off = None if on_off else False + return self.set_argument('--disable-javascript', on_off) + + def set_mute(self, on_off=True): + """设置是否静音 + :param on_off: 开或关 + :return: 当前对象 + """ + on_off = None if on_off else False + return self.set_argument('--mute-audio', on_off) + class PortFinder(object): used_port = {} diff --git a/DrissionPage/_configs/chromium_options.pyi b/DrissionPage/_configs/chromium_options.pyi index b378421..e42c6de 100644 --- a/DrissionPage/_configs/chromium_options.pyi +++ b/DrissionPage/_configs/chromium_options.pyi @@ -14,9 +14,9 @@ class ChromiumOptions(object): self._user_data_path: str = ... self._download_path: str = ... self._arguments: list = ... - self._binary_location: str = ... + self._browser_path: str = ... self._user: str = ... - self._page_load_strategy: str = ... + self._load_mode: str = ... self._timeouts: dict = ... self._proxy: str = ... self._debugger_address: str = ... @@ -41,7 +41,7 @@ class ChromiumOptions(object): def user(self) -> str: ... @property - def page_load_strategy(self) -> str: ... + def load_mode(self) -> str: ... @property def timeouts(self) -> dict: ... @@ -86,19 +86,21 @@ class ChromiumOptions(object): def set_user(self, user: str = 'Default') -> ChromiumOptions: ... - def set_headless(self, on_off: bool = True) -> ChromiumOptions: ... + def headless(self, on_off: bool = True) -> ChromiumOptions: ... - def set_no_imgs(self, on_off: bool = True) -> ChromiumOptions: ... + def no_imgs(self, on_off: bool = True) -> ChromiumOptions: ... - def set_no_js(self, on_off: bool = True) -> ChromiumOptions: ... + def no_js(self, on_off: bool = True) -> ChromiumOptions: ... - def set_mute(self, on_off: bool = True) -> ChromiumOptions: ... + def mute(self, on_off: bool = True) -> ChromiumOptions: ... def set_user_agent(self, user_agent: str) -> ChromiumOptions: ... def set_proxy(self, proxy: str) -> ChromiumOptions: ... - def set_page_load_strategy(self, value: str) -> ChromiumOptions: ... + def ignore_certificate_errors(self, on_off=True) -> ChromiumOptions: ... + + def set_load_mode(self, value: str) -> ChromiumOptions: ... def set_browser_path(self, path: Union[str, Path]) -> ChromiumOptions: ... diff --git a/DrissionPage/_configs/configs.ini b/DrissionPage/_configs/configs.ini index 0190176..e6cb8f5 100644 --- a/DrissionPage/_configs/configs.ini +++ b/DrissionPage/_configs/configs.ini @@ -3,15 +3,16 @@ download_path = [chrome_options] debugger_address = 127.0.0.1:9222 -binary_location = chrome +browser_path = chrome arguments = ['--remote-allow-origins=*', '--no-first-run', '--disable-infobars', '--disable-popup-blocking'] extensions = [] -experimental_options = {'prefs': {'profile.default_content_settings.popups': 0, 'profile.default_content_setting_values': {'notifications': 2}}} -page_load_strategy = normal +prefs = {'profile.default_content_settings.popups': 0, 'profile.default_content_setting_values': {'notifications': 2}} +load_mode = normal user = Default auto_port = False system_user_path = False is_existing_only = False +existing_only = False [session_options] headers = {'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/603.3.8 (KHTML, like Gecko) Version/10.1.2 Safari/603.3.8', 'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 'connection': 'keep-alive', 'accept-charset': 'GB2312,utf-8;q=0.7,*;q=0.7'} diff --git a/DrissionPage/_elements/chromium_element.py b/DrissionPage/_elements/chromium_element.py index f9a2d4c..bf052c7 100644 --- a/DrissionPage/_elements/chromium_element.py +++ b/DrissionPage/_elements/chromium_element.py @@ -15,12 +15,12 @@ from .._commons.locator import get_loc from .._commons.tools import get_usable_path from .._commons.web import make_absolute_link, get_ele_txt, format_html, is_js_func, offset_scroll from .._units.clicker import Clicker -from .._units.element_states import ElementStates, ShadowRootStates from .._units.ids import ShadowRootIds, ElementIds from .._units.locations import Locations from .._units.scroller import ElementScroller from .._units.select_element import SelectElement from .._units.setter import ChromiumElementSetter +from .._units.states import ElementStates, ShadowRootStates from .._units.waiter import ElementWaiter from ..errors import (ContextLossError, ElementLossError, JavaScriptError, ElementNotFoundError, CDPError, NoResourceError, AlertExistsError) diff --git a/DrissionPage/_elements/chromium_element.pyi b/DrissionPage/_elements/chromium_element.pyi index 315461e..90f3caa 100644 --- a/DrissionPage/_elements/chromium_element.pyi +++ b/DrissionPage/_elements/chromium_element.pyi @@ -14,12 +14,12 @@ from .._pages.chromium_frame import ChromiumFrame from .._pages.chromium_page import ChromiumPage from .._pages.web_page import WebPage from .._units.clicker import Clicker -from .._units.element_states import ShadowRootStates, ElementStates from .._units.ids import ElementIds, ShadowRootIds from .._units.locations import Locations from .._units.scroller import ElementScroller from .._units.select_element import SelectElement from .._units.setter import ChromiumElementSetter +from .._units.states import ShadowRootStates, ElementStates from .._units.waiter import ElementWaiter diff --git a/DrissionPage/_pages/chromium_base.py b/DrissionPage/_pages/chromium_base.py index ba10874..92c8c56 100644 --- a/DrissionPage/_pages/chromium_base.py +++ b/DrissionPage/_pages/chromium_base.py @@ -10,7 +10,6 @@ from re import findall from threading import Thread from time import perf_counter, sleep -from .._units.scroller import PageScroller from .._base.base import BasePage from .._commons.constants import ERROR, NoneElement from .._commons.locator import get_loc @@ -21,7 +20,9 @@ from .._elements.session_element import make_session_ele from .._units.action_chains import ActionChains from .._units.network_listener import NetworkListener from .._units.screencast import Screencast +from .._units.scroller import PageScroller from .._units.setter import ChromiumBaseSetter +from .._units.states import PageStates from .._units.waiter import BaseWaiter from ..errors import (ContextLossError, ElementLossError, CDPError, PageClosedError, NoRectError, AlertExistsError, GetDocumentError) @@ -44,6 +45,7 @@ class ChromiumBase(BasePage): self._screencast = None self._actions = None self._listener = None + self._states = None self._has_alert = False self._ready_state = None self._doc_got = False # 用于在LoadEventFired和FrameStoppedLoading间标记是否已获取doc @@ -69,7 +71,7 @@ class ChromiumBase(BasePage): def _d_set_runtime_settings(self): self._timeouts = Timeout(self) - self._page_load_strategy = 'normal' + self._load_mode = 'normal' def _connect_browser(self, tab_id=None): """连接浏览器,在第一次时运行 @@ -91,7 +93,7 @@ class ChromiumBase(BasePage): tab_id = tabs[0][0] self._driver_init(tab_id) - if self.ready_state == 'complete' and self._ready_state is None: + if self._js_ready_state == 'complete' and self._ready_state is None: self._get_document() self._ready_state = 'complete' if self._debug: @@ -170,7 +172,7 @@ class ChromiumBase(BasePage): self._doc_got = False self._ready_state = 'loading' self._is_loading = True - if self.page_load_strategy == 'eager': + if self._load_mode == 'eager': t = Thread(target=self._wait_to_stop) t.daemon = True t.start() @@ -199,7 +201,7 @@ class ChromiumBase(BasePage): print('在DomContentEventFired变成interactive') self._ready_state = 'interactive' - if self.page_load_strategy == 'eager': + if self._load_mode == 'eager': self.run_cdp('Page.stopLoading') if self._debug: @@ -261,9 +263,65 @@ class ChromiumBase(BasePage): if self._ready_state in ('interactive', 'complete') and self._is_loading: self.stop_loading() + # ----------挂件---------- + @property - def main(self): - return self._page + def wait(self): + """返回用于等待的对象""" + if self._wait is None: + self._wait = BaseWaiter(self) + return self._wait + + @property + def set(self): + """返回用于等待的对象""" + if self._set is None: + self._set = ChromiumBaseSetter(self) + return self._set + + @property + def screencast(self): + """返回用于录屏的对象""" + if self._screencast is None: + self._screencast = Screencast(self) + return self._screencast + + @property + def actions(self): + """返回用于执行动作链的对象""" + if self._actions is None: + self._actions = ActionChains(self) + self.wait.load_complete() + return self._actions + + @property + def listen(self): + """返回用于聆听数据包的对象""" + if self._listener is None: + self._listener = NetworkListener(self) + return self._listener + + @property + def states(self): + """返回用于获取状态信息的对象""" + if self._states is None: + self._states = PageStates(self) + return self._states + + @property + def scroll(self): + """返回用于滚动滚动条的对象""" + self.wait.load_complete() + if self._scroll is None: + self._scroll = PageScroller(self) + return self._scroll + + @property + def timeouts(self): + """返回timeouts设置""" + return self._timeouts + + # ----------挂件---------- @property def browser(self): @@ -276,20 +334,6 @@ class ChromiumBase(BasePage): raise RuntimeError('浏览器已关闭或链接已断开。') return self._driver - @property - def is_loading(self): - """返回页面是否正在加载状态""" - return self._is_loading - - @property - def is_alive(self): - """返回页面对象是否仍然可用""" - try: - self.run_cdp('Page.getLayoutMetrics') - return True - except PageClosedError: - return False - @property def title(self): """返回当前页面title""" @@ -329,16 +373,6 @@ class ChromiumBase(BasePage): """返回当前标签页id""" return self.driver.id if not self.driver._stopped.is_set() else '' - @property - def ready_state(self): - """返回当前页面加载状态,'loading' 'interactive' 'complete','timeout' 表示可能有弹出框""" - try: - return self.run_cdp('Runtime.evaluate', expression='document.readyState;', _timeout=3)['result']['value'] - except ContextLossError: - return None - except TimeoutError: - return 'timeout' - @property def size(self): """返回页面总宽高,格式:(宽, 高)""" @@ -351,74 +385,36 @@ class ChromiumBase(BasePage): return self.run_js_loaded('return document.activeElement;') @property - def page_load_strategy(self): + def load_mode(self): """返回页面加载策略,有3种:'none'、'normal'、'eager'""" - return self._page_load_strategy + return self._load_mode @property def user_agent(self): """返回user agent""" return self.run_cdp('Runtime.evaluate', expression='navigator.userAgent;')['result']['value'] - @property - def scroll(self): - """返回用于滚动滚动条的对象""" - self.wait.load_complete() - if self._scroll is None: - self._scroll = PageScroller(self) - return self._scroll - - @property - def timeouts(self): - """返回timeouts设置""" - return self._timeouts - @property def upload_list(self): """返回等待上传文件列表""" return self._upload_list - @property - def wait(self): - """返回用于等待的对象""" - if self._wait is None: - self._wait = BaseWaiter(self) - return self._wait - - @property - def set(self): - """返回用于等待的对象""" - if self._set is None: - self._set = ChromiumBaseSetter(self) - return self._set - - @property - def screencast(self): - """返回用于录屏的对象""" - if self._screencast is None: - self._screencast = Screencast(self) - return self._screencast - - @property - def actions(self): - """返回用于执行动作链的对象""" - if self._actions is None: - self._actions = ActionChains(self) - self.wait.load_complete() - return self._actions - - @property - def listen(self): - """返回用于聆听数据包的对象""" - if self._listener is None: - self._listener = NetworkListener(self) - return self._listener - @property def has_alert(self): """返回是否存在提示框""" return self._has_alert + @property + def _js_ready_state(self): + """返回js获取的ready state信息""" + try: + return self.run_cdp('Runtime.evaluate', expression='document.readyState;', + _timeout=3)['result']['value'] + except ContextLossError: + return None + except TimeoutError: + return 'timeout' + def run_cdp(self, cmd, **cmd_args): """执行Chrome DevTools Protocol语句 :param cmd: 协议项目 @@ -865,7 +861,7 @@ class ChromiumBase(BasePage): :param timeout: 超时时间 :return: 是否成功,超时返回False """ - if self.page_load_strategy == 'none': + if self._load_mode == 'none': return True timeout = timeout if timeout is not None else self.timeouts.page_load @@ -873,8 +869,8 @@ class ChromiumBase(BasePage): while perf_counter() < end_time: if self._ready_state == 'complete': return True - elif self.page_load_strategy == 'eager' and self._ready_state in ('interactive', - 'complete') and not self._is_loading: + elif self._load_mode == 'eager' and self._ready_state in ('interactive', + 'complete') and not self._is_loading: return True sleep(.1) @@ -913,7 +909,7 @@ class ChromiumBase(BasePage): self.stop_loading() continue - if self.page_load_strategy == 'none': + if self._load_mode == 'none': return True yu = end_time - perf_counter() @@ -1015,6 +1011,25 @@ class ChromiumBase(BasePage): f.write(png) return str(path.absolute()) + # --------------------即将废弃--------------------- + + @property + def page_load_strategy(self): + return self._load_mode + + @property + def is_alive(self): + return self.states.is_alive + + @property + def is_loading(self): + """返回页面是否正在加载状态""" + return self._is_loading + + @property + def ready_state(self): + return self._ready_state + class Timeout(object): """用于保存d模式timeout信息的类""" diff --git a/DrissionPage/_pages/chromium_base.pyi b/DrissionPage/_pages/chromium_base.pyi index 8d3a1ca..41caec9 100644 --- a/DrissionPage/_pages/chromium_base.pyi +++ b/DrissionPage/_pages/chromium_base.pyi @@ -19,6 +19,7 @@ from .._units.network_listener import NetworkListener from .._units.screencast import Screencast from .._units.scroller import Scroller, PageScroller from .._units.setter import ChromiumBaseSetter +from .._units.states import PageStates from .._units.waiter import BaseWaiter @@ -37,7 +38,7 @@ class ChromiumBase(BasePage): self._timeouts: Timeout = ... self._first_run: bool = ... self._is_loading: bool = ... - self._page_load_strategy: str = ... + self._load_mode: str = ... self._scroll: Scroller = ... self._url: str = ... self._root_id: str = ... @@ -48,6 +49,7 @@ class ChromiumBase(BasePage): self._screencast: Screencast = ... self._actions: ActionChains = ... self._listener: NetworkListener = ... + self._states: PageStates = ... self._alert: Alert = ... self._has_alert: bool = ... self._doc_got: bool = ... @@ -87,7 +89,7 @@ class ChromiumBase(BasePage): timeout: float = None) -> ChromiumElement: ... @property - def main(self) -> ChromiumPage: ... + def _js_ready_state(self) -> str: ... @property def browser(self) -> Browser: ... @@ -98,12 +100,6 @@ class ChromiumBase(BasePage): @property def driver(self) -> ChromiumDriver: ... - @property - def is_loading(self) -> bool: ... - - @property - def is_alive(self) -> bool: ... - @property def url(self) -> str: ... @@ -122,9 +118,6 @@ class ChromiumBase(BasePage): @property def tab_id(self) -> str: ... - @property - def ready_state(self) -> Union[str, None]: ... - @property def size(self) -> Tuple[int, int]: ... @@ -132,7 +125,7 @@ class ChromiumBase(BasePage): def active_ele(self) -> ChromiumElement: ... @property - def page_load_strategy(self) -> str: ... + def load_mode(self) -> str: ... @property def user_agent(self) -> str: ... @@ -161,6 +154,9 @@ class ChromiumBase(BasePage): @property def listen(self) -> NetworkListener: ... + @property + def states(self) -> PageStates: ... + @property def has_alert(self) -> bool: ... diff --git a/DrissionPage/_pages/chromium_frame.py b/DrissionPage/_pages/chromium_frame.py index 5e21623..c0e15d1 100644 --- a/DrissionPage/_pages/chromium_frame.py +++ b/DrissionPage/_pages/chromium_frame.py @@ -13,6 +13,7 @@ from .._units.ids import FrameIds from .._units.rect import FrameRect from .._units.scroller import FrameScroller from .._units.setter import ChromiumFrameSetter +from .._units.states import FrameStates from .._units.waiter import FrameWaiter from ..errors import ContextLossError, ElementLossError, GetDocumentError, PageClosedError @@ -57,6 +58,7 @@ class ChromiumFrame(ChromiumBase): end_time = perf_counter() + 5 while perf_counter() < end_time and self.url == 'about:blank': sleep(.1) + self._rect = None def __call__(self, loc_or_str, timeout=None): """在内部查找元素 @@ -78,7 +80,7 @@ class ChromiumFrame(ChromiumBase): self._timeouts = copy(self._target_page.timeouts) self.retry_times = self._target_page.retry_times self.retry_interval = self._target_page.retry_interval - self._page_load_strategy = self._target_page.page_load_strategy if not self._is_diff_domain else 'normal' + self._load_mode = self._target_page._load_mode if not self._is_diff_domain else 'normal' self._download_path = self._target_page.download_path def _driver_init(self, tab_id, is_init=True): @@ -261,6 +263,46 @@ class ChromiumFrame(ChromiumBase): if self._debug: print(f'{self._frame_id}执行FrameDetached完毕') + # ----------挂件---------- + + @property + def scroll(self): + """返回用于等待的对象""" + self.wait.load_complete() + if self._scroll is None: + self._scroll = FrameScroller(self) + return self._scroll + + @property + def set(self): + """返回用于等待的对象""" + if self._set is None: + self._set = ChromiumFrameSetter(self) + return self._set + + @property + def states(self): + """返回用于获取状态信息的对象""" + if self._states is None: + self._states = FrameStates(self) + return self._states + + @property + def wait(self): + """返回用于等待的对象""" + if self._wait is None: + self._wait = FrameWaiter(self) + return self._wait + + @property + def rect(self): + """返回获取坐标和大小的对象""" + if self._rect is None: + self._rect = FrameRect(self) + return self._rect + + # ----------挂件---------- + @property def page(self): return self._page @@ -326,11 +368,6 @@ class ChromiumFrame(ChromiumBase): """返回frame元素大小""" return self.frame_ele.size - @property - def rect(self): - """返回获取坐标和大小的对象""" - return FrameRect(self) - @property def active_ele(self): """返回当前焦点所在元素""" @@ -356,59 +393,6 @@ class ChromiumFrame(ChromiumBase): """返回frame的css selector绝对路径""" return self.frame_ele.css_path - @property - def ready_state(self): - """返回当前页面加载状态,'loading' 'interactive' 'complete'""" - if self._is_diff_domain: - try: - return super().ready_state - except: - return 'complete' - - else: - end_time = perf_counter() + 3 - while self.is_alive and perf_counter() < end_time: - try: - return self.doc_ele.run_js('return this.readyState;') - except ContextLossError: - try: - node = self.run_cdp('DOM.describeNode', backendNodeId=self.frame_ele.ids.backend_id)['node'] - doc = ChromiumElement(self._target_page, backend_id=node['contentDocument']['backendNodeId']) - return doc.run_js('return this.readyState;') - except: - pass - - sleep(.1) - - @property - def is_alive(self): - """返回是否仍可用""" - return self.states.is_alive - - @property - def scroll(self): - """返回用于等待的对象""" - return FrameScroller(self) - - @property - def set(self): - """返回用于等待的对象""" - if self._set is None: - self._set = ChromiumFrameSetter(self) - return self._set - - @property - def states(self): - """返回用于获取状态信息的对象""" - return self.frame_ele.states - - @property - def wait(self): - """返回用于等待的对象""" - if self._wait is None: - self._wait = FrameWaiter(self) - return self._wait - @property def tab_id(self): """返回frame所在tab的id""" @@ -418,6 +402,23 @@ class ChromiumFrame(ChromiumBase): def download_path(self): return self._download_path + @property + def _js_ready_state(self): + """返回当前页面加载状态,'loading' 'interactive' 'complete'""" + if self._is_diff_domain: + return super()._js_ready_state + + else: + try: + return self.doc_ele.run_js('return this.readyState;') + except ContextLossError: + try: + node = self.run_cdp('DOM.describeNode', backendNodeId=self.frame_ele.ids.backend_id)['node'] + doc = ChromiumElement(self._target_page, backend_id=node['contentDocument']['backendNodeId']) + return doc.run_js('return this.readyState;') + except: + return None + def refresh(self): """刷新frame页面""" self.doc_ele.run_js('this.location.reload();') @@ -644,3 +645,10 @@ class ChromiumFrame(ChromiumBase): def _is_inner_frame(self): """返回当前frame是否同域""" return self._frame_id in str(self._target_page.run_cdp('Page.getFrameTree')['frameTree']) + + # ----------------即将废弃----------------- + + @property + def is_alive(self): + """返回是否仍可用""" + return self.states.is_alive diff --git a/DrissionPage/_pages/chromium_frame.pyi b/DrissionPage/_pages/chromium_frame.pyi index facbb5f..cfaba9e 100644 --- a/DrissionPage/_pages/chromium_frame.pyi +++ b/DrissionPage/_pages/chromium_frame.pyi @@ -11,7 +11,7 @@ from .chromium_page import ChromiumPage from .chromium_tab import ChromiumTab from .web_page import WebPage from .._elements.chromium_element import ChromiumElement -from .._units.element_states import ElementStates +from .._units.states import FrameStates from .._units.ids import FrameIds from .._units.locations import Locations from .._units.rect import FrameRect @@ -32,9 +32,10 @@ class ChromiumFrame(ChromiumBase): self._doc_ele: ChromiumElement = ... self._is_diff_domain: bool = ... self.doc_ele: ChromiumElement = ... - self._states: ElementStates = ... + self._states: FrameStates = ... self._ids: FrameIds = ... self._is_init_get_doc: bool = ... + self._rect: FrameRect = ... def __call__(self, loc_or_str: Union[Tuple[str, str], str], @@ -112,12 +113,6 @@ class ChromiumFrame(ChromiumBase): @property def css_path(self) -> str: ... - @property - def ready_state(self) -> str: ... - - @property - def is_alive(self) -> bool: ... - @property def scroll(self) -> FrameScroller: ... @@ -125,7 +120,7 @@ class ChromiumFrame(ChromiumBase): def set(self) -> ChromiumFrameSetter: ... @property - def states(self) -> ElementStates: ... + def states(self) -> FrameStates: ... @property def wait(self) -> FrameWaiter: ... diff --git a/DrissionPage/_pages/chromium_page.py b/DrissionPage/_pages/chromium_page.py index 87c7c10..bd2cdd5 100644 --- a/DrissionPage/_pages/chromium_page.py +++ b/DrissionPage/_pages/chromium_page.py @@ -87,7 +87,7 @@ class ChromiumPage(ChromiumBase): implicit=self._driver_options.timeouts['implicit']) if self._driver_options.timeouts['implicit'] is not None: self._timeout = self._driver_options.timeouts['implicit'] - self._page_load_strategy = self._driver_options.page_load_strategy + self._load_mode = self._driver_options.load_mode self._download_path = str(Path(self._driver_options.download_path).absolute()) def _page_init(self): @@ -95,6 +95,32 @@ class ChromiumPage(ChromiumBase): self._rect = None self._browser.connect_to_page() + # ----------挂件---------- + + @property + def set(self): + """返回用于等待的对象""" + if self._set is None: + self._set = ChromiumPageSetter(self) + return self._set + + @property + def rect(self): + """返回保存窗口方位信息的对象""" + self.wait.load_complete() + if self._rect is None: + self._rect = TabRect(self) + return self._rect + + @property + def wait(self): + """返回用于等待的对象""" + if self._wait is None: + self._wait = PageWaiter(self) + return self._wait + + # ----------挂件---------- + @property def browser(self): """返回用于控制浏览器cdp的driver""" @@ -120,28 +146,6 @@ class ChromiumPage(ChromiumBase): """返回浏览器进程id""" return self.browser.process_id - @property - def set(self): - """返回用于等待的对象""" - if self._set is None: - self._set = ChromiumPageSetter(self) - return self._set - - @property - def rect(self): - """返回保存窗口方位信息的对象""" - self.wait.load_complete() - if self._rect is None: - self._rect = TabRect(self) - return self._rect - - @property - def wait(self): - """返回用于等待的对象""" - if self._wait is None: - self._wait = PageWaiter(self) - return self._wait - def get_tab(self, tab_id=None): """获取一个标签页对象 :param tab_id: 要获取的标签页id,为None时获取当前tab diff --git a/DrissionPage/_pages/chromium_tab.py b/DrissionPage/_pages/chromium_tab.py index 66edc32..8f8cc7c 100644 --- a/DrissionPage/_pages/chromium_tab.py +++ b/DrissionPage/_pages/chromium_tab.py @@ -32,7 +32,7 @@ class ChromiumTab(ChromiumBase): self._timeouts = copy(self.page.timeouts) self.retry_times = self.page.retry_times self.retry_interval = self.page.retry_interval - self._page_load_strategy = self.page.page_load_strategy + self._load_mode = self.page._load_mode self._download_path = self.page.download_path def close(self): @@ -90,6 +90,13 @@ class WebPageTab(SessionPage, ChromiumTab, BasePage): elif self._mode == 's': return super().__call__(loc_or_str) + @property + def set(self): + """返回用于等待的对象""" + if self._set is None: + self._set = WebPageTabSetter(self) + return self._set + @property def url(self): """返回当前url""" @@ -175,13 +182,6 @@ class WebPageTab(SessionPage, ChromiumTab, BasePage): """ self.set.timeouts(implicit=second) - @property - def set(self): - """返回用于等待的对象""" - if self._set is None: - self._set = WebPageTabSetter(self) - return self._set - def get(self, url, show_errmsg=False, retry=None, interval=None, timeout=None, **kwargs): """跳转到一个url :param url: 目标url diff --git a/DrissionPage/_units/ids.pyi b/DrissionPage/_units/ids.pyi index 20d4271..907005d 100644 --- a/DrissionPage/_units/ids.pyi +++ b/DrissionPage/_units/ids.pyi @@ -5,8 +5,8 @@ """ from typing import Union -from .._pages.chromium_frame import ChromiumFrame from .._elements.chromium_element import ChromiumElement, ChromiumShadowRoot +from .._pages.chromium_frame import ChromiumFrame class ShadowRootIds(object): diff --git a/DrissionPage/_units/rect.py b/DrissionPage/_units/rect.py index 8ebf0fe..1b31ebb 100644 --- a/DrissionPage/_units/rect.py +++ b/DrissionPage/_units/rect.py @@ -92,6 +92,11 @@ class FrameRect(object): """返回页面总宽高,格式:(宽, 高)""" return self._frame.page_size + @property + def size(self): + """返回页面总宽高,格式:(宽, 高)""" + return self._frame.size + @property def viewport_size(self): """返回视口宽高,不包括滚动条,格式:(宽, 高)""" diff --git a/DrissionPage/_units/rect.pyi b/DrissionPage/_units/rect.pyi index 678b34f..4c507fb 100644 --- a/DrissionPage/_units/rect.pyi +++ b/DrissionPage/_units/rect.pyi @@ -51,6 +51,9 @@ class FrameRect(object): @property def viewport_location(self) -> Tuple[float, float]: ... + @property + def size(self) -> Tuple[float, float]: ... + @property def page_size(self) -> Tuple[float, float]: ... diff --git a/DrissionPage/_units/scroller.py b/DrissionPage/_units/scroller.py index 1771468..1a67c4c 100644 --- a/DrissionPage/_units/scroller.py +++ b/DrissionPage/_units/scroller.py @@ -157,9 +157,8 @@ class FrameScroller(PageScroller): """ :param frame: ChromiumFrame对象 """ - self._driver = frame.doc_ele + super().__init__(frame.doc_ele) self.t1 = self.t2 = 'this.documentElement' - self._wait_complete = False def to_see(self, loc_or_ele, center=None): """滚动页面直到元素可见 diff --git a/DrissionPage/_units/setter.py b/DrissionPage/_units/setter.py index 78b20c3..a1ee590 100644 --- a/DrissionPage/_units/setter.py +++ b/DrissionPage/_units/setter.py @@ -16,9 +16,9 @@ class ChromiumBaseSetter(object): self._page = page @property - def load_strategy(self): + def load_mode(self): """返回用于设置页面加载策略的对象""" - return PageLoadStrategy(self._page) + return LoadMode(self._page) @property def scroll(self): @@ -117,6 +117,13 @@ class ChromiumBaseSetter(object): self._page.run_cdp('Network.enable') self._page.run_cdp('Network.setExtraHTTPHeaders', headers=headers) + # --------------即将废弃--------------- + + @property + def load_strategy(self): + """返回用于设置页面加载策略的对象""" + return LoadMode(self._page) + class TabSetter(ChromiumBaseSetter): def __init__(self, page): @@ -432,11 +439,10 @@ class ChromiumFrameSetter(ChromiumBaseSetter): :param value: 属性值 :return: None """ - self._page._check_ok() self._page.frame_ele.set.attr(attr, value) -class PageLoadStrategy(object): +class LoadMode(object): """用于设置页面加载策略的类""" def __init__(self, page): @@ -452,19 +458,19 @@ class PageLoadStrategy(object): """ if value.lower() not in ('normal', 'eager', 'none'): raise ValueError("只能选择 'normal', 'eager', 'none'。") - self._page._page_load_strategy = value + self._page._load_mode = value def normal(self): """设置页面加载策略为normal""" - self._page._page_load_strategy = 'normal' + self._page._load_mode = 'normal' def eager(self): """设置页面加载策略为eager""" - self._page._page_load_strategy = 'eager' + self._page._load_mode = 'eager' def none(self): """设置页面加载策略为none""" - self._page._page_load_strategy = 'none' + self._page._load_mode = 'none' class PageScrollSetter(object): diff --git a/DrissionPage/_units/setter.pyi b/DrissionPage/_units/setter.pyi index e337d3b..cf2f73e 100644 --- a/DrissionPage/_units/setter.pyi +++ b/DrissionPage/_units/setter.pyi @@ -11,8 +11,9 @@ from requests.adapters import HTTPAdapter from requests.auth import HTTPBasicAuth from requests.cookies import RequestsCookieJar +from .scroller import PageScroller from .._elements.chromium_element import ChromiumElement -from .._pages.chromium_base import ChromiumBase, ChromiumPageScroll +from .._pages.chromium_base import ChromiumBase from .._pages.chromium_frame import ChromiumFrame from .._pages.chromium_page import ChromiumPage from .._pages.chromium_tab import ChromiumTab @@ -27,7 +28,7 @@ class ChromiumBaseSetter(object): self._page: ChromiumBase = ... @property - def load_strategy(self) -> PageLoadStrategy: ... + def load_mode(self) -> LoadMode: ... @property def scroll(self) -> PageScrollSetter: ... @@ -163,7 +164,7 @@ class ChromiumFrameSetter(ChromiumBaseSetter): def attr(self, attr: str, value: str) -> None: ... -class PageLoadStrategy(object): +class LoadMode(object): def __init__(self, page: ChromiumBase): self._page: ChromiumBase = ... @@ -177,8 +178,8 @@ class PageLoadStrategy(object): class PageScrollSetter(object): - def __init__(self, scroll: ChromiumPageScroll): - self._scroll: ChromiumPageScroll = ... + def __init__(self, scroll: PageScroller): + self._scroll: PageScroller = ... def wait_complete(self, on_off: bool = True): ... diff --git a/DrissionPage/_units/element_states.py b/DrissionPage/_units/states.py similarity index 66% rename from DrissionPage/_units/element_states.py rename to DrissionPage/_units/states.py index 8f3b524..2b7c79f 100644 --- a/DrissionPage/_units/element_states.py +++ b/DrissionPage/_units/states.py @@ -4,7 +4,7 @@ @Contact : g1879@qq.com """ from .._commons.web import location_in_viewport -from ..errors import CDPError, NoRectError +from ..errors import CDPError, NoRectError, PageClosedError, ElementLossError class ElementStates(object): @@ -40,7 +40,7 @@ class ElementStates(object): def is_alive(self): """返回元素是否仍在DOM中""" try: - d = self._ele.attrs + self._ele.attrs return True except Exception: return False @@ -102,3 +102,54 @@ class ShadowRootStates(object): return True except Exception: return False + + +class PageStates(object): + """Page对象、Tab对象使用""" + + def __init__(self, page): + """ + :param page: ChromiumBase对象 + """ + self._page = page + + @property + def is_loading(self): + """返回页面是否在加载状态""" + return self._page._is_loading + + @property + def is_alive(self): + """返回页面对象是否仍然可用""" + try: + self._page.run_cdp('Page.getLayoutMetrics') + return True + except PageClosedError: + return False + + @property + def ready_state(self): + """返回当前页面加载状态,'loading' 'interactive' 'complete'""" + return self._page._ready_state + + +class FrameStates(object): + def __init__(self, frame): + """ + :param frame: ChromiumFrame对象 + """ + self._frame = frame + + @property + def is_alive(self): + """返回frame元素是否可用,且里面仍挂载有frame""" + try: + node = self._frame._target_page.run_cdp('DOM.describeNode', + backendNodeId=self._frame._frame_ele.ids.backend_id)['node'] + except (ElementLossError, PageClosedError): + return False + return 'frameId' in node + + @property + def ready_state(self): + return self._frame._ready_state diff --git a/DrissionPage/_units/element_states.pyi b/DrissionPage/_units/states.pyi similarity index 62% rename from DrissionPage/_units/element_states.pyi rename to DrissionPage/_units/states.pyi index 5c2485f..e3c303c 100644 --- a/DrissionPage/_units/element_states.pyi +++ b/DrissionPage/_units/states.pyi @@ -3,9 +3,11 @@ @Author : g1879 @Contact : g1879@qq.com """ -from typing import Union, Tuple, List +from typing import Union, Tuple, List, Optional from .._elements.chromium_element import ChromiumShadowRoot, ChromiumElement +from .._pages.chromium_base import ChromiumBase +from .._pages.chromium_frame import ChromiumFrame class ElementStates(object): @@ -52,3 +54,28 @@ class ShadowRootStates(object): @property def is_alive(self) -> bool: ... + + +class PageStates(object): + def __init__(self, page: ChromiumBase): + self._page: ChromiumBase = ... + + @property + def is_loading(self) -> bool: ... + + @property + def is_alive(self) -> bool: ... + + @property + def ready_state(self) -> Optional[str]: ... + + +class FrameStates(object): + def __init__(self, frame: ChromiumFrame): + self._frame: ChromiumFrame = ... + + @property + def is_alive(self) -> bool: ... + + @property + def ready_state(self) -> str: ... diff --git a/DrissionPage/_units/waiter.py b/DrissionPage/_units/waiter.py index 56de34e..0361c56 100644 --- a/DrissionPage/_units/waiter.py +++ b/DrissionPage/_units/waiter.py @@ -180,7 +180,7 @@ class BaseWaiter(object): timeout = self._driver.timeout end_time = perf_counter() + timeout while perf_counter() < end_time: - if self._driver.is_loading == start: + if self._driver._is_loading == start: return True sleep(gap) diff --git a/DrissionPage/easy_set.py b/DrissionPage/easy_set.py index c70cb68..2bf6df5 100644 --- a/DrissionPage/easy_set.py +++ b/DrissionPage/easy_set.py @@ -52,7 +52,7 @@ def set_paths(browser_path=None, return str(path) if path else '' if browser_path is not None: - om.set_item('chrome_options', 'binary_location', format_path(browser_path)) + om.set_item('chrome_options', 'browser_path', format_path(browser_path)) if local_port is not None: om.set_item('chrome_options', 'debugger_address', f'127.0.0.1:{local_port}') @@ -185,7 +185,7 @@ def get_chrome_path(ini_path=None, # -----------从ini文件中获取-------------- if ini_path and from_ini: try: - path = OptionsManager(ini_path).chrome_options['binary_location'] + path = OptionsManager(ini_path).chrome_options['browser_path'] except KeyError: path = None else: