diff --git a/DrissionPage/base.py b/DrissionPage/base.py index d2fab59..1305e67 100644 --- a/DrissionPage/base.py +++ b/DrissionPage/base.py @@ -6,7 +6,7 @@ """ from abc import abstractmethod from re import sub -from typing import Union, Tuple, List +from typing import Union, List from urllib.parse import quote from .common import format_html, get_loc @@ -26,7 +26,7 @@ class BaseParser(object): # ----------------以下属性或方法待后代实现---------------- @property - def html(self) -> str: + def html(self): return '' def s_ele(self, loc_or_ele): @@ -59,16 +59,16 @@ class BaseElement(BaseParser): def _ele(self, loc_or_str, timeout=None, single=True, relative=False): pass - def parent(self, level_or_loc = 1): + def parent(self, level_or_loc=1): pass - def prev(self, index = 1) -> None: + def prev(self, index=1) -> None: return None # ShadowRootElement直接继承 def prevs(self) -> None: return None # ShadowRootElement直接继承 - def next(self, index = 1): + def next(self, index=1): pass def nexts(self): @@ -290,7 +290,7 @@ class DrissionElement(BaseElement): class BasePage(BaseParser): """页面类的基类""" - def __init__(self, timeout = 10): + def __init__(self, timeout=10): """初始化函数""" self._url = None self.timeout = timeout @@ -346,9 +346,9 @@ class BasePage(BaseParser): return @abstractmethod - def get_cookies(self, as_dict= False): + def get_cookies(self, as_dict=False): return {} @abstractmethod - def get(self, url, show_errmsg= False, retry = None, interval= None): + def get(self, url, show_errmsg=False, retry=None, interval=None): pass diff --git a/DrissionPage/chromium_base.py b/DrissionPage/chromium_base.py index a5682c5..919ee5c 100644 --- a/DrissionPage/chromium_base.py +++ b/DrissionPage/chromium_base.py @@ -259,7 +259,7 @@ class ChromiumBase(BasePage): @property def set_page_load_strategy(self): """返回用于设置页面加载策略的对象""" - return pageLoadStrategy(self) + return PageLoadStrategy(self) def set_timeouts(self, implicit=None, page_load=None, script=None): """设置超时时间,单位为秒 \n @@ -367,13 +367,13 @@ class ChromiumBase(BasePage): """ return self._ele(loc_or_ele, timeout=timeout) - def eles(self, loc_or_ele, timeout=None): + def eles(self, loc_or_str, timeout=None): """获取所有符合条件的元素对象 \n - :param loc_or_ele: 定位符或元素对象 + :param loc_or_str: 定位符或元素对象 :param timeout: 查找超时时间 :return: ChromiumElement对象组成的列表 """ - return self._ele(loc_or_ele, timeout=timeout, single=False) + return self._ele(loc_or_str, timeout=timeout, single=False) def s_ele(self, loc_or_ele=None): """查找第一个符合条件的元素以SessionElement形式返回,处理复杂页面时效率很高 \n @@ -808,7 +808,7 @@ class Timeout(object): return self.page.timeout -class pageLoadStrategy(object): +class PageLoadStrategy(object): """用于设置页面加载策略的类""" def __init__(self, page): @@ -826,14 +826,14 @@ class pageLoadStrategy(object): raise ValueError("只能选择 'normal', 'eager', 'none'。") self.page._page_load_strategy = value - def set_normal(self): + def normal(self): """设置页面加载策略为normal""" self.page._page_load_strategy = 'normal' - def set_eager(self): + def eager(self): """设置页面加载策略为eager""" self.page._page_load_strategy = 'eager' - def set_none(self): + def none(self): """设置页面加载策略为none""" self.page._page_load_strategy = 'none' diff --git a/DrissionPage/chromium_base.pyi b/DrissionPage/chromium_base.pyi index c5129b0..7c58138 100644 --- a/DrissionPage/chromium_base.pyi +++ b/DrissionPage/chromium_base.pyi @@ -134,7 +134,7 @@ class ChromiumBase(BasePage): timeout: float = ...) -> Union[ChromiumElement, 'ChromiumFrame', None]: ... def eles(self, - loc_or_ele: Union[Tuple[str, str], str], + loc_or_str: Union[Tuple[str, str], str], timeout: float = ...) -> List[Union[ChromiumElement, 'ChromiumFrame']]: ... def s_ele(self, loc_or_ele: Union[Tuple[str, str], str, ChromiumElement] = ...) \ @@ -282,14 +282,14 @@ class Timeout(object): def implicit(self) -> float: ... -class pageLoadStrategy(object): +class PageLoadStrategy(object): def __init__(self, page: ChromiumBase): self.page: ChromiumBase = ... def __call__(self, value: str) -> None: ... - def set_normal(self) -> None: ... + def normal(self) -> None: ... - def set_eager(self) -> None: ... + def eager(self) -> None: ... - def set_none(self) -> None: ... + def none(self) -> None: ... diff --git a/DrissionPage/chromium_element.py b/DrissionPage/chromium_element.py index 4b4e35f..cdfcfbc 100644 --- a/DrissionPage/chromium_element.py +++ b/DrissionPage/chromium_element.py @@ -40,6 +40,8 @@ class ChromiumElement(DrissionElement): self._node_id = self._get_node_id(obj_id) self._obj_id = obj_id + self._doc_id = self.run_script('return this.ownerDocument;')['objectId'] + def __repr__(self): attrs = self.attrs attrs = [f"{attr}='{attrs[attr]}'" for attr in attrs] @@ -108,6 +110,11 @@ class ChromiumElement(DrissionElement): """返回cdp中的node id""" return self._node_id + @property + def doc_id(self): + """返回document的object id""" + return self._doc_id + @property def size(self): """返回元素宽和高""" @@ -1046,251 +1053,6 @@ class ChromiumShadowRootElement(BaseElement): return self.page.run_cdp('DOM.describeNode', nodeId=node_id)['node']['backendNodeId'] -# class ChromiumFrame(object): -# def __init__(self, page, ele: ChromiumElement): -# from .chromium_base import ChromiumBase -# self.page: ChromiumBase = page -# self._inner_ele = ele -# self._is_diff_domain = False -# self.frame_id = page.run_cdp('DOM.describeNode', nodeId=ele.node_id)['node'].get('frameId', None) -# -# src = ele.attr('src') -# if src: -# netloc1 = urlparse(src).netloc -# netloc2 = urlparse(page.url).netloc -# if netloc1 != netloc2: -# self._is_diff_domain = True -# from .chromium_base import ChromiumBase -# self.inner_page = ChromiumBase(page.address, self.frame_id, page.timeout) -# self.inner_page.set_page_load_strategy(self.page.page_load_strategy) -# self.inner_page.timeouts = self.page.timeouts -# -# def __repr__(self) -> str: -# attrs = self._inner_ele.attrs -# attrs = [f"{attr}='{attrs[attr]}'" for attr in attrs] -# return f'' -# -# @property -# def tag(self) -> str: -# """返回元素tag""" -# return self._inner_ele.tag -# -# @property -# def url(self) -> str: -# """""" -# if self._is_diff_domain: -# return self.inner_page.url -# else: -# r = self.page.run_cdp('DOM.describeNode', nodeId=self._inner_ele.node_id) -# return r['node']['contentDocument']['documentURL'] -# -# @property -# def html(self) -> str: -# """返回元素outerHTML文本""" -# if self._is_diff_domain: -# tag = self.tag -# out_html = self.page.run_cdp('DOM.getOuterHTML', nodeId=self._inner_ele.node_id)['outerHTML'] -# in_html = self.inner_page.html -# sign = search(rf'<{tag}.*?>', out_html).group(0) -# return f'{sign}{in_html}' -# -# else: -# return self._inner_ele.html -# -# @property -# def title(self) -> str: -# d = self.inner_page if self._is_diff_domain else self._inner_ele -# ele = d.ele('xpath://title') -# return ele.text if ele else None -# -# @property -# def cookies(self): -# return self.inner_page.cookies if self._is_diff_domain else self.page.cookies -# -# @property -# def inner_html(self) -> str: -# """返回元素innerHTML文本""" -# return self.inner_page.html if self._is_diff_domain else self._inner_ele.inner_html -# -# @property -# def attrs(self) -> dict: -# return self._inner_ele.attrs -# -# @property -# def frame_size(self) -> dict: -# if self._is_diff_domain: -# return self.inner_page.size -# else: -# h = self._inner_ele.run_script('return this.contentDocument.body.scrollHeight;') -# w = self._inner_ele.run_script('return this.contentDocument.body.scrollWidth;') -# return {'height': h, 'width': w} -# -# @property -# def size(self) -> dict: -# """返回frame元素大小""" -# return self._inner_ele.size -# -# @property -# def obj_id(self) -> str: -# """返回js中的object id""" -# return self._inner_ele.obj_id -# -# @property -# def node_id(self) -> str: -# """返回cdp中的node id""" -# return self._inner_ele.node_id -# -# @property -# def location(self) -> dict: -# """返回frame元素左上角的绝对坐标""" -# return self._inner_ele.location -# -# @property -# def is_displayed(self) -> bool: -# """返回frame元素是否显示""" -# return self._inner_ele.is_displayed -# -# def get(self, url): -# self.page._get(url, False, None, None, None, self.frame_id) -# -# def ele(self, -# loc_or_ele: Union[Tuple[str, str], str, ChromiumElement, 'ChromiumFrame'], -# timeout: float = None): -# d = self.inner_page if self._is_diff_domain else self._inner_ele -# return d.ele(loc_or_ele, timeout) -# -# def eles(self, -# loc_or_ele: Union[Tuple[str, str], str], -# timeout: float = None): -# d = self.inner_page if self._is_diff_domain else self._inner_ele -# return d.eles(loc_or_ele, timeout) -# -# # def s_ele(self, loc_or_ele: Union[Tuple[str, str], str, ChromiumElement] = None) \ -# # -> Union[SessionElement, str, None]: -# # """查找第一个符合条件的元素以SessionElement形式返回,处理复杂页面时效率很高 \n -# # :param loc_or_ele: 元素的定位信息,可以是loc元组,或查询字符串 -# # :return: SessionElement对象或属性、文本 -# # """ -# # if isinstance(loc_or_ele, ChromiumElement): -# # return make_session_ele(loc_or_ele) -# # else: -# # return make_session_ele(self, loc_or_ele) -# # -# # def s_eles(self, loc_or_str: Union[Tuple[str, str], str] = None) -> List[Union[SessionElement, str]]: -# # """查找所有符合条件的元素以SessionElement列表形式返回 \n -# # :param loc_or_str: 元素的定位信息,可以是loc元组,或查询字符串 -# # :return: SessionElement对象组成的列表 -# # """ -# # return make_session_ele(self, loc_or_str, single=False) -# -# def attr(self, attr: str) -> Union[str, None]: -# """返回frame元素attribute属性值 \n -# :param attr: 属性名 -# :return: 属性值文本,没有该属性返回None -# """ -# return self._inner_ele.attr(attr) -# -# def set_attr(self, attr: str, value: str) -> None: -# """设置frame元素attribute属性 \n -# :param attr: 属性名 -# :param value: 属性值 -# :return: None -# """ -# self._inner_ele.set_attr(attr, value) -# -# def remove_attr(self, attr: str) -> None: -# """删除frame元素attribute属性 \n -# :param attr: 属性名 -# :return: None -# """ -# self._inner_ele.remove_attr(attr) -# -# def parent(self, level_or_loc: Union[tuple, str, int] = 1) -> Union['ChromiumElement', None]: -# """返回上面某一级父元素,可指定层数或用查询语法定位 \n -# :param level_or_loc: 第几级父元素,或定位符 -# :return: 上级元素对象 -# """ -# return self._inner_ele.parent(level_or_loc) -# -# def prev(self, -# filter_loc: Union[tuple, str] = '', -# index: int = 1, -# timeout: float = 0) -> Union['ChromiumElement', str, None]: -# """返回前面的一个兄弟元素,可用查询语法筛选,可指定返回筛选结果的第几个 \n -# :param filter_loc: 用于筛选元素的查询语法 -# :param index: 前面第几个查询结果元素 -# :param timeout: 查找元素的超时时间 -# :return: 兄弟元素 -# """ -# return self._inner_ele.prev(index, filter_loc, timeout) -# -# def next(self, -# filter_loc: Union[tuple, str] = '', -# index: int = 1, -# timeout: float = 0) -> Union['ChromiumElement', str, None]: -# """返回后面的一个兄弟元素,可用查询语法筛选,可指定返回筛选结果的第几个 \n -# :param filter_loc: 用于筛选元素的查询语法 -# :param index: 后面第几个查询结果元素 -# :param timeout: 查找元素的超时时间 -# :return: 兄弟元素 -# """ -# return self._inner_ele.next(index, filter_loc, timeout) -# -# def before(self, -# filter_loc: Union[tuple, str] = '', -# index: int = 1, -# timeout: float = None) -> Union['ChromiumElement', str, None]: -# """返回当前元素前面的一个元素,可指定筛选条件和第几个。查找范围不限兄弟元素,而是整个DOM文档 \n -# :param filter_loc: 用于筛选元素的查询语法 -# :param index: 前面第几个查询结果元素 -# :param timeout: 查找元素的超时时间 -# :return: 本元素前面的某个元素或节点 -# """ -# return self._inner_ele.before(index, filter_loc, timeout) -# -# def after(self, -# filter_loc: Union[tuple, str] = '', -# index: int = 1, -# timeout: float = None) -> Union['ChromiumElement', str, None]: -# """返回当前元素后面的一个元素,可指定筛选条件和第几个。查找范围不限兄弟元素,而是整个DOM文档 \n -# :param filter_loc: 用于筛选元素的查询语法 -# :param index: 后面第几个查询结果元素 -# :param timeout: 查找元素的超时时间 -# :return: 本元素后面的某个元素或节点 -# """ -# return self._inner_ele.after(index, filter_loc, timeout) -# -# def prevs(self, -# filter_loc: Union[tuple, str] = '', -# timeout: float = 0) -> List[Union['ChromiumElement', str]]: -# """返回前面全部兄弟元素或节点组成的列表,可用查询语法筛选 \n -# :param filter_loc: 用于筛选元素的查询语法 -# :param timeout: 查找元素的超时时间 -# :return: 兄弟元素或节点文本组成的列表 -# """ -# return self._inner_ele.prevs(filter_loc, timeout) -# -# def nexts(self, -# filter_loc: Union[tuple, str] = '', -# timeout: float = 0) -> List[Union['ChromiumElement', str]]: -# """返回后面全部兄弟元素或节点组成的列表,可用查询语法筛选 \n -# :param filter_loc: 用于筛选元素的查询语法 -# :param timeout: 查找元素的超时时间 -# :return: 兄弟元素或节点文本组成的列表 -# """ -# return self._inner_ele.nexts(filter_loc, timeout) -# -# def befores(self, -# filter_loc: Union[tuple, str] = '', -# timeout: float = None) -> List[Union['ChromiumElement', str]]: -# """返回当前元素后面符合条件的全部兄弟元素或节点组成的列表,可用查询语法筛选。查找范围不限兄弟元素,而是整个DOM文档 \n -# :param filter_loc: 用于筛选元素的查询语法 -# :param timeout: 查找元素的超时时间 -# :return: 本元素前面的元素或节点组成的列表 -# """ -# return self._inner_ele.befores(filter_loc, timeout) - - def make_chromium_ele(ele, loc, single=True, timeout=None, relative=True): """在chromium元素中查找 \n :param ele: ChromiumElement对象 @@ -1481,6 +1243,8 @@ def run_script(page_or_ele, script, as_expr=False, timeout=None, args=None): if isinstance(page_or_ele, (ChromiumElement, ChromiumShadowRootElement)): page = page_or_ele.page obj_id = page_or_ele.obj_id + # elif isinstance(page_or_ele, ChromiumFrame): + # pass else: page = page_or_ele obj_id = page_or_ele._root_id @@ -1527,8 +1291,11 @@ def _parse_js_result(page, ele, result): return None elif sub_type == 'node': - if result['className'] == 'ShadowRoot': + class_name = result['className'] + if class_name == 'ShadowRoot': return ChromiumShadowRootElement(ele, obj_id=result['objectId']) + elif class_name == 'HTMLDocument': + return result else: return _make_chromium_ele(page, obj_id=result['objectId']) diff --git a/DrissionPage/chromium_element.pyi b/DrissionPage/chromium_element.pyi index 794451f..d322626 100644 --- a/DrissionPage/chromium_element.pyi +++ b/DrissionPage/chromium_element.pyi @@ -25,6 +25,7 @@ class ChromiumElement(DrissionElement): self.page: Union[ChromiumPage, WebPage] = ... self._node_id: str = ... self._obj_id: str = ... + self._doc_id: str = ... self._scroll: ChromeScroll = ... self._select: ChromeSelect = ... @@ -59,6 +60,9 @@ class ChromiumElement(DrissionElement): @property def node_id(self) -> str: ... + @property + def doc_id(self) -> str: ... + @property def size(self) -> dict: ... @@ -299,27 +303,27 @@ class ChromiumShadowRootElement(BaseElement): loc_or_str: Union[Tuple[str, str], str], timeout: float = ...) -> List[ChromiumElement]: ... - def s_ele(self, loc_or_str: Union[Tuple[str, str], str]=...) -> Union[SessionElement, str, None]: ... + def s_ele(self, loc_or_str: Union[Tuple[str, str], str] = ...) -> Union[SessionElement, str, None]: ... def s_eles(self, loc_or_str: Union[Tuple[str, str], str]) -> List[SessionElement]: ... def _ele(self, loc_or_str: Union[Tuple[str, str], str], timeout: float = ..., - single: bool = ..., relative: bool =...) -> Union['ChromiumElement', None, List[ChromiumElement]]: ... + single: bool = ..., relative: bool = ...) -> Union['ChromiumElement', None, List[ChromiumElement]]: ... - def _get_node_id(self, obj_id:str) -> str: ... + def _get_node_id(self, obj_id: str) -> str: ... - def _get_obj_id(self, back_id:str) -> str: ... + def _get_obj_id(self, back_id: str) -> str: ... - def _get_backend_id(self, node_id:str) -> str: ... + def _get_backend_id(self, node_id: str) -> str: ... def make_chromium_ele(ele: ChromiumElement, loc: Union[str, Tuple[str, str]], single: bool = ..., timeout: float = ..., - relative: bool =...) -> Union[ChromiumElement, str, None, List[Union[ChromiumElement, str]]]: ... + relative: bool = ...) -> Union[ChromiumElement, str, None, List[Union[ChromiumElement, str]]]: ... def _find_by_xpath(ele: ChromiumElement, @@ -341,8 +345,9 @@ def _make_chromium_ele(page: ChromiumBase, node_id: str = ..., obj_id: str = ... def _make_js_for_find_ele_by_xpath(xpath: str, type_txt: str, node_txt: str) -> str: ... -def run_script(page_or_ele: Union[ChromiumBase, ChromiumElement, ChromiumShadowRootElement], script: str, as_expr: bool = ..., - timeout: float = ..., args: tuple = ...) -> Any: ... +def run_script(page_or_ele: Union[ChromiumBase, ChromiumElement, ChromiumShadowRootElement], script: str, + as_expr: bool = ..., + timeout: float = ..., args: tuple = ...) -> Any: ... def _parse_js_result(page: ChromiumBase, ele: ChromiumElement, result: dict): ... diff --git a/DrissionPage/chromium_page.py b/DrissionPage/chromium_page.py index 875614e..6fb9d4f 100644 --- a/DrissionPage/chromium_page.py +++ b/DrissionPage/chromium_page.py @@ -97,9 +97,8 @@ class ChromiumPage(ChromiumBase): @property def tabs(self): """返回所有标签页id""" - self._driver - json = self._control_session.get(f'http://{self.address}/json').json() - return [i['id'] for i in json if i['type'] == 'page'] + tabs = self.run_cdp('Target.getTargets', filter=[{'type': "page"}])['targetInfos'] + return [i['targetId'] for i in tabs] @property def process_id(self): diff --git a/DrissionPage/chromium_page.pyi b/DrissionPage/chromium_page.pyi index ce4e8d9..c9b49d0 100644 --- a/DrissionPage/chromium_page.pyi +++ b/DrissionPage/chromium_page.pyi @@ -34,7 +34,7 @@ class ChromiumPage(ChromiumBase): def tabs_count(self) -> int: ... @property - def tabs(self) -> list: ... + def tabs(self) -> List[str]: ... @property def process_id(self) -> Union[None, int]: ... diff --git a/DrissionPage/session_element.py b/DrissionPage/session_element.py index 714f16e..b0f44ce 100644 --- a/DrissionPage/session_element.py +++ b/DrissionPage/session_element.py @@ -285,7 +285,7 @@ def make_session_ele(html_or_ele, loc=None, single=True): loc = loc[0], loc_str - elif isinstance(html_or_ele, DrissionElement): # DriverElement + elif isinstance(html_or_ele, DrissionElement): # ChromiumElement, DriverElement loc_str = loc[1] if loc[0] == 'xpath' and loc[1].lstrip().startswith('/'): loc_str = f'.{loc[1]}' @@ -296,7 +296,11 @@ def make_session_ele(html_or_ele, loc=None, single=True): # 获取整个页面html再定位到当前元素,以实现查找上级元素 page = html_or_ele.page xpath = html_or_ele.xpath - html_or_ele = fromstring(html_or_ele.page.html) + if hasattr(html_or_ele, 'doc_id'): # ChromiumElement,支持元素在iframe内的情况 + html = html_or_ele.page.run_cdp('DOM.getOuterHTML', objectId=html_or_ele.doc_id)['outerHTML'] + else: + html = html_or_ele.page.html + html_or_ele = fromstring(html) html_or_ele = html_or_ele.xpath(xpath)[0] elif isinstance(html_or_ele, BasePage): # MixPage, DriverPage 或 SessionPage diff --git a/DrissionPage/session_page.py b/DrissionPage/session_page.py index d8313b3..81bf9bc 100644 --- a/DrissionPage/session_page.py +++ b/DrissionPage/session_page.py @@ -17,12 +17,19 @@ class SessionPage(BasePage): """SessionPage封装了页面操作的常用功能,使用requests来获取、解析网页""" def __init__(self, session_or_options=None, timeout=10): - """初始化函数""" + """初始化 \n + :param session_or_options: Session对象或SessionOptions对象 + :param timeout: 连接超时时间 + """ super().__init__(timeout) self._response = None self._create_session(session_or_options) def _create_session(self, Session_or_Options): + """创建内建Session对象 + :param Session_or_Options: Session对象或SessionOptions对象 + :return: None + """ if Session_or_Options is None or isinstance(Session_or_Options, SessionOptions): options = Session_or_Options or SessionOptions() self._set_session(options.as_dict()) diff --git a/DrissionPage/tab.py b/DrissionPage/tab.py index a59bfc4..a9209e7 100644 --- a/DrissionPage/tab.py +++ b/DrissionPage/tab.py @@ -1,7 +1,4 @@ # -*- coding: utf-8 -*- - -from __future__ import unicode_literals - from functools import partial from json import dumps, loads from logging import getLogger diff --git a/README.md b/README.md index 1f912a1..ac4a7e6 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ DrissionPage,即 driver 和 session 组合而成的 page。 除了合并两者,本库还以网页为单位封装了常用功能,提供非常简便的操作和语句,在用于网页自动化操作时,减少考虑细节,专注功能实现,使用更方便。 一切从简,尽量提供简单直接的使用方法,对新手更友好。 -# 🔆 3.0 版隆重推出(测试中) +# 🔆 3.0 版隆重推出 以前的版本是对 selenium 进行重新封装实现的。从 3.0 开始,作者另起炉灶,对底层进行了重新开发,摆脱对 selenium 的依赖,增强了功能,提升了运行效率。 3.0 全新开发的页面对象是`WebPage`,支持 chromium 内核的浏览器(如 chrome 和 edge)。除了保持之前的功能,比依赖 selenium 的`MixPage`有以下优点: diff --git a/docs/MixPage使用方法/创建页面对象.md b/docs/MixPage使用方法/创建页面对象.md index 6023035..3be9ea3 100644 --- a/docs/MixPage使用方法/创建页面对象.md +++ b/docs/MixPage使用方法/创建页面对象.md @@ -47,9 +47,8 @@ page = MixPage('s') - ini_path:ini 文件路径,为`None`则读取默认 ini 文件 ```python -from Drission import MixPage # 导入 DriverOptions -from DrissionPage.config import DriverOptions +from DrissionPage import MixPage, DriverOptions # 创建浏览器配置对象,从文件读取配置,并增加设置浏览器静音和不加载图片 do = DriverOptions().set_mute().set_no_imgs() @@ -59,23 +58,23 @@ page = MixPage(driver_options=do) ## `SessionOptions`类 -`SessionOptions`用于管理创建浏览器时的配置,浏览器创建后再修改这个配置是没有效果的。 -`SessionOptions`对象能实现链式操作。 +`SessionOptions`用于管理创建`Session`对象时的配置,内置了常用的配置,并能实现链式操作。详细使用方法见“启动配置”一节。 初始化参数: - read_file:是否从 ini 文件中读取配置信息 - ini_path:ini 文件路径,为`None`则读取默认 ini 文件 +!>**注意:**
`Session`对象创建后再修改这个配置是没有效果的。 + ```python -from Drission import MixPage -# 导入 SessionOptions -from DrissionPage.config import SessionOptions +# 导入 DriverOptions +from DrissionPage import MixPage,SessionOptions proxies = {'http': 'http://127.0.0.1:1080', 'https': 'https://127.0.0.1:1080'} -# 创建浏览器配置对象,不从 ini 文件读取,并设置代理信息 +# 创建配置对象,不从 ini 文件读取,并设置代理信息 so = SessionOptions(read_file=False).set_proxies(proxies) # 用该配置创建页面对象(s 模式) page = MixPage(mode='s', session_options=so) @@ -89,8 +88,7 @@ page = MixPage(mode='s', session_options=so, driver_options=do) # 传入`Drission`对象创建 -在入门指南的基本概念一节里,我们讲过`Drission`对象相当于驱动器的角色。事实上,上述两种方式,`MixPage`都会自动创建一个`Drission`对象用于管理与网站或浏览器的连接,我们当然也可以手动创建并传入`MixPage` -。 +在入门指南的基本概念一节里,我们讲过`Drission`对象相当于驱动器的角色。事实上,上述两种方式,`MixPage`都会自动创建一个`Drission`对象用于管理与网站或浏览器的连接,我们当然也可以手动创建并传入`MixPage`。 `Drission`一般是不用手动创建的,要手动创建的时候,一般是用于i以下几种情况: - 指定使用某个配置文件 @@ -120,7 +118,7 @@ page = MixPage(drission=d) 多页面对象间共用驱动器,如多个`MixPage`控制一个浏览器: ```python -from Drission import MixPage +from DrissionPage import MixPage page1 = MixPage() d = page1.drission diff --git a/docs/README.md b/docs/README.md index 7bf4047..8f5571b 100644 --- a/docs/README.md +++ b/docs/README.md @@ -19,7 +19,7 @@ DrissionPage,即 driver 和 session 组合而成的 page。 除了合并两者,本库还以网页为单位封装了常用功能,提供非常简便的操作和语句,在用于网页自动化操作时,减少考虑细节,专注功能实现,使用更方便。 一切从简,尽量提供简单直接的使用方法,对新手更友好。 -# 🔆 3.0 版隆重推出(测试中) +# 🔆 3.0 版隆重推出 以前的版本是对 selenium 进行重新封装实现的。从 3.0 开始,作者另起炉灶,对底层进行了重新开发,摆脱对 selenium 的依赖,增强了功能,提升了运行效率。 3.0 全新开发的页面对象是`WebPage`,支持 chromium 内核的浏览器(如 chrome 和 edge)。除了保持之前的功能,比依赖 selenium 的`MixPage`有以下优点: @@ -34,9 +34,9 @@ DrissionPage,即 driver 和 session 组合而成的 page。 - 把 iframe 看作普通元素,获取后可直接在其中查找元素,逻辑更清晰 -- 可以同时操作浏览器中的多个标签页,即使标签页为非激活状态 +- 可以同时操作浏览器中的多个标签页,即使标签页为非激活状态,无需切换 -- 可以直接读取浏览器缓存来保持图片,无需用 GUI 点击保存 +- 可以直接读取浏览器缓存来保存图片,无需用 GUI 点击另存 - 可以对整个网页截图,包括视口外的部分(90以上版本浏览器支持) diff --git a/docs/WebPage使用方法/3.2访问网页.md b/docs/WebPage使用方法/3.2访问网页.md index 1ec4bfb..93bf508 100644 --- a/docs/WebPage使用方法/3.2访问网页.md +++ b/docs/WebPage使用方法/3.2访问网页.md @@ -55,7 +55,7 @@ page.get('https://www.163.com', retry=1, interval=1, timeout=1.5) from DrissionPage import WebPage page = WebPage() -page.set_page_load_strategy.set_eager() +page.set_page_load_strategy() ``` # ✔️ s 模式 diff --git a/docs/WebPage使用方法/3.3查找元素.md b/docs/WebPage使用方法/3.3查找元素.md index 204fd76..984ce91 100644 --- a/docs/WebPage使用方法/3.3查找元素.md +++ b/docs/WebPage使用方法/3.3查找元素.md @@ -1,4 +1,4 @@ -本节介绍如何获取元素对象。 +本节介绍如何获取元素对象。可以从页面中查找元素,也可以在元素中查找子元素。 无论是数据采集还是页面自动化,定位元素都是重中之重的的技能,浏览器开发者工具虽然可以直接复制绝对 xpath 或 css 路径,但这样做一来代码繁琐,可读性低,二来难以应付动态变化的页面。 本库提供一套简洁易用的语法,用于快速定位元素,并且内置等待功能、支持链式查找,减少了代码的复杂性。 @@ -13,6 +13,8 @@ d 模式的元素还有专门用于处理 shadow dom 的`shadow_root`属性。 # ✔️ 示例 +先看一些示例,感性认识一下,后面在详细讲解用法。 + ## 📍 简单示例 ```html @@ -81,7 +83,8 @@ titles = ul_ele.eles('tag:a') for i in titles: print(i.text) -"""输出: +""" +输出: 全部推荐项目 前沿技术 智能硬件 @@ -93,33 +96,41 @@ IOT/物联网/边缘计算 # ✔️ 查找元素方法 -以下方法,既可用于在页面中查找元素,也可用于在元素中查找子元素。 +## 📍 查找单个元素 -## ele() +查找单个元素使用`ele()`方法。 -此方法用于查找并返回第一个匹配的元素,d 模式下返回`ChromiumElement`,s 模式下返回`SessionElement`,用 xpath 获取元素属性时,直接返回属性文本。查找不到结果则返回`None`。 +页面对象和元素对象都拥有此方法,用于查找第一个匹配条件的元素。 +d 模式下返回`ChromiumElement`对象,s 模式下返回`SessionElement`对象,没有找到返回`None`。 +在元素下查找子元素时,还可以用 xpath 获取元素属性,直接返回属性文本。 +页面对象和元素对象的`ele()`方法参数名称稍有不同,但用法一样。 参数: -- loc_or_str(元素对象拥有):元素的定位信息,可以是 loc 元组,或查询字符串 -- loc_or_ele(页面对象拥有):元素的定位信息,可以是元素对象,loc 元组,或查询字符串 +- loc_or_str(元素对象):元素的定位信息,可以是 loc 元组,或查询字符串 +- loc_or_ele(页面对象):元素的定位信息,可以是元素对象,loc 元组,或查询字符串 - timeout:查找元素超时时间,默认与元素所在页面等待时间一致,s 模式下无效 -返回:s 模式下返回`SessionElement`,d 模式下返回`DriverElement`,或用 xpath 获取到的属性值 +返回:s 模式下返回`SessionElement`,d 模式下返回`ChromiumElement`;或用 xpath 获取到的属性值 ```python +from DrissionPage import WebPage + +page = WebPage() + # 在页面内查找元素 ele1 = page.ele('search text') # 在元素内查找后代元素 ele2 = ele1.ele('search text') - -# 使用 xpath 获取后代中第一个 div 元素的 class 属性 -class = ele1.ele('xpath://div/@class') +# 使用 xpath 获取后代中第一个 div 元素的 class 属性(元素内查找可用) +ele_class = ele1.ele('xpath://div/@class') ``` -## eles() +## 📍 查找多个元素 + +查找多个元素使用`eles()`方法。 此方法与`ele()`相似,但返回的是匹配到的所有元素组成的列表,用 xpath 获取元素属性时,返回属性文本组成的列表。 @@ -128,7 +139,7 @@ class = ele1.ele('xpath://div/@class') - loc_or_str:元素的定位信息,可以是 loc 元组,或查询字符串 - timeout:查找元素超时时间,默认与元素所在页面等待时间一致,s 模式下无效 -返回:s 模式下返回`SessionElement`组成的列表,d 模式下返回`DriverElement`组成的列表,或用 xpath 获取到的属性值组成的列表 +返回:s 模式下返回`SessionElement`组成的列表,d 模式下返回`ChromiumElement`组成的列表;或用 xpath 获取到的属性值组成的列表 ```python # 获取 ele 元素内的所有 p 元素 @@ -137,7 +148,18 @@ p_eles = ele.eles('tag:p') print(p_eles[0]) ``` -## s_ele() +## 📍 查找单个静态元素 + +静态元素即 s 模式的`SessionElement`元素对象,是纯文本构造的,因此用它处理速度非常快速。对于复杂的页面,要在成百上千个元素中采集数据时,转换为静态元素可把速度提升几个数量级。作者曾在采集的时候,用同一套逻辑,仅仅把元素转换为静态,就把一个要 30 秒才采集 完成的页面,加速到零点几秒完成。 +我们甚至可以把整个页面转换为静态元素,再在其中提取信息。 +当然,这种元素不能进行点击等交互。 +s 模式下,所有元素本身就是静态元素,d 模式下,用`s_ele()`可在把查找到的动态元素转换为静态元素输出,或者获取元素或页面本身的静态元素副本。 + +`s_ele()`方法 + +页面对象和元素对象都拥有此方法,用于查找第一个匹配条件的元素,获取其静态版本。 + +页面对象和元素对象的`s_ele()`方法参数名称稍有不同,但用法一样。 此方法用于在一个元素下查找后代元素,以`SessionElement`形式返回结果(xpath 获取属性值时依然是返回`str`),也可以直接将一个元素或页面转换为`SessionElement`版本。 @@ -147,24 +169,39 @@ s 模式下这个方法和`ele()`是一样的。 参数: -- loc_or_str(元素对象拥有):元素的定位信息,可以是 loc 元组,或查询字符串。为`None`时直接返回当前元素的`SessionElemnet`版本 -- loc_or_ele(页面对象拥有):元素的定位信息,可以是 loc 元组,或查询字符串。为`None`时直接返回当前页面的 `SessionElemnet`版本 +- loc_or_str(元素对象):元素的定位信息,可以是 loc 元组,或查询字符串。为`None`时直接返回当前元素的`SessionElemnet`版本 +- loc_or_ele(页面对象):元素的定位信息,可以是 loc 元组,或查询字符串。为`None`时直接返回当前页面的 `SessionElemnet`版本 返回:`SessionElement`,或用 xpath 获取到的属性值 +!>**注意:**
页面对象和元素对象的`s_ele()`方法不能搜索到在 frame 里的元素,页面对象的静态版本也不能搜索 frame 里的元素。要使用 frame 里元素的静态版本,可先获取该元素,再转换。而使用`ChromiumFrame`对象,则可以直接用`s_ele()`查找元素,这在后面章节再讲述。 + ```python -# 获取元素或页面的的 SessionElement 版本 -ele2 = ele1.s_ele() -ele2 = page.s_ele() +from DrissionPage import WebPage -# 在 ele1 元素下查找元素,并以 SessionElemnet 返回 -ele2 = ele1.s_ele('search text') +p = WebPage() -# 在页面下查找元素,并以 SessionElemnet 返回 -ele = page.s_ele('search text') +# 在页面中查找元素,获取其静态版本 +ele1 = page.s_ele('search text') + +# 在动态元素中查找元素,获取其静态版本 +ele = page.ele('search text') +ele2 = ele.s_ele() + +# 获取页面元素的静态副本(不传入参数) +s_page = page.s_ele() + +# 获取动态元素的静态副本 +s_ele = ele.s_ele() + +# 在静态副本中查询下级元素(因为已经是静态元素,用ele()查找结果也是静态) +ele3 = s_page.ele('search text') +ele4 = s_ele.ele('search text') ``` -## s_eles() +## 📍 查找多个静态元素 + +查找多个静态元素使用`s_eles()`方法。 此方法与`s_ele()`相似,但返回的是匹配到的所有元素组成的列表,或属性值组成的列表。 @@ -174,19 +211,26 @@ ele = page.s_ele('search text') 返回:`SessionElement`组成的列表,或用 xpath 获取到的属性值组成的列表 -## active_ele +```python +from DrissionPage import WebPage -该属性返回当前页面焦点所在元素。d 模式独有。 +p = WebPage() +for ele in p.s_eles('search text'): + print(ele.text) +``` + +## 📍 获取当前焦点元素 + +使用方法`active_ele()`获取页面上焦点所在元素,为 d 模式独有。 ```python ele = page.active_ele ``` -## shadow_root +## 📍 获取 shadow_root -`DriverElement`元素除了以上方法和属性外,还有`shadow_root`属性,用于获取其内部的 shadow_root 元素。 -该属性返回的是一个`ShadowRootElement`,类似于`DriverElement`,功能比`DriverElement`少。但也有`ele()`和`eles()`方法,可直接搜索其下的元素,返回 `DriverElement` -元素。返回的`DriverElement`和普通的没有区别。 +d 模式元素如果包含 shadow_root,可使用`shadow_root`属性获取。 +该属性返回的是一个`ChromiumShadowRootElement`对象,用法与`ChromiumElement`相似。也能使用各种元素查找方式,返回内部元素或相对位置元素,返回 `ChromiumElement`元素。返回的`ChromiumElement`和普通的没有区别。 ```python # 获取一个元素下是 shadow root @@ -281,8 +325,7 @@ ele2 = ele1.ele('css:div[abc\@def="v"]') - 属性名本身以`-`开头 -!> **注意:** -如果属性中包含特殊字符,如包含`@`,用这个方式不能正确匹配到,须使用 css selector 方式查找。且特殊字符要用`\`转义。 +!> **注意:**:
如果属性中包含特殊字符,如包含`@`,用这个方式不能正确匹配到,须使用 css selector 方式查找。且特殊字符要用`\`转义。 ```python # 查找 name 属性为 name 且 class 属性包含 cls 文本的元素 @@ -400,9 +443,7 @@ ele2 = ele1.ele('xpath://div') txt = ele1.ele('xpath://div/@class') ``` -?> **Tips:**
-查找元素的后代时,selenium 原生代码要求 xpath 前面必须加`.`,否则会变成在全个页面中查找。笔者觉得这个设计是画蛇添足,既然已经通过元素查找了,自然应该只查找这个元素内部的元素。所以,用 xpath -在元素下查找时,最前面`//`或`/`前面的`.`可以省略。 +?> **Tips:**
查找元素的后代时,selenium 原生代码要求 xpath 前面必须加`.`,否则会变成在全个页面中查找。作者觉得这个设计是画蛇添足,既然已经通过元素查找了,自然应该只查找这个元素内部的元素。所以,用 xpath 在元素下查找时,最前面`//`或`/`前面的`.`可以省略。 ## 📍 selenium 的 loc 元组 @@ -445,9 +486,9 @@ ele2 = ele1.ele('some text', timeout=1) 以下方法可以以某元素为基准,在 DOM 中按照条件获取其兄弟元素、祖先元素、文档前后元素。 除获取元素外,还能通过 xpath 获取任意节点内容,如文本节点、注释节点。这在处理元素和文本节点混排的时候非常有用。 -## parent() +## 📍 获取父级元素 -此方法获取当前元素某一级父元素,可指定筛选条件或层数。 +`parent()`方法获取当前元素某一级父元素,可指定筛选条件或层数。 参数: @@ -463,14 +504,14 @@ ele2 = ele1.parent(2) ele2 = ele1.parent('#id1') ``` -## next() +## 📍 获取后面单个兄弟元素 -此方法返回当前元素后面的某一个兄弟元素,可指定筛选条件和第几个。 +`next()`方法返回当前元素后面的某一个同级元素,可指定筛选条件和第几个。 参数: -- index:查询结果中的第几个 - filter_loc:用于筛选元素的查询语法 +- index:查询结果中的第几个 - timeout:查找元素的超时时间 返回:本元素后面某个兄弟元素或节点文本 @@ -483,15 +524,15 @@ ele2 = ele1.next() ele2 = ele1.next(3) # 获取 ele1 后面第 3 个 div 兄弟元素 -ele2 = ele1.next(3, 'tag:div') +ele2 = ele1.next('tag:div', 3) # 获取 ele1 后面第一个文本节点的文本 -txt = ele1.next(1, 'xpath:text()') +txt = ele1.next('xpath:text()', 1) ``` -## nexts() +## 📍 获取后面多个兄弟元素 -此方法返回后面全部符合条件的兄弟元素或节点组成的列表,可用查询语法筛选。 +`nexts()`方法返回当前元素后面全部符合条件的同级元素或节点组成的列表,可用查询语法筛选。 参数: @@ -511,14 +552,14 @@ divs = ele1.nexts('tag:div') txts = ele1.nexts('xpath:text()') ``` -## prev() +## 📍 获取前面单个兄弟元素 -此方法返回当前元素前面的某一个兄弟元素,可指定筛选条件和第几个。 +`prev()`方法返回当前元素前面的某一个同级元素,可指定筛选条件和第几个。 参数: -- index:查询结果中的第几个 - filter_loc:用于筛选元素的查询语法 +- index:查询结果中的第几个 - timeout:查找元素的超时时间 返回:本元素前面某个兄弟元素或节点文本 @@ -537,9 +578,9 @@ ele2 = ele1.prev(3, 'tag:div') txt = ele1.prev(1, 'xpath:text()') ``` -## prevs() +## 📍 获取前面多个兄弟元素 -此方法返回前面全部符合条件的兄弟元素或节点组成的列表,可用查询语法筛选。 +`prevs()`方法返回当前元素前面全部符合条件的同级元素或节点组成的列表,可用查询语法筛选。 参数: @@ -556,14 +597,14 @@ eles = ele1.prevs() divs = ele1.prevs('tag:div') ``` -## after() +## 📍 在后面文档中查找单个元素 -此方法返回当前元素后面的某一个元素,可指定筛选条件和第几个。这个方法查找范围不局限在兄弟元素间,而是整个 DOM 文档。 +`after()`方法返回当前元素后面的某一个元素,可指定筛选条件和第几个。这个方法查找范围不局限在兄弟元素间,而是整个 DOM 文档。 参数: -- index:查询结果中的第几个 - filter_loc:用于筛选元素的查询语法 +- index:查询结果中的第几个 - timeout:查找元素的超时时间 返回:本元素后面某个元素或节点 @@ -573,15 +614,15 @@ divs = ele1.prevs('tag:div') ele2 = ele1.after(3) # 获取 ele1 后面第 3 个 div 元素 -ele2 = ele1.after(3, 'tag:div') +ele2 = ele1.after('tag:div', 3) # 获取 ele1 后面第一个文本节点的文本 -txt = ele1.after(1, 'xpath:text()') +txt = ele1.after('xpath:text()', 1) ``` -## afters() +## 📍 在后面文档中查找多个元素 -此方法返回后面符合条件的全部元素或节点组成的列表,可用查询语法筛选。这个方法查找范围不局限在兄弟元素间,而是整个 DOM 文档。 +`afters()`方法返回当前元素后面符合条件的全部元素或节点组成的列表,可用查询语法筛选。这个方法查找范围不局限在兄弟元素间,而是整个 DOM 文档。 参数: @@ -598,14 +639,14 @@ eles = ele1.afters() divs = ele1.afters('tag:div') ``` -## before() +## 📍 在前面文档中查找单个元素 -此方法返回当前元素前面的某一个元素,可指定筛选条件和第几个。这个方法查找范围不局限在兄弟元素间,而是整个 DOM 文档。 +`before()`方法返回当前元素前面的某一个元素,可指定筛选条件和第几个。这个方法查找范围不局限在兄弟元素间,而是整个 DOM 文档。 参数: -- index:查询结果中的第几个 - filter_loc:用于筛选元素的查询语法 +- index:查询结果中的第几个 - timeout:查找元素的超时时间 返回:本元素前面某个元素或节点 @@ -615,15 +656,15 @@ divs = ele1.afters('tag:div') ele2 = ele1.before(3) # 获取 ele1 前面第 3 个 div 元素 -ele2 = ele1.before(3, 'tag:div') +ele2 = ele1.before('tag:div', 3) # 获取 ele1 前面第一个文本节点的文本 -txt = ele1.before(1, 'xpath:text()') +txt = ele1.before('xpath:text()', 1) ``` -## befores() +## 📍 在前面文档中查找多个元素 -此方法返回前面全部符合条件的元素或节点组成的列表,可用查询语法筛选。这个方法查找范围不局限在兄弟元素间,而是整个 DOM 文档。 +`befores()`方法返回当前元素前面全部符合条件的元素或节点组成的列表,可用查询语法筛选。这个方法查找范围不局限在兄弟元素间,而是整个 DOM 文档。 参数: @@ -640,14 +681,18 @@ eles = ele1.befores() divs = ele1.befores('tag:div') ``` +# ✔️ 查找 frame 里的元素 + +与 selenium 不同,本库可以直接查找 frame 里面的元素,而无需切入切出,大大简化了程序逻辑,使用更便捷。 + +未完待续。。。 + # ✔️ `ShadowRootElement`相关查找 -本库把 shadow-root 也作为元素对象看待,是为`ShadowRootElement`对象。对`ShadowRootElement`对象可与普通元素一样查找下级元素和 DOM 内相对定位,但不能用页面布局相对定位。 -对`ShadowRootElement`对象进行相对定位时,把它看作其父对象内部的第一个对象,其余定位逻辑与普通对象一致。 +本库把 shadow-root 也作为元素对象看待,是为`ChromiumShadowRootElement`对象。该对象可与普通元素一样查找下级元素和 DOM 内相对定位。 +对`ChromiumShadowRootElement`对象进行相对定位时,把它看作其父对象内部的第一个对象,其余定位逻辑与普通对象一致。 -!> **注意:**
-如果`ShadowRootElement`元素的下级元素中有其它`ShadowRootElement`元素,那这些下级`ShadowRootElement` -元素内部是无法直接通过定位语句查找到的,只能先定位到其父元素,再用`shadow-root`属性获取。 +!> **注意:**
如果`ChromiumShadowRootElement`元素的下级元素中有其它`ChromiumShadowRootElement`元素,那这些下级`ChromiumShadowRootElement`元素内部是无法直接通过定位语句查找到的,只能先定位到其父元素,再用`shadow-root`属性获取。 ```python # 获取一个 shadow-root 元素 @@ -658,8 +703,8 @@ ele1 = sr_ele.ele('tag:div') # 用相对定位获取其它元素 ele1 = sr_ele.parent(2) -ele1 = sr_ele.next(1, 'tag:div') -ele1 = sr_ele.after(1, 'tag:div') +ele1 = sr_ele.next('tag:div', 1) +ele1 = sr_ele.after('tag:div', 1) eles = sr_ele.nexts('tag:div') # 定位下级元素中的 shadow+-root 元素 @@ -701,8 +746,8 @@ ele2 = ele1('x://div[@class="ele_class"]') | css | c | | shadow_root | sr | -# Tips +# ✔️ Tips -- 从一个`DriverElement`元素获取到的`SessionElement`版本,依然能够使用相对定位方法定位祖先或兄弟元素。 +- 从一个`ChromiumElement`元素获取到的`SessionElement`版本,依然能够使用相对定位方法定位祖先或兄弟元素。但如果元素在 frame 中,相对定位不能超越 frame 文档。 - `SessionElement`和`SessionPage`的`ele()`和`eles()`方法也有`timeout`参数,但它是不生效的,仅用于保持与 d 模式元素书写一致,便于无差别的调用。 - 定位语句内容与关键字重复时,请使用 xpath 或 css selector 代替。 diff --git a/docs/_sidebar.md b/docs/_sidebar.md index e94bec1..88a84a0 100644 --- a/docs/_sidebar.md +++ b/docs/_sidebar.md @@ -1,51 +1,51 @@ * [⭐️ 1 简介](README.md) * [🧭 2 入门指南](#) - - * [🔥 2.1 基本概念](入门指南\基本概念.md) - * [👍 2.2 快速上手](入门指南\快速上手.md) - * [🍀 2.3 特性演示](#) - * [🌿 与 requests 对比](入门指南\特性演示\与requests代码对比.md) - * [🌿 与 selenium 对比](入门指南\特性演示\与selenium代码对比.md) - * [🌿 模式切换](入门指南\特性演示\模式切换.md) - * [🌿 获取并打印元素属性](入门指南\特性演示\获取并打印元素属性.md) - * [🌿 下载文件](入门指南\特性演示\下载文件.md) + + * [🔥 2.1 基本概念](入门指南\基本概念.md) + * [👍 2.2 快速上手](入门指南\快速上手.md) + * [🍀 2.3 特性演示](#) + * [🌿 与 requests 对比](入门指南\特性演示\与requests代码对比.md) + * [🌿 与 selenium 对比](入门指南\特性演示\与selenium代码对比.md) + * [🌿 模式切换](入门指南\特性演示\模式切换.md) + * [🌿 获取并打印元素属性](入门指南\特性演示\获取并打印元素属性.md) + * [🌿 下载文件](入门指南\特性演示\下载文件.md) * [🛠 3 WebPage 使用方法](#) - - * [🔨 3.1 创建页面对象](WebPage使用方法\3.1创建页面对象.md) - * [🔨 3.2 访问网页](WebPage使用方法\3.2访问网页.md) - * [🔨 3.3 查找元素](WebPage使用方法\3.3查找元素.md) + + * [🔨 3.1 创建页面对象](WebPage使用方法\3.1创建页面对象.md) + * [🔨 3.2 访问网页](WebPage使用方法\3.2访问网页.md) + * [🔨 3.3 查找元素](WebPage使用方法\3.3查找元素.md) * [🛠 4 MixPage 使用方法](#) - - * [🔨 4.1 创建页面对象](MixPage使用方法\创建页面对象.md) - * [🔨 4.2 访问网页](MixPage使用方法\访问网页.md) - * [🔨 4.3 查找页面元素](MixPage使用方法\查找页面元素.md) - * [🔨 4.4 获取元素信息](MixPage使用方法\获取元素信息.md) - * [🔨 4.5 元素操作](MixPage使用方法\元素操作.md) - * [🔨 4.6 获取网页信息](MixPage使用方法\获取网页信息.md) - * [🔨 4.7 页面操作](MixPage使用方法\页面操作.md) - * [🔨 4.8 启动配置](#) - * [🔧 概述](MixPage使用方法\启动配置\概述.md) - * [🔧 Chrome 启动配置](MixPage使用方法\启动配置\Chrome启动配置.md) - * [🔧 Session 启动配置](MixPage使用方法\启动配置\Session启动配置.md) - * [🔧 使用配置文件](MixPage使用方法\启动配置\使用配置文件.md) - * [🔨 4.9 下载文件](MixPage使用方法\下载文件.md) - * [🔨 4.10 监听浏览器网络数据](MixPage使用方法\监听浏览器网络数据.md) - * [🔨 4.11 cookies 的使用](MixPage使用方法\cookies的使用.md) - * [🔨 4.12 Drission 对象](MixPage使用方法\Drission对象.md) - * [🔨 4.13 对接 selenium 及 requests 代码](MixPage使用方法\对接selenium及requests代码.md) - * [🔨 4.14 使用其它系统或浏览器](MixPage使用方法\使用其它系统或浏览器.md) - * [🔨 4.15 DriverPage 和 SessionPage](MixPage使用方法\DriverPage和SessionPage.md) - * [🔨 4.16 打包程序](MixPage使用方法\打包程序.md) + + * [🔨 4.1 创建页面对象](MixPage使用方法\创建页面对象.md) + * [🔨 4.2 访问网页](MixPage使用方法\访问网页.md) + * [🔨 4.3 查找页面元素](MixPage使用方法\查找页面元素.md) + * [🔨 4.4 获取元素信息](MixPage使用方法\获取元素信息.md) + * [🔨 4.5 元素操作](MixPage使用方法\元素操作.md) + * [🔨 4.6 获取网页信息](MixPage使用方法\获取网页信息.md) + * [🔨 4.7 页面操作](MixPage使用方法\页面操作.md) + * [🔨 4.8 启动配置](#) + * [🔧 概述](MixPage使用方法\启动配置\概述.md) + * [🔧 Chrome 启动配置](MixPage使用方法\启动配置\Chrome启动配置.md) + * [🔧 Session 启动配置](MixPage使用方法\启动配置\Session启动配置.md) + * [🔧 使用配置文件](MixPage使用方法\启动配置\使用配置文件.md) + * [🔨 4.9 下载文件](MixPage使用方法\下载文件.md) + * [🔨 4.10 监听浏览器网络数据](MixPage使用方法\监听浏览器网络数据.md) + * [🔨 4.11 cookies 的使用](MixPage使用方法\cookies的使用.md) + * [🔨 4.12 Drission 对象](MixPage使用方法\Drission对象.md) + * [🔨 4.13 对接 selenium 及 requests 代码](MixPage使用方法\对接selenium及requests代码.md) + * [🔨 4.14 使用其它系统或浏览器](MixPage使用方法\使用其它系统或浏览器.md) + * [🔨 4.15 DriverPage 和 SessionPage](MixPage使用方法\DriverPage和SessionPage.md) + * [🔨 4.16 打包程序](MixPage使用方法\打包程序.md) * [💖 5 实用示例](#) - - * [🧡 自动登录码云](实用示例\自动登录码云.md) - * [🧡 获取各国疫情排名](实用示例\获取各国疫情排名.md) - * [🧡 下载星巴克产品图片](实用示例\下载星巴克产品图片.md) - * [🧡 同时操作多个浏览器](实用示例\同时操作多个浏览器.md) + + * [🧡 自动登录码云](实用示例\自动登录码云.md) + * [🧡 获取各国疫情排名](实用示例\获取各国疫情排名.md) + * [🧡 下载星巴克产品图片](实用示例\下载星巴克产品图片.md) + * [🧡 同时操作多个浏览器](实用示例\同时操作多个浏览器.md) * [⚡️ 6 Tips大集合](Tips大集合.md)