diff --git a/DrissionPage/chromium_base.py b/DrissionPage/chromium_base.py index 51cb540..3ec452c 100644 --- a/DrissionPage/chromium_base.py +++ b/DrissionPage/chromium_base.py @@ -257,7 +257,7 @@ class ChromiumBase(BasePage): @property def page_load_strategy(self): - """返回页面加载策略""" + """返回页面加载策略,有3种:'none'、'normal'、'eager'""" return self._page_load_strategy @property @@ -292,7 +292,7 @@ class ChromiumBase(BasePage): """运行javascript代码 \n :param script: js文本 :param as_expr: 是否作为表达式运行,为True时args无效 - :param args: 参数,按顺序在js文本中对应argument[0]、argument[2]... + :param args: 参数,按顺序在js文本中对应argument[0]、argument[1]... :return: 运行的结果 """ return run_script(self, script, as_expr, self.timeouts.script, args) @@ -301,7 +301,7 @@ class ChromiumBase(BasePage): """以异步方式执行js代码 \n :param script: js文本 :param as_expr: 是否作为表达式运行,为True时args无效 - :param args: 参数,按顺序在js文本中对应argument[0]、argument[2]... + :param args: 参数,按顺序在js文本中对应argument[0]、argument[1]... :return: None """ from threading import Thread @@ -664,7 +664,7 @@ class PageLoadStrategy(object): """ :param page: ChromiumBase对象 """ - self.page = page + self._page = page def __call__(self, value): """设置加载策略 \n @@ -673,16 +673,16 @@ class PageLoadStrategy(object): """ if value.lower() not in ('normal', 'eager', 'none'): raise ValueError("只能选择 'normal', 'eager', 'none'。") - self.page._page_load_strategy = value + self._page._page_load_strategy = value def normal(self): """设置页面加载策略为normal""" - self.page._page_load_strategy = 'normal' + self._page._page_load_strategy = 'normal' def eager(self): """设置页面加载策略为eager""" - self.page._page_load_strategy = 'eager' + self._page._page_load_strategy = 'eager' def none(self): """设置页面加载策略为none""" - self.page._page_load_strategy = 'none' + self._page._page_load_strategy = 'none' diff --git a/DrissionPage/chromium_base.pyi b/DrissionPage/chromium_base.pyi index b4717ef..2dd937e 100644 --- a/DrissionPage/chromium_base.pyi +++ b/DrissionPage/chromium_base.pyi @@ -199,7 +199,7 @@ class Timeout(object): class PageLoadStrategy(object): def __init__(self, page: ChromiumBase): - self.page: ChromiumBase = ... + self._page: ChromiumBase = ... def __call__(self, value: str) -> None: ... diff --git a/DrissionPage/chromium_frame.py b/DrissionPage/chromium_frame.py index 69eb9e6..70ed271 100644 --- a/DrissionPage/chromium_frame.py +++ b/DrissionPage/chromium_frame.py @@ -28,9 +28,6 @@ class ChromiumFrame(ChromiumBase): obj_id = super().run_script('document;', as_expr=True)['objectId'] self.doc_ele = ChromiumElement(self, obj_id=obj_id) - self._tab_obj.Page.FrameAttached = self._onFrameAttached - self._tab_obj.Page.FrameDetached = self._onFrameDetached - def __call__(self, loc_or_str, timeout=None): """在内部查找元素 \n 例:ele2 = ele1('@id=ele_id') \n @@ -45,6 +42,21 @@ class ChromiumFrame(ChromiumBase): attrs = [f"{attr}='{attrs[attr]}'" for attr in attrs] return f'' + # def _reload_document(self): + # self._frame_ele = ChromiumElement(self.page, backend_id=self._backend_id) + # node = self.page.run_cdp('DOM.describeNode', nodeId=self._frame_ele.node_id, not_change=True)['node'] + # + # if self._is_inner_frame(): + # self._is_diff_domain = False + # self.doc_ele = ChromiumElement(self.page, backend_id=node['contentDocument']['backendNodeId']) + # super().__init__(self.page.address, self.page.tab_id, self.page.timeout) + # else: + # self._is_diff_domain = True + # self._tab_obj.stop() + # super().__init__(self.page.address, self.frame_id, self.page.timeout) + # obj_id = super().run_script('document;', as_expr=True)['objectId'] + # self.doc_ele = ChromiumElement(self, obj_id=obj_id) + def _get_new_document(self): """刷新cdp使用的document数据""" if not self._is_reading: @@ -55,10 +67,11 @@ class ChromiumFrame(ChromiumBase): while True: try: - if self._is_inner_frame(): + if self._is_diff_domain is False: node = self.page.run_cdp('DOM.describeNode', backendNodeId=self.backend_id, not_change=True)['node'] self.doc_ele = ChromiumElement(self.page, backend_id=node['contentDocument']['backendNodeId']) + else: b_id = self._tab_obj.DOM.getDocument()['root']['backendNodeId'] self.doc_ele = ChromiumElement(self, backend_id=b_id) @@ -91,14 +104,6 @@ class ChromiumFrame(ChromiumBase): print('页面停止加载 FrameStoppedLoading') self._get_new_document() - def _onFrameAttached(self, **kwargs): - if self._debug: - print(f'FrameAttached{[kwargs]}') - - def _onFrameDetached(self, **kwargs): - if self._debug: - print(f'FrameDetached{[kwargs]}') - @property def tab_id(self): """返回当前标签页id""" @@ -204,9 +209,17 @@ class ChromiumFrame(ChromiumBase): def ready_state(self): """返回当前页面加载状态,'loading' 'interactive' 'complete'""" if self._is_diff_domain: - return super().ready_state + try: + return super().ready_state + except: + return 'complete' + else: - return self.doc_ele.run_script('return this.readyState;') + while True: + try: + return self.doc_ele.run_script('return this.readyState;') + except: + pass def refresh(self): """刷新frame页面""" diff --git a/DrissionPage/chromium_page.py b/DrissionPage/chromium_page.py index a7965b3..8473a43 100644 --- a/DrissionPage/chromium_page.py +++ b/DrissionPage/chromium_page.py @@ -75,7 +75,7 @@ class ChromiumPage(ChromiumBase): self._init_page(tab_id) self._get_document() self._first_run = False - self.main_tab: str = self.tab_id + self._main_tab = self.tab_id def _init_page(self, tab_id=None): """新建页面、页面刷新、切换标签页后要进行的cdp参数初始化 @@ -100,10 +100,14 @@ class ChromiumPage(ChromiumBase): @property def tabs(self): - """返回所有标签页id""" + """返回所有标签页id组成的列表""" tabs = self.run_cdp('Target.getTargets', filter=[{'type': "page"}])['targetInfos'] return [i['targetId'] for i in tabs] + @property + def main_tab(self): + return self._main_tab + @property def process_id(self): """返回浏览器进程id""" @@ -116,7 +120,7 @@ class ChromiumPage(ChromiumBase): def set_window(self): """返回用于设置窗口大小的对象""" if not hasattr(self, '_window_setter'): - self._window_setter = WindowSizeSetter(self) + self._window_setter = WindowSetter(self) return self._window_setter def get_tab(self, tab_id=None): @@ -184,7 +188,7 @@ class ChromiumPage(ChromiumBase): self._control_session.get(f'http://{self.address}/json/activate/{self.tab_id}') def new_tab(self, url=None, switch_to=True): - """新建并定位到一个标签页,该标签页在最后面 \n + """新建一个标签页,该标签页在最后面 \n :param url: 新标签页跳转到的网址 :param switch_to: 新建标签页后是否把焦点移过去 :return: None @@ -207,9 +211,16 @@ class ChromiumPage(ChromiumBase): else: self._control_session.get(f'http://{self.address}/json/new') + def set_main_tab(self, tab_id=None): + """设置主tab \n + :param tab_id: 标签页id,不传入则设置当前tab + :return: None + """ + self._main_tab = tab_id or self.tab_id + def to_main_tab(self): """跳转到主标签页""" - self.to_tab(self.main_tab) + self.to_tab(self._main_tab) def to_tab(self, tab_id=None, activate=True): """跳转到标签页 \n @@ -228,7 +239,7 @@ class ChromiumPage(ChromiumBase): """ tabs = self.tabs if not tab_id: - tab_id = self.main_tab + tab_id = self._main_tab if tab_id not in tabs: tab_id = tabs[0] @@ -273,8 +284,8 @@ class ChromiumPage(ChromiumBase): while len(self.tabs) != end_len: sleep(.1) - if self.main_tab in tabs: - self.main_tab = self.tabs[0] + if self._main_tab in tabs: + self._main_tab = self.tabs[0] self.to_tab() @@ -350,7 +361,7 @@ class Alert(object): self.response_text = None -class WindowSizeSetter(object): +class WindowSetter(object): """用于设置窗口大小的类""" def __init__(self, page): @@ -373,7 +384,7 @@ class WindowSizeSetter(object): """设置窗口为常规模式""" self._perform({'windowState': 'normal'}) - def new_size(self, width=None, height=None): + def size(self, width=None, height=None): """设置窗口大小 \n :param width: 窗口宽度 :param height: 窗口高度 @@ -385,7 +396,7 @@ class WindowSizeSetter(object): height = height or info['height'] self._perform({'width': width, 'height': height}) - def to_location(self, x=None, y=None): + def location(self, x=None, y=None): """设置窗口在屏幕中的位置,相对左上角坐标 \n :param x: 距离顶部距离 :param y: 距离左边距离 diff --git a/DrissionPage/chromium_page.pyi b/DrissionPage/chromium_page.pyi index 57e3008..ffd0cba 100644 --- a/DrissionPage/chromium_page.pyi +++ b/DrissionPage/chromium_page.pyi @@ -21,8 +21,8 @@ class ChromiumPage(ChromiumBase): timeout: float = ...): self.options: DriverOptions = ... self.process: popen = ... - self._window_setter: WindowSizeSetter = ... - self.main_tab: str = ... + self._window_setter: WindowSetter = ... + self._main_tab: str = ... self._alert: Alert = ... def _connect_browser(self, @@ -39,11 +39,14 @@ class ChromiumPage(ChromiumBase): @property def tabs(self) -> List[str]: ... + @property + def main_tab(self) -> str: ... + @property def process_id(self) -> Union[None, int]: ... @property - def set_window(self) -> 'WindowSizeSetter': ... + def set_window(self) -> 'WindowSetter': ... def get_tab(self, tab_id: str = ...) -> ChromiumTab: ... @@ -91,7 +94,7 @@ class Alert(object): self.response_text: str = ... -class WindowSizeSetter(object): +class WindowSetter(object): def __init__(self, page: ChromiumPage): self.driver: ChromiumDriver = ... @@ -105,9 +108,9 @@ class WindowSizeSetter(object): def normal(self) -> None: ... - def new_size(self, width: int = ..., height: int = ...) -> None: ... + def size(self, width: int = ..., height: int = ...) -> None: ... - def to_location(self, x: int = ..., y: int = ...) -> None: ... + def location(self, x: int = ..., y: int = ...) -> None: ... def _get_info(self) -> dict: ... diff --git a/DrissionPage/web_page.py b/DrissionPage/web_page.py index 0ee85e8..d8cb093 100644 --- a/DrissionPage/web_page.py +++ b/DrissionPage/web_page.py @@ -198,10 +198,9 @@ class WebPage(SessionPage, ChromiumPage, BasePage): return super(SessionPage, self).s_eles(loc_or_str) def change_mode(self, mode=None, go=True, copy_cookies=True): - """切换模式,接收's'或'd',除此以外的字符串会切换为 d 模式 \n - 切换时会把当前模式的cookies复制到目标模式 \n - 切换后,如果go是True,调用相应的get函数使访问的页面同步 \n - 注意:s转d时,若浏览器当前网址域名和s模式不一样,必须会跳转 \n + """切换模式,接收's'或'd',除此以外的字符串会切换为 d 模式 \n + 如copy_cookies为True,切换时会把当前模式的cookies复制到目标模式 \n + 切换后,如果go是True,调用相应的get函数使访问的页面同步 \n :param mode: 模式字符串 :param go: 是否跳转到原模式的url :param copy_cookies: 是否复制cookies到目标模式 @@ -283,6 +282,12 @@ class WebPage(SessionPage, ChromiumPage, BasePage): return cookies def set_cookies(self, cookies, set_session=False, set_driver=False): + """添加cookies信息到浏览器或session对象 \n + :param cookies: 可以接收`CookieJar`、`list`、`tuple`、`str`、`dict`格式的`cookies` + :param set_session: 是否设置到Session对象 + :param set_driver: 是否设置到浏览器 + :return: None + """ # 添加cookie到driver if set_driver: cookies = cookies_to_tuple(cookies) diff --git a/docs/WebPage使用方法/3.1创建页面对象.md b/docs/WebPage使用方法/3.1创建页面对象.md index 0053ee9..698238e 100644 --- a/docs/WebPage使用方法/3.1创建页面对象.md +++ b/docs/WebPage使用方法/3.1创建页面对象.md @@ -1,15 +1,49 @@ 在入门指南的快速上手一节,我们已经初步了解如何创建页面对象,本节进一步介绍更多功能。 -# ✔️ `WebPage`类 +常用的页面对象有 3 种: + +- `WebPage`:整合 d 模式和 s 模式于一体的页面对象 + +- `ChromiumPage`:单纯用于操作浏览器的页面对象 + +- `SessionPage`:单纯用于收发数据包的页面对象 + +这 3 种页面对象的使用逻辑是一致的,使用时可根据实际须要选择使用。 + +# ✔️ 三种页面对象 + +## 📍 `WebPage` `WebPage`对象封装了常用的网页操作,并实现在浏览器和 requests 两种模式之间的切换。 -初始化参数: +**初始化参数:** -- mode:初始化时模式,`'d'`或`'s'`,默认为`'d'` -- timeout:超时时间,s 模式时为连接时间,d 模式时为查找元素、处理弹出框、输入文本等超时时间 -- driver_or_options:`Tab`对象或`DriverOptions`对象,为`None`时使用 ini 文件配置,为`False`时不读取 ini 文件 -- session_or_options:`Session`对象或`SessionOptions`对象,为`None`时使用 ini 文件配置,为`False`时不读取 ini 文件 +- `mode`:初始化时模式,`'d'`或`'s'`,默认为`'d'` +- `timeout`:超时时间,s 模式时为连接时间,d 模式时为查找元素、处理弹出框、输入文本等超时时间 +- `driver_or_options`:`ChromiumDriver`对象或`DriverOptions`对象,为`None`时使用 ini 文件配置,为`False`时不读取 ini 文件 +- `session_or_options`:`Session`对象或`SessionOptions`对象,为`None`时使用 ini 文件配置,为`False`时不读取 ini 文件 + +## 📍 `ChromiumPage` + +`ChroumiumPage`对象纯粹用于操作浏览器,不能切换模式。一个该对象对应的是浏览器上一个标签页。 + +**初始化参数:** + +- `addr_driver_opts`:浏览器信息,可以是 '地址:端口'字符串、`ChromiumDriver`对象,或`DriverOptions`对象 + +- `tab_id`:要控制的标签页id,不指定默认为激活的 + +- `timeout`:总体超时时间 + +## 📍 `SessionPage` + +`SessionPage`对象纯粹用于收发数据包,不能切换模式,产生的元素对象为`SessionElement`。 + +**初始化参数:** + +- `session_or_options`:`Session`对象或`SessionOptions`对象 + +- `timeout`:连接超时时间 # ✔️ 直接创建 @@ -23,6 +57,12 @@ page = WebPage('d') # 指定以 s 模式创建页面对象 page = WebPage('s') + +# 用 ChroumiumPage 创建页面对象 +page = ChromiumPage() + +# 用 SessionPage 创建页面对象 +page = SessionPage() ``` # ✔️ 通过配置信息创建 @@ -50,6 +90,15 @@ do = DriverOptions().set_paths(chrome_path=r'D:\chrome.exe', local_port=9333) page = WebPage(driver_or_options=do) ``` +用 ChroumiumPage 创建页面对象 + +```python +page = ChromiumPage(addr_driver_opts=do) + +# 也可以直接把要控制的浏览器地址写在参数里 +page = ChromiumPage(addr_driver_opts='127.0.0.1:9333') +``` + ## 📍 `SessionOptions`类 `SessionOptions`用于管理创建`Session`对象时的配置,内置了常用的配置,并能实现链式操作。详细使用方法见“启动配置”一节。 @@ -74,6 +123,12 @@ so = SessionOptions(read_file=False).set_proxies(proxies) page = WebPage(mode='s', session_or_options=so) ``` +用 SessionPage 创建页面对象 + +```python +page = SessionPage(session_or_options=so) +``` + d 模式的配置和 s 模式的配置是可以同时使用的,不会互相影响。 ```python diff --git a/docs/WebPage使用方法/3.2访问网页.md b/docs/WebPage使用方法/3.2访问网页.md index 04bad1e..a79ab98 100644 --- a/docs/WebPage使用方法/3.2访问网页.md +++ b/docs/WebPage使用方法/3.2访问网页.md @@ -10,15 +10,15 @@ d 模式下使用`get()`方法访问网页。 当连接失败时,程序默认重试 3 次,每次间隔 2 秒,可以通过参数设置重试次数和间隔。 d 模式下根据浏览器的状态,还可以通过重写`check_page()`方法实现自定义检查方式。 -参数: +**参数:** -- url:目标 url -- show_errmsg:是否显示和抛出异常。默认不抛出,连接错误会返回`None` -- retry:重试次数,与页面对象的设置一致,默认 3 次 -- interval:重试间隔(秒),与页面对象的设置一致,默认 2 秒 -- timeout:连接超时时间(秒) +- `url`:目标 url +- `show_errmsg`:是否显示和抛出异常。默认不抛出,连接错误会返回`None` +- `retry`:重试次数,与页面对象的设置一致,默认 3 次 +- `interval`:重试间隔(秒),与页面对象的设置一致,默认 2 秒 +- `timeout`:连接超时时间(秒) -返回:`bool`类型,表示是否连接成功 +**返回:**`bool`类型,表示是否连接成功 ```python from DrissionPage import WebPage @@ -69,12 +69,12 @@ s 模式基于 requests,因此可使用 requests 内置的所有请求方式 参数: -- url:目标 url -- show_errmsg:是否显示和抛出异常,默认不抛出,连接错误会返回 None -- retry:重试次数,与页面对象的设置一致,默认 3 次 -- interval:重试间隔(秒),与页面对象的设置一致,默认 2 秒 -- timeout:连接超时时间(秒) -- **kwargs:连接参数,具体见 requests用法 +- `url`:目标 url +- `show_errmsg`:是否显示和抛出异常,默认不抛出,连接错误会返回`None` +- `retry`:重试次数,与页面对象的设置一致,默认 3 次 +- `interval`:重试间隔(秒),与页面对象的设置一致,默认 2 秒 +- `timeout`:连接超时时间(秒) +- `**kwargs`:连接参数,具体见 requests 用法 返回:`bool`类型,表示是否连接成功,根据`Response`对象的`status_code`参数决定 @@ -107,13 +107,13 @@ page.get(url, headers=headers, cookies=cookies, proxies=proxies) 参数: -- url:目标 url -- data:提交的数据,可以是`dict`或`str`类型 -- json:提交的数据,可以是`dict`或`str`类型 -- show_errmsg:是否显示和抛出异常,默认不抛出,连接错误会返回 None -- retry:重试次数,与页面对象的设置一致,默认 3 次 -- interval:重试间隔(秒),与页面对象的设置一致,默认 2 秒 -- **kwargs:连接参数,s 模式专用 +- `url`:目标 url +- `data`:提交的数据,可以是`dict`或`str`类型 +- `json`:提交的数据,可以是`dict`或`str`类型 +- `show_errmsg`:是否显示和抛出异常,默认不抛出,连接错误会返回`None` +- `retry`:重试次数,与页面对象的设置一致,默认 3 次 +- `interval`:重试间隔(秒),与页面对象的设置一致,默认 2 秒 +- `**kwargs`:连接参数,具体见 requests 用法 ```python from DrissionPage import WebPage diff --git a/docs/WebPage使用方法/3.4获取元素信息.md b/docs/WebPage使用方法/3.4获取元素信息.md index 31e0ee7..5a2f9de 100644 --- a/docs/WebPage使用方法/3.4获取元素信息.md +++ b/docs/WebPage使用方法/3.4获取元素信息.md @@ -1,12 +1,12 @@ 获取到须要的页面元素后,可以使用元素对象获取元素的信息。 -`WebPage`生成的元素对象有四种: +`WebPage`常用的元素对象有四种: - `ChromiumElement`:浏览器一般元素 - `ChromiumShadowRootElement`:shadow-root 元素 -- `ChromiumFrame`:s 模式产生的元素 +- `ChromiumFrame`:专门用于操作`