mirror of
https://gitee.com/g1879/DrissionPage.git
synced 2024-12-10 04:00:23 +08:00
444 lines
17 KiB
Python
444 lines
17 KiB
Python
# -*- coding:utf-8 -*-
|
||
"""
|
||
@Author : g1879
|
||
@Contact : g1879@qq.com
|
||
@Copyright: (c) 2024 by g1879, Inc. All Rights Reserved.
|
||
@License : BSD 3-Clause.
|
||
"""
|
||
from time import perf_counter, sleep
|
||
|
||
from .locator import is_str_loc
|
||
from .._elements.none_element import NoneElement
|
||
|
||
|
||
class SessionElementsList(list):
|
||
def __init__(self, owner=None, *args):
|
||
super().__init__(*args)
|
||
self._owner = owner
|
||
|
||
@property
|
||
def get(self):
|
||
return Getter(self)
|
||
|
||
@property
|
||
def filter(self):
|
||
return SessionFilter(self)
|
||
|
||
@property
|
||
def filter_one(self):
|
||
return SessionFilterOne(self)
|
||
|
||
|
||
class ChromiumElementsList(SessionElementsList):
|
||
|
||
@property
|
||
def filter(self):
|
||
return ChromiumFilter(self)
|
||
|
||
@property
|
||
def filter_one(self):
|
||
return ChromiumFilterOne(self)
|
||
|
||
def search(self, displayed=None, checked=None, selected=None, enabled=None, clickable=None,
|
||
have_rect=None, have_text=None, tag=None):
|
||
return _search(self, displayed=displayed, checked=checked, selected=selected, enabled=enabled,
|
||
clickable=clickable, have_rect=have_rect, have_text=have_text, tag=tag)
|
||
|
||
def search_one(self, index=1, displayed=None, checked=None, selected=None, enabled=None, clickable=None,
|
||
have_rect=None, have_text=None, tag=None):
|
||
return _search_one(self, index=index, displayed=displayed, checked=checked, selected=selected,
|
||
enabled=enabled, clickable=clickable, have_rect=have_rect, have_text=have_text, tag=tag)
|
||
|
||
|
||
class SessionFilterOne(object):
|
||
def __init__(self, _list):
|
||
self._list = _list
|
||
self._index = 1
|
||
|
||
def __call__(self, index=1):
|
||
self._index = index
|
||
return self
|
||
|
||
def tag(self, name, equal=True):
|
||
num = 0
|
||
name = name.lower()
|
||
if equal:
|
||
for i in self._list:
|
||
if not isinstance(i, str) and i.tag == name:
|
||
num += 1
|
||
if self._index == num:
|
||
return i
|
||
else:
|
||
for i in self._list:
|
||
if not isinstance(i, str) and i.tag != name:
|
||
num += 1
|
||
if self._index == num:
|
||
return i
|
||
return NoneElement(self._list._owner, 'tag()', args={'name': name, 'equal': equal, 'index': self._index})
|
||
|
||
def attr(self, name, value, equal=True):
|
||
return self._get_attr(name, value, 'attr', equal=equal)
|
||
|
||
def text(self, text, fuzzy=True, contain=True):
|
||
num = 0
|
||
if contain:
|
||
for i in self._list:
|
||
t = i if isinstance(i, str) else i.raw_text
|
||
if (fuzzy and text in t) or (not fuzzy and text == t):
|
||
num += 1
|
||
if self._index == num:
|
||
return i
|
||
else:
|
||
for i in self._list:
|
||
t = i if isinstance(i, str) else i.raw_text
|
||
if (fuzzy and text not in t) or (not fuzzy and text != t):
|
||
num += 1
|
||
if self._index == num:
|
||
return i
|
||
return NoneElement(self._list._owner, 'text()',
|
||
args={'text': text, 'fuzzy': fuzzy, 'contain': contain, 'index': self._index})
|
||
|
||
def _get_attr(self, name, value, method, equal=True):
|
||
num = 0
|
||
if equal:
|
||
for i in self._list:
|
||
if not isinstance(i, str) and getattr(i, method)(name) == value:
|
||
num += 1
|
||
if self._index == num:
|
||
return i
|
||
else:
|
||
for i in self._list:
|
||
if not isinstance(i, str) and getattr(i, method)(name) != value:
|
||
num += 1
|
||
if self._index == num:
|
||
return i
|
||
return NoneElement(self._list._owner, f'{method}()',
|
||
args={'name': name, 'value': value, 'equal': equal, 'index': self._index})
|
||
|
||
|
||
class SessionFilter(SessionFilterOne):
|
||
|
||
def __iter__(self):
|
||
return iter(self._list)
|
||
|
||
def __next__(self):
|
||
return next(self._list)
|
||
|
||
def __len__(self):
|
||
return len(self._list)
|
||
|
||
def __getitem__(self, item):
|
||
return self._list[item]
|
||
|
||
@property
|
||
def get(self):
|
||
return self._list.get
|
||
|
||
def tag(self, name, equal=True):
|
||
self._list = _tag_all(self._list, SessionElementsList(owner=self._list._owner), name=name, equal=equal)
|
||
return self
|
||
|
||
def text(self, text, fuzzy=True, contain=True):
|
||
self._list = _text_all(self._list, SessionElementsList(owner=self._list._owner),
|
||
text=text, fuzzy=fuzzy, contain=contain)
|
||
return self
|
||
|
||
def _get_attr(self, name, value, method, equal=True):
|
||
self._list = _attr_all(self._list, SessionElementsList(owner=self._list._owner),
|
||
name=name, value=value, method=method, equal=equal)
|
||
return self
|
||
|
||
|
||
class ChromiumFilterOne(SessionFilterOne):
|
||
|
||
def displayed(self, equal=True):
|
||
return self._any_state('is_displayed', equal=equal)
|
||
|
||
def checked(self, equal=True):
|
||
return self._any_state('is_checked', equal=equal)
|
||
|
||
def selected(self, equal=True):
|
||
return self._any_state('is_selected', equal=equal)
|
||
|
||
def enabled(self, equal=True):
|
||
return self._any_state('is_enabled', equal=equal)
|
||
|
||
def clickable(self, equal=True):
|
||
return self._any_state('is_clickable', equal=equal)
|
||
|
||
def have_rect(self, equal=True):
|
||
return self._any_state('has_rect', equal=equal)
|
||
|
||
def style(self, name, value, equal=True):
|
||
return self._get_attr(name, value, 'style', equal=equal)
|
||
|
||
def property(self, name, value, equal=True):
|
||
return self._get_attr(name, value, 'property', equal=equal)
|
||
|
||
def _any_state(self, name, equal=True):
|
||
num = 0
|
||
if equal:
|
||
for i in self._list:
|
||
if not isinstance(i, str) and getattr(i.states, name):
|
||
num += 1
|
||
if self._index == num:
|
||
return i
|
||
else:
|
||
for i in self._list:
|
||
if not isinstance(i, str) and not getattr(i.states, name):
|
||
num += 1
|
||
if self._index == num:
|
||
return i
|
||
return NoneElement(self._list._owner, f'{name}()', args={'equal': equal, 'index': self._index})
|
||
|
||
|
||
class ChromiumFilter(ChromiumFilterOne):
|
||
|
||
def __iter__(self):
|
||
return iter(self._list)
|
||
|
||
def __next__(self):
|
||
return next(self._list)
|
||
|
||
def __len__(self):
|
||
return len(self._list)
|
||
|
||
def __getitem__(self, item):
|
||
return self._list[item]
|
||
|
||
@property
|
||
def get(self):
|
||
return self._list.get
|
||
|
||
def search_one(self, index=1, displayed=None, checked=None, selected=None, enabled=None, clickable=None,
|
||
have_rect=None, have_text=None, tag=None):
|
||
return _search_one(self._list, index=index, displayed=displayed, checked=checked, selected=selected,
|
||
enabled=enabled, clickable=clickable, have_rect=have_rect, have_text=have_text, tag=tag)
|
||
|
||
def search(self, displayed=None, checked=None, selected=None, enabled=None, clickable=None,
|
||
have_rect=None, have_text=None, tag=None):
|
||
return _search(self._list, displayed=displayed, checked=checked, selected=selected, enabled=enabled,
|
||
clickable=clickable, have_rect=have_rect, have_text=have_text, tag=tag)
|
||
|
||
def tag(self, name, equal=True):
|
||
self._list = _tag_all(self._list, ChromiumElementsList(owner=self._list._owner), name=name, equal=equal)
|
||
return self
|
||
|
||
def text(self, text, fuzzy=True, contain=True):
|
||
self._list = _text_all(self._list, ChromiumElementsList(owner=self._list._owner),
|
||
text=text, fuzzy=fuzzy, contain=contain)
|
||
return self
|
||
|
||
def _get_attr(self, name, value, method, equal=True):
|
||
self._list = _attr_all(self._list, ChromiumElementsList(owner=self._list._owner),
|
||
name=name, value=value, method=method, equal=equal)
|
||
return self
|
||
|
||
def _any_state(self, name, equal=True):
|
||
r = ChromiumElementsList(owner=self._list._owner)
|
||
if equal:
|
||
for i in self._list:
|
||
if not isinstance(i, str) and getattr(i.states, name):
|
||
r.append(i)
|
||
else:
|
||
for i in self._list:
|
||
if not isinstance(i, str) and not getattr(i.states, name):
|
||
r.append(i)
|
||
self._list = r
|
||
return self
|
||
|
||
|
||
class Getter(object):
|
||
def __init__(self, _list):
|
||
self._list = _list
|
||
|
||
def links(self):
|
||
return [e.link for e in self._list if not isinstance(e, str)]
|
||
|
||
def texts(self):
|
||
return [e if isinstance(e, str) else e.text for e in self._list]
|
||
|
||
def attrs(self, name):
|
||
return [e.attr(name) for e in self._list if not isinstance(e, str)]
|
||
|
||
|
||
def get_eles(locators, owner, any_one=False, first_ele=True, timeout=10):
|
||
if isinstance(locators, (tuple, str)):
|
||
locators = (locators,)
|
||
res = {loc: None for loc in locators}
|
||
|
||
if timeout == 0:
|
||
for loc in locators:
|
||
ele = owner._ele(loc, timeout=0, raise_err=False, index=1 if first_ele else None, method='find()')
|
||
res[loc] = ele
|
||
if ele and any_one:
|
||
return res
|
||
return res
|
||
|
||
end_time = perf_counter() + timeout
|
||
while perf_counter() <= end_time:
|
||
for loc in locators:
|
||
if res[loc]:
|
||
continue
|
||
ele = owner._ele(loc, timeout=0, raise_err=False, index=1 if first_ele else None, method='find()')
|
||
res[loc] = ele
|
||
if ele and any_one:
|
||
return res
|
||
if all(res.values()):
|
||
return res
|
||
sleep(.05)
|
||
|
||
return res
|
||
|
||
|
||
def get_frame(owner, loc_ind_ele, timeout=None):
|
||
if isinstance(loc_ind_ele, str):
|
||
if not is_str_loc(loc_ind_ele):
|
||
xpath = f'xpath://*[(name()="iframe" or name()="frame") and ' \
|
||
f'(@name="{loc_ind_ele}" or @id="{loc_ind_ele}")]'
|
||
else:
|
||
xpath = loc_ind_ele
|
||
ele = owner._ele(xpath, timeout=timeout)
|
||
if ele and ele._type != 'ChromiumFrame':
|
||
raise TypeError('该定位符不是指向frame元素。')
|
||
r = ele
|
||
|
||
elif isinstance(loc_ind_ele, tuple):
|
||
ele = owner._ele(loc_ind_ele, timeout=timeout)
|
||
if ele and ele._type != 'ChromiumFrame':
|
||
raise TypeError('该定位符不是指向frame元素。')
|
||
r = ele
|
||
|
||
elif getattr(loc_ind_ele, '_type', None) == 'ChromiumFrame':
|
||
r = loc_ind_ele
|
||
|
||
else:
|
||
raise TypeError('必须传入定位符、iframe序号、id、name、ChromiumFrame对象其中之一。')
|
||
|
||
if isinstance(r, NoneElement):
|
||
r.method = 'get_frame()'
|
||
r.args = {'loc_ind_ele': loc_ind_ele}
|
||
return r
|
||
|
||
|
||
def _attr_all(src_list, aim_list, name, value, method, equal=True):
|
||
if equal:
|
||
for i in src_list:
|
||
if not isinstance(i, str) and getattr(i, method)(name) == value:
|
||
aim_list.append(i)
|
||
else:
|
||
for i in src_list:
|
||
if not isinstance(i, str) and getattr(i, method)(name) != value:
|
||
aim_list.append(i)
|
||
return aim_list
|
||
|
||
|
||
def _tag_all(src_list, aim_list, name, equal=True):
|
||
name = name.lower()
|
||
if equal:
|
||
for i in src_list:
|
||
if not isinstance(i, str) and i.tag == name:
|
||
aim_list.append(i)
|
||
else:
|
||
for i in src_list:
|
||
if not isinstance(i, str) and i.tag != name:
|
||
aim_list.append(i)
|
||
return aim_list
|
||
|
||
|
||
def _text_all(src_list, aim_list, text, fuzzy=True, contain=True):
|
||
"""以是否含有指定文本为条件筛选元素
|
||
:param text: 用于匹配的文本
|
||
:param fuzzy: 是否模糊匹配
|
||
:param contain: 是否包含该字符串,False表示不包含
|
||
:return: 筛选结果
|
||
"""
|
||
if contain:
|
||
for i in src_list:
|
||
t = i if isinstance(i, str) else i.raw_text
|
||
if (fuzzy and text in t) or (not fuzzy and text == t):
|
||
aim_list.append(i)
|
||
else:
|
||
for i in src_list:
|
||
t = i if isinstance(i, str) else i.raw_text
|
||
if (fuzzy and text not in t) or (not fuzzy and text != t):
|
||
aim_list.append(i)
|
||
return aim_list
|
||
|
||
|
||
def _search(_list, displayed=None, checked=None, selected=None, enabled=None, clickable=None,
|
||
have_rect=None, have_text=None, tag=None):
|
||
"""或关系筛选元素
|
||
:param displayed: 是否显示,bool,None为忽略该项
|
||
:param checked: 是否被选中,bool,None为忽略该项
|
||
:param selected: 是否被选择,bool,None为忽略该项
|
||
:param enabled: 是否可用,bool,None为忽略该项
|
||
:param clickable: 是否可点击,bool,None为忽略该项
|
||
:param have_rect: 是否拥有大小和位置,bool,None为忽略该项
|
||
:param have_text: 是否含有文本,bool,None为忽略该项
|
||
:param tag: 元素类型
|
||
:return: 筛选结果
|
||
"""
|
||
r = ChromiumElementsList(owner=_list._owner)
|
||
for i in _list:
|
||
if not isinstance(i, str) and (
|
||
(displayed is not None and (displayed is True and i.states.is_displayed)
|
||
or (displayed is False and not i.states.is_displayed))
|
||
or (checked is not None and (checked is True and i.states.is_checked)
|
||
or (checked is False and not i.states.is_checked))
|
||
or (selected is not None and (selected is True and i.states.is_selected)
|
||
or (selected is False and not i.states.is_selected))
|
||
or (enabled is not None and (enabled is True and i.states.is_enabled)
|
||
or (enabled is False and not i.states.is_enabled))
|
||
or (clickable is not None and (clickable is True and i.states.is_clickable)
|
||
or (clickable is False and not i.states.is_clickable))
|
||
or (have_rect is not None and (have_rect is True and i.states.has_rect)
|
||
or (have_rect is False and not i.states.has_rect))
|
||
or (have_text is not None and (have_text is True and i.raw_text)
|
||
or (have_text is False and not i.raw_text))
|
||
or (tag is not None and i.tag == tag.lower())):
|
||
r.append(i)
|
||
return ChromiumFilter(r)
|
||
|
||
|
||
def _search_one(_list, index=1, displayed=None, checked=None, selected=None, enabled=None, clickable=None,
|
||
have_rect=None, have_text=None, tag=None):
|
||
"""或关系筛选元素,获取一个结果
|
||
:param index: 元素序号,从1开始
|
||
:param displayed: 是否显示,bool,None为忽略该项
|
||
:param checked: 是否被选中,bool,None为忽略该项
|
||
:param selected: 是否被选择,bool,None为忽略该项
|
||
:param enabled: 是否可用,bool,None为忽略该项
|
||
:param clickable: 是否可点击,bool,None为忽略该项
|
||
:param have_rect: 是否拥有大小和位置,bool,None为忽略该项
|
||
:param have_text: 是否含有文本,bool,None为忽略该项
|
||
:param tag: 元素类型
|
||
:return: 筛选结果
|
||
"""
|
||
num = 0
|
||
for i in _list:
|
||
if not isinstance(i, str) and (
|
||
(displayed is not None and (displayed is True and i.states.is_displayed)
|
||
or (displayed is False and not i.states.is_displayed))
|
||
or (checked is not None and (checked is True and i.states.is_checked)
|
||
or (checked is False and not i.states.is_checked))
|
||
or (selected is not None and (selected is True and i.states.is_selected)
|
||
or (selected is False and not i.states.is_selected))
|
||
or (enabled is not None and (enabled is True and i.states.is_enabled)
|
||
or (enabled is False and not i.states.is_enabled))
|
||
or (clickable is not None and (clickable is True and i.states.is_clickable)
|
||
or (clickable is False and not i.states.is_clickable))
|
||
or (have_rect is not None and (have_rect is True and i.states.has_rect)
|
||
or (have_rect is False and not i.states.has_rect))
|
||
or (have_text is not None and (have_text is True and i.raw_text)
|
||
or (have_text is False and not i.raw_text))
|
||
or (tag is not None and i.tag == tag.lower())):
|
||
num += 1
|
||
if num == index:
|
||
return i
|
||
|
||
return NoneElement(_list._owner, method='filter()', args={'displayed': displayed, 'checked': checked,
|
||
'selected': selected, 'enabled': enabled,
|
||
'clickable': clickable, 'have_rect': have_rect,
|
||
'have_text': have_text, 'tag': tag})
|