修复frame内元素无法使用s_ele()问题;更新文档

This commit is contained in:
g1879 2022-12-16 16:43:13 +08:00
parent d68763c398
commit 925a7400c7
16 changed files with 234 additions and 412 deletions

View File

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

View File

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

View File

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

View File

@ -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'<ChromiumFrame {self._inner_ele.tag} {" ".join(attrs)}>'
#
# @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}</{tag}>'
#
# 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'])

View File

@ -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: ...
@ -341,7 +345,8 @@ 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 = ...,
def run_script(page_or_ele: Union[ChromiumBase, ChromiumElement, ChromiumShadowRootElement], script: str,
as_expr: bool = ...,
timeout: float = ..., args: tuple = ...) -> Any: ...

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -21,7 +21,7 @@ DrissionPage即 driver 和 session 组合而成的 page。
除了合并两者,本库还以网页为单位封装了常用功能,提供非常简便的操作和语句,在用于网页自动化操作时,减少考虑细节,专注功能实现,使用更方便。
一切从简,尽量提供简单直接的使用方法,对新手更友好。
# 🔆 3.0 版隆重推出(测试中)
# 🔆 3.0 版隆重推出
以前的版本是对 selenium 进行重新封装实现的。从 3.0 开始,作者另起炉灶,对底层进行了重新开发,摆脱对 selenium 的依赖,增强了功能,提升了运行效率。
3.0 全新开发的页面对象是`WebPage`,支持 chromium 内核的浏览器(如 chrome 和 edge。除了保持之前的功能比依赖 selenium 的`MixPage`有以下优点:

View File

@ -47,9 +47,8 @@ page = MixPage('s')
- ini_pathini 文件路径,为`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_pathini 文件路径,为`None`则读取默认 ini 文件
!>**注意:**<br>`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

View File

@ -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以上版本浏览器支持

View File

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

View File

@ -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 获取到的属性值
!>**注意:**<br>页面对象和元素对象的`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 方式查找。且特殊字符要用`\`转义。
!> **注意:**<br>如果属性中包含特殊字符,如包含`@`,用这个方式不能正确匹配到,须使用 css selector 方式查找。且特殊字符要用`\`转义。
```python
# 查找 name 属性为 name 且 class 属性包含 cls 文本的元素
@ -400,9 +443,7 @@ ele2 = ele1.ele('xpath://div')
txt = ele1.ele('xpath://div/@class')
```
?> **Tips:** <br>
查找元素的后代时selenium 原生代码要求 xpath 前面必须加`.`,否则会变成在全个页面中查找。笔者觉得这个设计是画蛇添足,既然已经通过元素查找了,自然应该只查找这个元素内部的元素。所以,用 xpath
在元素下查找时,最前面`//``/`前面的`.`可以省略。
?> **Tips:** <br>查找元素的后代时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`对象进行相对定位时,把它看作其父对象内部的第一个对象,其余定位逻辑与普通对象一致。
!> **注意:** <br>
如果`ShadowRootElement`元素的下级元素中有其它`ShadowRootElement`元素,那这些下级`ShadowRootElement`
元素内部是无法直接通过定位语句查找到的,只能先定位到其父元素,再用`shadow-root`属性获取。
!> **注意:** <br>如果`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 代替。