页面增加steates属性,配置文件优化

页面对象的is_loading、ready_state、is_alive属性移到states属性中;
重构Frame的steates属性;
page_load_strategy改为load_mode;
ini文件experimental_options改为prefs;
ChromiumOptinos增加ignore_certificate_errors()
set_headless()、set_no_imgs()、set_no_js()、set_mute()数个设置去掉set_
This commit is contained in:
g1879 2023-11-16 15:11:04 +08:00
parent 66de3a6db1
commit a089bcbffc
21 changed files with 409 additions and 253 deletions

View File

@ -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默认情况下使用, 等待所有资源下载完成
eagerDOM访问已准备就绪, 但其他资源 (如图像) 可能仍在加载中
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 = {}

View File

@ -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: ...

View File

@ -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'}

View File

@ -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)

View File

@ -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

View File

@ -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信息的类"""

View File

@ -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: ...

View File

@ -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

View File

@ -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: ...

View File

@ -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

View File

@ -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

View File

@ -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):

View File

@ -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):
"""返回视口宽高,不包括滚动条,格式:(宽, 高)"""

View File

@ -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]: ...

View File

@ -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):
"""滚动页面直到元素可见

View File

@ -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):

View File

@ -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): ...

View File

@ -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

View File

@ -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: ...

View File

@ -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)

View File

@ -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: