修复异域iframe跳转到同域时问题;其它修改,未完成

This commit is contained in:
g1879 2024-07-01 00:35:22 +08:00
parent 139bee5a91
commit 6d552330cd
39 changed files with 708 additions and 513 deletions

View File

@ -12,10 +12,10 @@ from urllib.parse import quote
from DownloadKit import DownloadKit
from .._functions.settings import Settings
from .._functions.locator import get_loc
from .._functions.web import format_html
from .._elements.none_element import NoneElement
from .._functions.locator import get_loc
from .._functions.settings import Settings
from .._functions.web import format_html
from ..errors import ElementNotFoundError
@ -414,10 +414,6 @@ class BasePage(BaseParser):
def user_agent(self):
return
@abstractmethod
def cookies(self, as_dict=False, all_info=False):
return {}
@abstractmethod
def get(self, url, show_errmsg=False, retry=None, interval=None):
pass

View File

@ -205,7 +205,7 @@ class BasePage(BaseParser):
self._DownloadKit: DownloadKit = ...
self._none_ele_return_value: bool = ...
self._none_ele_value: Any = ...
self._page: Union[ChromiumPage, SessionPage, WebPage]=...
self._page: Union[ChromiumPage, SessionPage, WebPage] = ...
@property
def title(self) -> Union[str, None]: ...
@ -234,9 +234,6 @@ class BasePage(BaseParser):
@property
def user_agent(self) -> str: ...
@abstractmethod
def cookies(self, as_dict: bool = False, all_info: bool = False) -> Union[list, dict]: ...
@abstractmethod
def get(self, url: str, show_errmsg: bool = False, retry: int = None, interval: float = None): ...

View File

@ -17,6 +17,7 @@ from .driver import BrowserDriver, Driver
from .._configs.chromium_options import ChromiumOptions
from .._configs.session_options import SessionOptions
from .._functions.browser import connect_browser
from .._functions.cookies import CookiesList
from .._functions.settings import Settings
from .._functions.tools import PortFinder
from .._functions.tools import raise_error
@ -161,6 +162,15 @@ class Browser(object):
当Settings.singleton_tab_obj==True时返回Tab对象否则返回tab id"""
return self.get_tab(self.tab_ids[0], as_id=not Settings.singleton_tab_obj)
def cookies(self, all_info=False):
"""以list格式返回所有域名的cookies
:param all_info: 是否返回所有内容False则只返回name, value, domain
:return: cookies组成的列表
"""
cks = self._run_cdp(f'Storage.getCookies')['cookies']
r = cks if all_info else [{'name': c['name'], 'value': c['value'], 'domain': c['domain']} for c in cks]
return CookiesList(r)
def new_tab(self, url=None, new_window=False, background=False, new_context=False):
"""新建一个标签页
:param url: 新标签页跳转到的网址

View File

@ -11,6 +11,7 @@ from typing import List, Optional, Set, Dict, Union, Tuple
from .driver import BrowserDriver, Driver
from .._configs.chromium_options import ChromiumOptions
from .._configs.session_options import SessionOptions
from .._functions.web import CookiesList
from .._pages.chromium_base import Timeout
from .._pages.chromium_tab import ChromiumTab, MixTab
from .._units.downloader import DownloadManager
@ -82,6 +83,8 @@ class Browser(object):
@property
def latest_tab(self) -> Union[ChromiumTab, str]: ...
def cookies(self, all_info: bool = False) -> CookiesList: ...
def close_tabs(self,
tabs_or_ids: Union[str, ChromiumTab, List[Union[str, ChromiumTab]],
Tuple[Union[str, ChromiumTab]]] = None,

View File

@ -30,6 +30,7 @@ class Driver(object):
self.address = address
self.type = tab_type
self.owner = owner
# self._debug = True
# self._debug = False
self.alert_flag = False # 标记alert出现跳过一条请求后复原
@ -158,7 +159,7 @@ class Driver(object):
self.event_queue.task_done()
def _handle_immediate_event_loop(self):
while not self._stopped.is_set() and not self.immediate_event_queue.empty():
while not self.immediate_event_queue.empty():
function, kwargs = self.immediate_event_queue.get(timeout=1)
try:
function(**kwargs)
@ -227,6 +228,15 @@ class Driver(object):
self._ws = None
# try:
# while not self.immediate_event_queue.empty():
# function, kwargs = self.immediate_event_queue.get_nowait()
# try:
# function(**kwargs)
# except PageDisconnectedError:
# raise
# pass
# sleep(.1)
#
# while not self.event_queue.empty():
# event = self.event_queue.get_nowait()
# function = self.event_handlers.get(event['method'])

View File

@ -12,7 +12,8 @@ from requests import Session
from requests.structures import CaseInsensitiveDict
from .options_manage import OptionsManager
from .._functions.web import cookies_to_tuple, set_session_cookies, format_headers
from .._functions.cookies import cookies_to_tuple, set_session_cookies
from .._functions.web import format_headers
class SessionOptions(object):

View File

@ -16,9 +16,9 @@ from DataRecorder.tools import get_usable_path, make_valid_name
from .none_element import NoneElement
from .session_element import make_session_ele
from .._base.base import DrissionElement, BaseElement
from .._functions.elements import ChromiumElementsList
from .._functions.keys import input_text_or_keys
from .._functions.locator import get_loc, locator_to_tuple
from .._functions.elements import ChromiumElementsList
from .._functions.web import make_absolute_link, get_ele_txt, format_html, is_js_func, offset_scroll, get_blob
from .._units.clicker import Clicker
from .._units.rect import ElementRect
@ -95,29 +95,29 @@ class ChromiumElement(DrissionElement):
def tag(self):
"""返回元素tag"""
if self._tag is None:
self._tag = self.owner.run_cdp('DOM.describeNode',
backendNodeId=self._backend_id)['node']['localName'].lower()
self._tag = self.owner._run_cdp('DOM.describeNode',
backendNodeId=self._backend_id)['node']['localName'].lower()
return self._tag
@property
def html(self):
"""返回元素outerHTML文本"""
return self.owner.run_cdp('DOM.getOuterHTML', backendNodeId=self._backend_id)['outerHTML']
return self.owner._run_cdp('DOM.getOuterHTML', backendNodeId=self._backend_id)['outerHTML']
@property
def inner_html(self):
"""返回元素innerHTML文本"""
return self.run_js('return this.innerHTML;')
return self._run_js('return this.innerHTML;')
@property
def attrs(self):
"""返回元素所有attribute属性"""
try:
attrs = self.owner.run_cdp('DOM.getAttributes', nodeId=self._node_id)['attributes']
attrs = self.owner._run_cdp('DOM.getAttributes', nodeId=self._node_id)['attributes']
return {attrs[i]: attrs[i + 1] for i in range(0, len(attrs), 2)}
except ElementLostError:
self._refresh_id()
attrs = self.owner.run_cdp('DOM.getAttributes', nodeId=self._node_id)['attributes']
attrs = self.owner._run_cdp('DOM.getAttributes', nodeId=self._node_id)['attributes']
return {attrs[i]: attrs[i + 1] for i in range(0, len(attrs), 2)}
except CDPError: # 文档根元素不能调用此方法
return {}
@ -164,7 +164,7 @@ class ChromiumElement(DrissionElement):
@property
def shadow_root(self):
"""返回当前元素的shadow_root元素对象"""
info = self.owner.run_cdp('DOM.describeNode', backendNodeId=self._backend_id)['node']
info = self.owner._run_cdp('DOM.describeNode', backendNodeId=self._backend_id)['node']
if not info.get('shadowRoots', None):
return None
@ -225,8 +225,8 @@ class ChromiumElement(DrissionElement):
elif not is_checked and not uncheck:
js = 'this.checked=true'
if js:
self.run_js(js)
self.run_js('this.dispatchEvent(new Event("change", {bubbles: true}));')
self._run_js(js)
self._run_js('this.dispatchEvent(new Event("change", {bubbles: true}));')
else:
if (is_checked and uncheck) or (not is_checked and not uncheck):
@ -360,9 +360,9 @@ class ChromiumElement(DrissionElement):
x, y = self.rect.location
try:
return ChromiumElement(owner=self.owner,
backend_id=self.owner.run_cdp('DOM.getNodeForLocation', x=x + offset_x,
y=y + offset_y, includeUserAgentShadowDOM=True,
ignorePointerEventsNone=False)['backendNodeId'])
backend_id=self.owner._run_cdp('DOM.getNodeForLocation', x=x + offset_x,
y=y + offset_y, includeUserAgentShadowDOM=True,
ignorePointerEventsNone=False)['backendNodeId'])
except CDPError:
return NoneElement(page=self.owner, method='offset()', args={'offset_x': offset_x, 'offset_y': offset_y})
@ -439,8 +439,8 @@ class ChromiumElement(DrissionElement):
cdp_data[variable] += locator
try:
return ChromiumElement(owner=self.owner,
backend_id=self.owner.run_cdp('DOM.getNodeForLocation',
**cdp_data)['backendNodeId'])
backend_id=self.owner._run_cdp('DOM.getNodeForLocation',
**cdp_data)['backendNodeId'])
except CDPError:
return NoneElement(page=self.owner, method=f'{mode}()', args={'locator': locator})
@ -453,7 +453,7 @@ class ChromiumElement(DrissionElement):
while 0 < cdp_data[variable] < max_len:
cdp_data[variable] += value
try:
bid = self.owner.run_cdp('DOM.getNodeForLocation', **cdp_data)['backendNodeId']
bid = self.owner._run_cdp('DOM.getNodeForLocation', **cdp_data)['backendNodeId']
if bid == curr_ele:
continue
else:
@ -505,7 +505,7 @@ class ChromiumElement(DrissionElement):
:param name: 属性名
:return: None
"""
self.run_js(f'this.removeAttribute("{name}");')
self._run_js(f'this.removeAttribute("{name}");')
def property(self, name):
"""获取一个property属性值
@ -513,12 +513,22 @@ class ChromiumElement(DrissionElement):
:return: 属性值文本
"""
try:
value = self.run_js(f'return this.{name};')
value = self._run_js(f'return this.{name};')
return format_html(value) if isinstance(value, str) else value
except:
return None
def run_js(self, script, *args, as_expr=False, timeout=None):
"""对本元素执行javascript代码
:param script: js文本文本中用this表示本元素
:param args: 参数按顺序在js文本中对应arguments[0]arguments[1]...
:param as_expr: 是否作为表达式运行为True时args无效
:param timeout: js超时时间为None则使用页面timeouts.script设置
:return: 运行的结果
"""
return self._run_js(script, *args, as_expr=as_expr, timeout=timeout)
def _run_js(self, script, *args, as_expr=False, timeout=None):
"""对本元素执行javascript代码
:param script: js文本文本中用this表示本元素
:param args: 参数按顺序在js文本中对应arguments[0]arguments[1]...
@ -588,7 +598,7 @@ class ChromiumElement(DrissionElement):
"""
if pseudo_ele:
pseudo_ele = f', "{pseudo_ele}"' if pseudo_ele.startswith(':') else f', "::{pseudo_ele}"'
return self.run_js(f'return window.getComputedStyle(this{pseudo_ele}).getPropertyValue("{style}");')
return self._run_js(f'return window.getComputedStyle(this{pseudo_ele}).getPropertyValue("{style}");')
def src(self, timeout=None, base64_to_bytes=True):
"""返回元素src资源base64的可转为bytes返回其它返回str
@ -602,7 +612,7 @@ class ChromiumElement(DrissionElement):
'&& this.naturalWidth > 0 && typeof this.naturalHeight != "undefined" '
'&& this.naturalHeight > 0')
end_time = perf_counter() + timeout
while not self.run_js(js) and perf_counter() < end_time:
while not self._run_js(js) and perf_counter() < end_time:
sleep(.1)
src = self.attr('src')
@ -631,11 +641,11 @@ class ChromiumElement(DrissionElement):
if not src:
continue
node = self.owner.run_cdp('DOM.describeNode', backendNodeId=self._backend_id)['node']
node = self.owner._run_cdp('DOM.describeNode', backendNodeId=self._backend_id)['node']
frame = node.get('frameId', None) or self.owner._frame_id
try:
result = self.owner.run_cdp('Page.getResourceContent', frameId=frame, url=src)
result = self.owner._run_cdp('Page.getResourceContent', frameId=frame, url=src)
break
except CDPError:
pass
@ -698,7 +708,7 @@ class ChromiumElement(DrissionElement):
js = ('return this.complete && typeof this.naturalWidth != "undefined" && this.naturalWidth > 0 '
'&& typeof this.naturalHeight != "undefined" && this.naturalHeight > 0')
end_time = perf_counter() + self.owner.timeout
while not self.run_js(js) and perf_counter() < end_time:
while not self._run_js(js) and perf_counter() < end_time:
sleep(.1)
if scroll_to_center:
self.scroll.to_see(center=True)
@ -729,7 +739,7 @@ class ChromiumElement(DrissionElement):
if isinstance(vals, (list, tuple)):
vals = ''.join([str(i) for i in vals])
self.set.property('value', str(vals))
self.run_js('this.dispatchEvent(new Event("change", {bubbles: true}));')
self._run_js('this.dispatchEvent(new Event("change", {bubbles: true}));')
return
self.wait.clickable(wait_moved=False, timeout=.5)
@ -746,8 +756,8 @@ class ChromiumElement(DrissionElement):
:return: None
"""
if by_js:
self.run_js("this.value='';")
self.run_js('this.dispatchEvent(new Event("change", {bubbles: true}));')
self._run_js("this.value='';")
self._run_js('this.dispatchEvent(new Event("change", {bubbles: true}));')
return
self._input_focus()
@ -756,16 +766,16 @@ class ChromiumElement(DrissionElement):
def _input_focus(self):
"""输入前使元素获取焦点"""
try:
self.owner.run_cdp('DOM.focus', backendNodeId=self._backend_id)
self.owner._run_cdp('DOM.focus', backendNodeId=self._backend_id)
except Exception:
self.click(by_js=None)
def focus(self):
"""使元素获取焦点"""
try:
self.owner.run_cdp('DOM.focus', backendNodeId=self._backend_id)
self.owner._run_cdp('DOM.focus', backendNodeId=self._backend_id)
except Exception:
self.run_js('this.focus();')
self._run_js('this.focus();')
def hover(self, offset_x=None, offset_y=None):
"""鼠标悬停可接受偏移量偏移量相对于元素左上角坐标。不传入x或y值时悬停在元素中点
@ -775,7 +785,7 @@ class ChromiumElement(DrissionElement):
"""
self.owner.scroll.to_see(self)
x, y = offset_scroll(self, offset_x, offset_y)
self.owner.run_cdp('Input.dispatchMouseEvent', type='mouseMoved', x=x, y=y, _ignore=AlertExistsError)
self.owner._run_cdp('Input.dispatchMouseEvent', type='mouseMoved', x=x, y=y, _ignore=AlertExistsError)
def drag(self, offset_x=0, offset_y=0, duration=.5):
"""拖拽当前元素到相对位置
@ -808,9 +818,9 @@ class ChromiumElement(DrissionElement):
:return: js中的object id
"""
if node_id:
return self.owner.run_cdp('DOM.resolveNode', nodeId=node_id)['object']['objectId']
return self.owner._run_cdp('DOM.resolveNode', nodeId=node_id)['object']['objectId']
else:
return self.owner.run_cdp('DOM.resolveNode', backendNodeId=backend_id)['object']['objectId']
return self.owner._run_cdp('DOM.resolveNode', backendNodeId=backend_id)['object']['objectId']
def _get_node_id(self, obj_id=None, backend_id=None):
"""根据传入object id或backend id获取cdp中的node id
@ -819,9 +829,9 @@ class ChromiumElement(DrissionElement):
:return: cdp中的node id
"""
if obj_id:
return self.owner.run_cdp('DOM.requestNode', objectId=obj_id)['nodeId']
return self.owner._run_cdp('DOM.requestNode', objectId=obj_id)['nodeId']
else:
n = self.owner.run_cdp('DOM.describeNode', backendNodeId=backend_id)['node']
n = self.owner._run_cdp('DOM.describeNode', backendNodeId=backend_id)['node']
self._tag = n['localName']
return n['nodeId']
@ -830,7 +840,7 @@ class ChromiumElement(DrissionElement):
:param node_id:
:return: backend id
"""
n = self.owner.run_cdp('DOM.describeNode', nodeId=node_id)['node']
n = self.owner._run_cdp('DOM.describeNode', nodeId=node_id)['node']
self._tag = n['localName']
return n['backendNodeId']
@ -876,7 +886,7 @@ class ChromiumElement(DrissionElement):
}
return e(this);}
'''
t = self.run_js(js)
t = self._run_js(js)
return f'{t}' if mode == 'css' else t
def _set_file_input(self, files):
@ -887,7 +897,7 @@ class ChromiumElement(DrissionElement):
if isinstance(files, str):
files = files.split('\n')
files = [str(Path(i).absolute()) for i in files]
self.owner.run_cdp('DOM.setFileInputFiles', files=files, backendNodeId=self._backend_id)
self.owner._run_cdp('DOM.setFileInputFiles', files=files, backendNodeId=self._backend_id)
class ShadowRoot(BaseElement):
@ -942,7 +952,7 @@ class ShadowRoot(BaseElement):
@property
def inner_html(self):
"""返回内部的html文本"""
return self.run_js('return this.innerHTML;')
return self._run_js('return this.innerHTML;')
@property
def states(self):
@ -952,6 +962,16 @@ class ShadowRoot(BaseElement):
return self._states
def run_js(self, script, *args, as_expr=False, timeout=None):
"""运行javascript代码
:param script: js文本
:param args: 参数按顺序在js文本中对应arguments[0]arguments[1]...
:param as_expr: 是否作为表达式运行为True时args无效
:param timeout: js超时时间为None则使用页面timeouts.script设置
:return: 运行的结果
"""
return self._run_js(script, *args, as_expr=as_expr, timeout=timeout)
def _run_js(self, script, *args, as_expr=False, timeout=None):
"""运行javascript代码
:param script: js文本
:param args: 参数按顺序在js文本中对应arguments[0]arguments[1]...
@ -1163,14 +1183,14 @@ class ShadowRoot(BaseElement):
def do_find():
if loc[0] == 'css selector':
if index == 1:
nod_id = self.owner.run_cdp('DOM.querySelector', nodeId=self._node_id, selector=loc[1])['nodeId']
nod_id = self.owner._run_cdp('DOM.querySelector', nodeId=self._node_id, selector=loc[1])['nodeId']
if nod_id:
r = make_chromium_eles(self.owner, _ids=nod_id, is_obj_id=False)
return None if r is False else r
else:
nod_ids = self.owner.run_cdp('DOM.querySelectorAll',
nodeId=self._node_id, selector=loc[1])['nodeId']
nod_ids = self.owner._run_cdp('DOM.querySelectorAll',
nodeId=self._node_id, selector=loc[1])['nodeId']
r = make_chromium_eles(self.owner, _ids=nod_ids, index=index, is_obj_id=False)
return None if r is False else r
@ -1182,14 +1202,14 @@ class ShadowRoot(BaseElement):
css = [i.css_path[61:] for i in eles]
if index is not None:
try:
node_id = self.owner.run_cdp('DOM.querySelector', nodeId=self._node_id,
selector=css[index - 1])['nodeId']
node_id = self.owner._run_cdp('DOM.querySelector', nodeId=self._node_id,
selector=css[index - 1])['nodeId']
except IndexError:
return None
r = make_chromium_eles(self.owner, _ids=node_id, is_obj_id=False)
return None if r is False else r
else:
node_ids = [self.owner.run_cdp('DOM.querySelector', nodeId=self._node_id, selector=i)['nodeId']
node_ids = [self.owner._run_cdp('DOM.querySelector', nodeId=self._node_id, selector=i)['nodeId']
for i in css]
if 0 in node_ids:
return None
@ -1209,15 +1229,15 @@ class ShadowRoot(BaseElement):
def _get_node_id(self, obj_id):
"""返回元素node id"""
return self.owner.run_cdp('DOM.requestNode', objectId=obj_id)['nodeId']
return self.owner._run_cdp('DOM.requestNode', objectId=obj_id)['nodeId']
def _get_obj_id(self, back_id):
"""返回元素object id"""
return self.owner.run_cdp('DOM.resolveNode', backendNodeId=back_id)['object']['objectId']
return self.owner._run_cdp('DOM.resolveNode', backendNodeId=back_id)['object']['objectId']
def _get_backend_id(self, node_id):
"""返回元素object id"""
r = self.owner.run_cdp('DOM.describeNode', nodeId=node_id)['node']
r = self.owner._run_cdp('DOM.describeNode', nodeId=node_id)['node']
self._tag = r['localName'].lower()
return r['backendNodeId']
@ -1269,15 +1289,15 @@ def find_by_xpath(ele, xpath, index, timeout, relative=True):
ele.owner.wait.doc_loaded()
def do_find():
res = ele.owner.run_cdp('Runtime.callFunctionOn', functionDeclaration=js, objectId=ele._obj_id,
returnByValue=False, awaitPromise=True, userGesture=True)
res = ele.owner._run_cdp('Runtime.callFunctionOn', functionDeclaration=js, objectId=ele._obj_id,
returnByValue=False, awaitPromise=True, userGesture=True)
if res['result']['type'] == 'string':
return res['result']['value']
if 'exceptionDetails' in res:
if 'The result is not a node set' in res['result']['description']:
js1 = make_js_for_find_ele_by_xpath(xpath, '1', node_txt)
res = ele.owner.run_cdp('Runtime.callFunctionOn', functionDeclaration=js1, objectId=ele._obj_id,
returnByValue=False, awaitPromise=True, userGesture=True)
res = ele.owner._run_cdp('Runtime.callFunctionOn', functionDeclaration=js1, objectId=ele._obj_id,
returnByValue=False, awaitPromise=True, userGesture=True)
return res['result']['value']
else:
raise SyntaxError(f'查询语句错误:\n{res}')
@ -1290,8 +1310,8 @@ def find_by_xpath(ele, xpath, index, timeout, relative=True):
return None if r is False else r
else:
res = ele.owner.run_cdp('Runtime.getProperties', objectId=res['result']['objectId'],
ownProperties=True)['result'][:-1]
res = ele.owner._run_cdp('Runtime.getProperties', objectId=res['result']['objectId'],
ownProperties=True)['result'][:-1]
if index is None:
r = ChromiumElementsList(page=ele.owner)
for i in res:
@ -1341,8 +1361,8 @@ def find_by_css(ele, selector, index, timeout):
ele.owner.wait.doc_loaded()
def do_find():
res = ele.owner.run_cdp('Runtime.callFunctionOn', functionDeclaration=js, objectId=ele._obj_id,
returnByValue=False, awaitPromise=True, userGesture=True)
res = ele.owner._run_cdp('Runtime.callFunctionOn', functionDeclaration=js, objectId=ele._obj_id,
returnByValue=False, awaitPromise=True, userGesture=True)
if 'exceptionDetails' in res:
raise SyntaxError(f'查询语句错误:\n{res}')
@ -1354,9 +1374,9 @@ def find_by_css(ele, selector, index, timeout):
return None if r is False else r
else:
obj_ids = [i['value']['objectId'] for i in ele.owner.run_cdp('Runtime.getProperties',
objectId=res['result']['objectId'],
ownProperties=True)['result']]
obj_ids = [i['value']['objectId'] for i in ele.owner._run_cdp('Runtime.getProperties',
objectId=res['result']['objectId'],
ownProperties=True)['result']]
r = make_chromium_eles(ele.owner, _ids=obj_ids, index=index, is_obj_id=True)
return None if r is False else r
@ -1535,16 +1555,16 @@ def run_js(page_or_ele, script, as_expr, timeout, args=None):
end_time = perf_counter() + timeout
try:
if as_expr:
res = page.run_cdp('Runtime.evaluate', expression=script, returnByValue=False,
awaitPromise=True, userGesture=True, _timeout=timeout, _ignore=AlertExistsError)
res = page._run_cdp('Runtime.evaluate', expression=script, returnByValue=False,
awaitPromise=True, userGesture=True, _timeout=timeout, _ignore=AlertExistsError)
else:
args = args or ()
if not is_js_func(script):
script = f'function(){{{script}}}'
res = page.run_cdp('Runtime.callFunctionOn', functionDeclaration=script, objectId=obj_id,
arguments=[convert_argument(arg) for arg in args], returnByValue=False,
awaitPromise=True, userGesture=True, _timeout=timeout, _ignore=AlertExistsError)
res = page._run_cdp('Runtime.callFunctionOn', functionDeclaration=script, objectId=obj_id,
arguments=[convert_argument(arg) for arg in args], returnByValue=False,
awaitPromise=True, userGesture=True, _timeout=timeout, _ignore=AlertExistsError)
except TimeoutError:
raise TimeoutError(f'执行js超时等待{timeout}秒)。')
except ContextLostError:
@ -1591,7 +1611,7 @@ def parse_js_result(page, ele, result, end_time):
return r
elif sub_type == 'array':
r = page.run_cdp('Runtime.getProperties', objectId=result['objectId'], ownProperties=True)['result']
r = page._run_cdp('Runtime.getProperties', objectId=result['objectId'], ownProperties=True)['result']
return [parse_js_result(page, ele, result=i['value'], end_time=end_time) for i in r if i['name'].isdigit()]
elif 'objectId' in result:
@ -1599,9 +1619,9 @@ def parse_js_result(page, ele, result, end_time):
if timeout < 0:
return
js = 'function(){return JSON.stringify(this);}'
r = page.run_cdp('Runtime.callFunctionOn', functionDeclaration=js, objectId=result['objectId'],
returnByValue=False, awaitPromise=True, userGesture=True, _ignore=AlertExistsError,
_timeout=timeout)
r = page._run_cdp('Runtime.callFunctionOn', functionDeclaration=js, objectId=result['objectId'],
returnByValue=False, awaitPromise=True, userGesture=True, _ignore=AlertExistsError,
_timeout=timeout)
return loads(parse_js_result(page, ele, r['result'], end_time))
else:

View File

@ -195,6 +195,8 @@ class ChromiumElement(DrissionElement):
def run_js(self, script: str, *args, as_expr: bool = False, timeout: float = None) -> Any: ...
def _run_js(self, script: str, *args, as_expr: bool = False, timeout: float = None) -> Any: ...
def run_async_js(self, script: str, *args, as_expr: bool = False) -> None: ...
def ele(self,
@ -298,6 +300,8 @@ class ShadowRoot(BaseElement):
def run_js(self, script: str, *args, as_expr: bool = False, timeout: float = None) -> Any: ...
def _run_js(self, script: str, *args, as_expr: bool = False, timeout: float = None) -> Any: ...
def run_async_js(self, script: str, *args, as_expr: bool = False, timeout: float = None) -> None: ...
def parent(self, level_or_loc: Union[str, int] = 1, index: int = 1) -> ChromiumElement: ...

View File

@ -349,11 +349,11 @@ def make_session_ele(html_or_ele, loc=None, index=1, method=None):
xpath = html_or_ele.xpath
# ChromiumElement兼容传入的元素在iframe内的情况
if html_or_ele._doc_id is None:
doc = html_or_ele.run_js('return this.ownerDocument;')
doc = html_or_ele._run_js('return this.ownerDocument;')
html_or_ele._doc_id = doc['objectId'] if doc else False
if html_or_ele._doc_id:
html = html_or_ele.owner.run_cdp('DOM.getOuterHTML', objectId=html_or_ele._doc_id)['outerHTML']
html = html_or_ele.owner._run_cdp('DOM.getOuterHTML', objectId=html_or_ele._doc_id)['outerHTML']
else:
html = html_or_ele.owner.html
html_or_ele = fromstring(html)

View File

@ -0,0 +1,233 @@
# -*- coding:utf-8 -*-
"""
@Author : g1879
@Contact : g1879@qq.com
@Copyright: (c) 2024 by g1879, Inc. All Rights Reserved.
@License : BSD 3-Clause.
"""
from datetime import datetime
from http.cookiejar import Cookie, CookieJar
from tldextract import extract
def cookie_to_dict(cookie):
"""把Cookie对象转为dict格式
:param cookie: Cookie对象字符串或字典
:return: cookie字典
"""
if isinstance(cookie, Cookie):
cookie_dict = cookie.__dict__.copy()
cookie_dict.pop('rfc2109', None)
cookie_dict.pop('_rest', None)
return cookie_dict
elif isinstance(cookie, dict):
cookie_dict = cookie
elif isinstance(cookie, str):
cookie_dict = {}
for attr in cookie.strip().rstrip(';,').split(',' if ',' in cookie else ';'):
attr_val = attr.strip().split('=', 1)
if attr_val[0] in ('domain', 'path', 'expires', 'max-age', 'HttpOnly', 'secure', 'expiry', 'name', 'value'):
cookie_dict[attr_val[0]] = attr_val[1] if len(attr_val) == 2 else ''
else:
cookie_dict['name'] = attr_val[0]
cookie_dict['value'] = attr_val[1] if len(attr_val) == 2 else ''
return cookie_dict
else:
raise TypeError('cookie参数必须为Cookie、str或dict类型。')
return cookie_dict
def cookies_to_tuple(cookies):
"""把cookies转为tuple格式
:param cookies: cookies信息可为CookieJar, list, tuple, str, dict
:return: 返回tuple形式的cookies
"""
if isinstance(cookies, (list, tuple, CookieJar)):
cookies = tuple(cookie_to_dict(cookie) for cookie in cookies)
elif isinstance(cookies, str):
c_dict = {}
cookies = cookies.rstrip('; ')
cookies = cookies.split(';')
for attr in cookies:
attr_val = attr.strip().split('=', 1)
c_dict[attr_val[0]] = attr_val[1] if len(attr_val) == 2 else True
cookies = _dict_cookies_to_tuple(c_dict)
elif isinstance(cookies, dict):
cookies = _dict_cookies_to_tuple(cookies)
elif isinstance(cookies, Cookie):
cookies = (cookie_to_dict(cookies),)
else:
raise TypeError('cookies参数必须为Cookie、CookieJar、list、tuple、str或dict类型。')
return cookies
def set_session_cookies(session, cookies):
"""设置Session对象的cookies
:param session: Session对象
:param cookies: cookies信息
:return: None
"""
for cookie in cookies_to_tuple(cookies):
if cookie['value'] is None:
cookie['value'] = ''
kwargs = {x: cookie[x] for x in cookie
if x.lower() in ('version', 'port', 'domain', 'path', 'secure',
'expires', 'discard', 'comment', 'comment_url', 'rest')}
if 'expiry' in cookie:
kwargs['expires'] = cookie['expiry']
session.cookies.set(cookie['name'], cookie['value'], **kwargs)
def set_browser_cookies(browser, cookies):
"""设置cookies值
:param browser: 页面对象
:param cookies: cookies信息
:return: None
"""
c = []
for cookie in cookies_to_tuple(cookies):
if 'domain' not in cookie and 'url' not in cookie:
raise ValueError(f"cookie必须带有'domain''url'字段:{cookie}")
c.append(format_cookie(cookie))
browser._run_cdp('Storage.setCookies', cookies=c)
def set_tab_cookies(page, cookies):
"""设置cookies值
:param page: 页面对象
:param cookies: cookies信息
:return: None
"""
for cookie in cookies_to_tuple(cookies):
cookie = format_cookie(cookie)
if cookie['name'].startswith('__Host-'):
if not page.url.startswith('http'):
cookie['name'] = cookie['name'].replace('__Host-', '__Secure-', 1)
else:
cookie['url'] = page.url
page._run_cdp_loaded('Network.setCookie', **cookie)
continue # 不用设置域名,可退出
if cookie.get('domain', None):
try:
page._run_cdp_loaded('Network.setCookie', **cookie)
if is_cookie_in_driver(page, cookie):
continue
except Exception:
pass
url = page._browser_url
if not url.startswith('http'):
raise RuntimeError(f'未设置域名请设置cookie的domain参数或先访问一个网站。{cookie}')
ex_url = extract(url)
d_list = ex_url.subdomain.split('.')
d_list.append(f'{ex_url.domain}.{ex_url.suffix}' if ex_url.suffix else ex_url.domain)
tmp = [d_list[0]]
if len(d_list) > 1:
for i in d_list[1:]:
tmp.append('.')
tmp.append(i)
for i in range(len(tmp)):
cookie['domain'] = ''.join(tmp[i:])
page._run_cdp_loaded('Network.setCookie', **cookie)
if is_cookie_in_driver(page, cookie):
break
def is_cookie_in_driver(page, cookie):
"""查询cookie是否在浏览器内
:param page: BasePage对象
:param cookie: dict格式cookie
:return: bool
"""
if 'domain' in cookie:
for c in page.cookies(all_domains=True):
if cookie['name'] == c['name'] and cookie['value'] == c['value'] and cookie['domain'] == c.get('domain',
None):
return True
else:
for c in page.cookies(all_domains=True):
if cookie['name'] == c['name'] and cookie['value'] == c['value']:
return True
return False
def format_cookie(cookie):
"""设置cookie为可用格式
:param cookie: dict格式cookie
:return: 格式化后的cookie字典
"""
if 'expiry' in cookie:
cookie['expires'] = int(cookie['expiry'])
cookie.pop('expiry')
if 'expires' in cookie:
if not cookie['expires']:
cookie.pop('expires')
elif isinstance(cookie['expires'], str):
if cookie['expires'].isdigit():
cookie['expires'] = int(cookie['expires'])
elif cookie['expires'].replace('.', '').isdigit():
cookie['expires'] = float(cookie['expires'])
else:
try:
cookie['expires'] = datetime.strptime(cookie['expires'], '%a, %d %b %Y %H:%M:%S GMT').timestamp()
except ValueError:
cookie['expires'] = datetime.strptime(cookie['expires'], '%a, %d %b %y %H:%M:%S GMT').timestamp()
if cookie['value'] is None:
cookie['value'] = ''
elif not isinstance(cookie['value'], str):
cookie['value'] = str(cookie['value'])
if cookie['name'].startswith('__Host-'):
cookie['path'] = '/'
cookie['secure'] = True
elif cookie['name'].startswith('__Secure-'):
cookie['secure'] = True
return cookie
class CookiesList(list):
def as_dict(self):
"""以dict格式返回只包含name和value字段"""
return {c['name']: c['value'] for c in self}
def as_str(self):
"""以str格式返回只包含name和value字段"""
return '; '.join([f'{c["name"]}={c["value"]}' for c in self])
def _dict_cookies_to_tuple(cookies: dict):
"""把dict形式的cookies转换为tuple形式
:param cookies: 单个或多个cookies单个时包含'name''value'
:return: 多个dict格式cookies组成的列表
"""
if 'name' in cookies and 'value' in cookies: # 单个cookie
return (cookies,)
keys = ('domain', 'path', 'expires', 'max-age', 'HttpOnly', 'secure', 'expiry')
template = {k: v for k, v in cookies.items() if k in keys}
return tuple(dict(**{'name': k, 'value': v}, **template) for k, v in cookies.items() if k not in keys)

View File

@ -0,0 +1,44 @@
# -*- coding:utf-8 -*-
"""
@Author : g1879
@Contact : g1879@qq.com
@Copyright: (c) 2024 by g1879, Inc. All Rights Reserved.
@License : BSD 3-Clause.
"""
from http.cookiejar import Cookie
from typing import Union
from requests import Session
from requests.cookies import RequestsCookieJar
from .._base.browser import Browser
from .._pages.chromium_base import ChromiumBase
def cookie_to_dict(cookie: Union[Cookie, str, dict]) -> dict: ...
def cookies_to_tuple(cookies: Union[RequestsCookieJar, list, tuple, str, dict, Cookie]) -> tuple: ...
def set_session_cookies(session: Session, cookies: Union[RequestsCookieJar, list, tuple, str, dict]) -> None: ...
def set_browser_cookies(browser: Browser, cookies: Union[RequestsCookieJar, list, tuple, str, dict]) -> None: ...
def set_tab_cookies(page: ChromiumBase, cookies: Union[RequestsCookieJar, list, tuple, str, dict]) -> None: ...
def is_cookie_in_driver(page: ChromiumBase, cookie: dict) -> bool: ...
def format_cookie(cookie: dict) -> dict: ...
class CookiesList(list):
def as_dict(self) -> dict: ...
def as_str(self) -> str: ...
def __next__(self) -> dict: ...

View File

@ -427,12 +427,12 @@ def send_key(page, modifier, key):
'isKeypad': description['location'] == 3,
'_ignore': AlertExistsError}
page.run_cdp('Input.dispatchKeyEvent', **data)
page._run_cdp('Input.dispatchKeyEvent', **data)
data['type'] = 'keyUp'
page.run_cdp('Input.dispatchKeyEvent', **data)
page._run_cdp('Input.dispatchKeyEvent', **data)
else:
page.run_cdp('Input.insertText', text=key, _ignore=AlertExistsError)
page._run_cdp('Input.insertText', text=key, _ignore=AlertExistsError)
def input_text_or_keys(page, text_or_keys):
@ -451,7 +451,7 @@ def input_text_or_keys(page, text_or_keys):
return
if text_or_keys.endswith(('\n', '\ue007')):
page.run_cdp('Input.insertText', text=text_or_keys[:-1], _ignore=AlertExistsError)
page._run_cdp('Input.insertText', text=text_or_keys[:-1], _ignore=AlertExistsError)
send_key(page, modifier, '\n')
else:
page.run_cdp('Input.insertText', text=text_or_keys, _ignore=AlertExistsError)
page._run_cdp('Input.insertText', text=text_or_keys, _ignore=AlertExistsError)

View File

@ -198,10 +198,11 @@ def configs_to_here(save_name=None):
om.save(save_name)
def raise_error(result, ignore=None):
def raise_error(result, ignore=None, user=False):
"""抛出error对应报错
:param result: 包含error的dict
:param ignore: 要忽略的错误
:param user: 是否用户调用的
:return: None
"""
error = result['error']
@ -227,13 +228,18 @@ def raise_error(result, ignore=None):
elif error == 'Given expression does not evaluate to a function':
r = JavaScriptError(f'传入的js无法解析成函数\n{result["args"]["functionDeclaration"]}')
elif error.endswith("' wasn't found"):
r = RuntimeError(f'你的浏览器可能太旧。\n方法:{result["method"]}\n参数:{result["args"]}')
elif result['type'] in ('call_method_error', 'timeout'):
r = RuntimeError(f'没有找到对应功能,方法错误或你的浏览器太旧。\n方法:{result["method"]}\n参数:{result["args"]}')
elif result['type'] == 'timeout':
from DrissionPage import __version__
txt = f'\n错误:{result["error"]}\n方法:{result["method"]}\n参数:{result["args"]}\n' \
f'版本:{__version__}\n超时,可能是浏览器卡了。'
r = TimeoutError(txt)
elif result['type'] == 'call_method_error' and not user:
from DrissionPage import __version__
txt = f'\n错误:{result["error"]}\n方法:{result["method"]}\n参数:{result["args"]}\n' \
f'版本:{__version__}\n出现这个错误可能意味着程序有bug请把错误信息和重现方法' \
'告知作者,谢谢。\n报告网站https://gitee.com/g1879/DrissionPage/issues'
r = TimeoutError(txt) if result['type'] == 'timeout' else CDPError(txt)
r = CDPError(txt)
else:
r = RuntimeError(result)

View File

@ -47,4 +47,4 @@ def wait_until(function: callable, kwargs: dict = None, timeout: float = 10): ..
def configs_to_here(file_name: Union[Path, str] = None) -> None: ...
def raise_error(result: dict, ignore=None) -> None: ...
def raise_error(result: dict, ignore=None, user: bool = False) -> None: ...

View File

@ -5,16 +5,13 @@
@Copyright: (c) 2024 by g1879, Inc. All Rights Reserved.
@License : BSD 3-Clause.
"""
from datetime import datetime
from html import unescape
from http.cookiejar import Cookie, CookieJar
from os.path import sep
from pathlib import Path
from re import sub
from urllib.parse import urlparse, urljoin, urlunparse
from DataRecorder.tools import make_valid_name
from tldextract import extract
def get_ele_txt(e):
@ -106,7 +103,7 @@ def location_in_viewport(page, loc_x, loc_y):
const vHeight = document.documentElement.clientHeight;
if (x< scrollLeft || y < scrollTop || x > vWidth + scrollLeft || y > vHeight + scrollTop){{return false;}}
return true;}}'''
return page.run_js(js)
return page._run_js(js)
def offset_scroll(ele, offset_x, offset_y):
@ -122,8 +119,8 @@ def offset_scroll(ele, offset_x, offset_y):
lx = loc_x + offset_x if offset_x else cp_x
ly = loc_y + offset_y if offset_y else cp_y
if not location_in_viewport(ele.owner, lx, ly):
clientWidth = ele.owner.run_js('return document.body.clientWidth;')
clientHeight = ele.owner.run_js('return document.body.clientHeight;')
clientWidth = ele.owner._run_js('return document.body.clientWidth;')
clientHeight = ele.owner._run_js('return document.body.clientHeight;')
ele.owner.scroll.to_location(lx - clientWidth // 2, ly - clientHeight // 2)
cl_x, cl_y = ele.rect.viewport_location
ccp_x, ccp_y = ele.rect.viewport_click_point
@ -174,188 +171,6 @@ def is_js_func(func):
return False
def cookie_to_dict(cookie):
"""把Cookie对象转为dict格式
:param cookie: Cookie对象字符串或字典
:return: cookie字典
"""
if isinstance(cookie, Cookie):
cookie_dict = cookie.__dict__.copy()
cookie_dict.pop('rfc2109', None)
cookie_dict.pop('_rest', None)
return cookie_dict
elif isinstance(cookie, dict):
cookie_dict = cookie
elif isinstance(cookie, str):
cookie_dict = {}
for attr in cookie.strip().rstrip(';,').split(',' if ',' in cookie else ';'):
attr_val = attr.strip().split('=', 1)
if attr_val[0] in ('domain', 'path', 'expires', 'max-age', 'HttpOnly', 'secure', 'expiry', 'name', 'value'):
cookie_dict[attr_val[0]] = attr_val[1] if len(attr_val) == 2 else ''
else:
cookie_dict['name'] = attr_val[0]
cookie_dict['value'] = attr_val[1] if len(attr_val) == 2 else ''
return cookie_dict
else:
raise TypeError('cookie参数必须为Cookie、str或dict类型。')
return cookie_dict
def cookies_to_tuple(cookies):
"""把cookies转为tuple格式
:param cookies: cookies信息可为CookieJar, list, tuple, str, dict
:return: 返回tuple形式的cookies
"""
if isinstance(cookies, (list, tuple, CookieJar)):
cookies = tuple(cookie_to_dict(cookie) for cookie in cookies)
elif isinstance(cookies, str):
c_dict = {}
cookies = cookies.rstrip('; ')
cookies = cookies.split(';')
# r = match(r'.*?=([^=]+)=', cookies)
# if not r: # 只有一个
# cookies = [cookies.rstrip(',;')]
# else:
# s = match(r'.*([,;]).*', r.group(1)).group(1)
# cookies = cookies.rstrip(s).split(s)
for attr in cookies:
attr_val = attr.strip().split('=', 1)
c_dict[attr_val[0]] = attr_val[1] if len(attr_val) == 2 else True
cookies = _dict_cookies_to_tuple(c_dict)
elif isinstance(cookies, dict):
cookies = _dict_cookies_to_tuple(cookies)
elif isinstance(cookies, Cookie):
cookies = (cookie_to_dict(cookies),)
else:
raise TypeError('cookies参数必须为Cookie、CookieJar、list、tuple、str或dict类型。')
return cookies
def set_session_cookies(session, cookies):
"""设置Session对象的cookies
:param session: Session对象
:param cookies: cookies信息
:return: None
"""
for cookie in cookies_to_tuple(cookies):
if cookie['value'] is None:
cookie['value'] = ''
kwargs = {x: cookie[x] for x in cookie
if x.lower() in ('version', 'port', 'domain', 'path', 'secure',
'expires', 'discard', 'comment', 'comment_url', 'rest')}
if 'expiry' in cookie:
kwargs['expires'] = cookie['expiry']
session.cookies.set(cookie['name'], cookie['value'], **kwargs)
def set_browser_cookies(page, cookies):
"""设置cookies值
:param page: 页面对象
:param cookies: cookies信息
:return: None
"""
for cookie in cookies_to_tuple(cookies):
if 'expiry' in cookie:
cookie['expires'] = int(cookie['expiry'])
cookie.pop('expiry')
if 'expires' in cookie:
if not cookie['expires']:
cookie.pop('expires')
elif isinstance(cookie['expires'], str):
if cookie['expires'].isdigit():
cookie['expires'] = int(cookie['expires'])
elif cookie['expires'].replace('.', '').isdigit():
cookie['expires'] = float(cookie['expires'])
else:
try:
cookie['expires'] = datetime.strptime(cookie['expires'],
'%a, %d %b %Y %H:%M:%S GMT').timestamp()
except ValueError:
cookie['expires'] = datetime.strptime(cookie['expires'],
'%a, %d %b %y %H:%M:%S GMT').timestamp()
if cookie['value'] is None:
cookie['value'] = ''
elif not isinstance(cookie['value'], str):
cookie['value'] = str(cookie['value'])
if cookie['name'].startswith('__Host-'):
cookie['path'] = '/'
cookie['secure'] = True
if not page.url.startswith('http'):
cookie['name'] = cookie['name'].replace('__Host-', '__Secure-', 1)
else:
cookie['url'] = page.url
page.run_cdp_loaded('Network.setCookie', **cookie)
continue # 不用设置域名,可退出
if cookie['name'].startswith('__Secure-'):
cookie['secure'] = True
if cookie.get('domain', None):
try:
page.run_cdp_loaded('Network.setCookie', **cookie)
if is_cookie_in_driver(page, cookie):
continue
except Exception:
pass
url = page._browser_url
if not url.startswith('http'):
raise RuntimeError(f'未设置域名请设置cookie的domain参数或先访问一个网站。{cookie}')
ex_url = extract(url)
d_list = ex_url.subdomain.split('.')
d_list.append(f'{ex_url.domain}.{ex_url.suffix}' if ex_url.suffix else ex_url.domain)
tmp = [d_list[0]]
if len(d_list) > 1:
for i in d_list[1:]:
tmp.append('.')
tmp.append(i)
for i in range(len(tmp)):
cookie['domain'] = ''.join(tmp[i:])
page.run_cdp_loaded('Network.setCookie', **cookie)
if is_cookie_in_driver(page, cookie):
break
def is_cookie_in_driver(page, cookie):
"""查询cookie是否在浏览器内
:param page: BasePage对象
:param cookie: dict格式cookie
:return: bool
"""
if 'domain' in cookie:
for c in page.cookies(all_domains=True):
if cookie['name'] == c['name'] and cookie['value'] == c['value'] and cookie['domain'] == c.get('domain',
None):
return True
else:
for c in page.cookies(all_domains=True):
if cookie['name'] == c['name'] and cookie['value'] == c['value']:
return True
return False
def get_blob(page, url, as_bytes=True):
"""获取知道blob资源
:param page: 资源所在页面对象
@ -381,7 +196,7 @@ def get_blob(page, url, as_bytes=True):
}
"""
try:
result = page.run_js(js, url)
result = page._run_js(js, url)
except:
raise RuntimeError('无法获取该资源。')
if as_bytes:
@ -429,7 +244,7 @@ def get_mhtml(page, path=None, name=None):
:param name: 文件名为None且path不为None时用title属性值
:return: mhtml文本
"""
r = page.run_cdp('Page.captureSnapshot')['data']
r = page._run_cdp('Page.captureSnapshot')['data']
if path is None and name is None:
return r
@ -455,7 +270,7 @@ def get_pdf(page, path=None, name=None, kwargs=None):
if 'printBackground' not in kwargs:
kwargs['printBackground'] = True
try:
r = page.run_cdp('Page.printToPDF', **kwargs)['data']
r = page._run_cdp('Page.printToPDF', **kwargs)['data']
except:
raise RuntimeError('保存失败,可能浏览器版本不支持。')
from base64 import b64decode
@ -539,15 +354,3 @@ def format_headers(txt):
name, value = header.split(': ', maxsplit=1)
headers[name] = value
return headers
def _dict_cookies_to_tuple(cookies: dict):
"""把dict形式的cookies转换为tuple形式
:param cookies: 单个或多个cookies单个时包含'name''value'
:return: 多个dict格式cookies组成的列表
"""
if 'name' in cookies and 'value' in cookies: # 单个cookie
return (cookies,)
keys = ('domain', 'path', 'expires', 'max-age', 'HttpOnly', 'secure', 'expiry')
template = {k: v for k, v in cookies.items() if k in keys}
return tuple(dict(**{'name': k, 'value': v}, **template) for k, v in cookies.items() if k not in keys)

View File

@ -5,13 +5,9 @@
@Copyright: (c) 2024 by g1879, Inc. All Rights Reserved.
@License : BSD 3-Clause.
"""
from http.cookiejar import Cookie
from pathlib import Path
from typing import Union, Optional
from requests import Session
from requests.cookies import RequestsCookieJar
from .._base.base import DrissionElement, BaseParser
from .._elements.chromium_element import ChromiumElement
from .._pages.chromium_base import ChromiumBase
@ -37,21 +33,6 @@ def make_absolute_link(link: str, baseURI: str = None) -> str: ...
def is_js_func(func: str) -> bool: ...
def cookie_to_dict(cookie: Union[Cookie, str, dict]) -> dict: ...
def cookies_to_tuple(cookies: Union[RequestsCookieJar, list, tuple, str, dict, Cookie]) -> tuple: ...
def set_session_cookies(session: Session, cookies: Union[RequestsCookieJar, list, tuple, str, dict]) -> None: ...
def set_browser_cookies(page: ChromiumBase, cookies: Union[RequestsCookieJar, list, tuple, str, dict]) -> None: ...
def is_cookie_in_driver(page: ChromiumBase, cookie: dict) -> bool: ...
def get_blob(page: ChromiumBase, url: str, as_bytes: bool = True) -> bytes: ...

View File

@ -18,6 +18,7 @@ from .._base.base import BasePage
from .._elements.chromium_element import run_js, make_chromium_eles
from .._elements.none_element import NoneElement
from .._elements.session_element import make_session_ele
from .._functions.cookies import CookiesList
from .._functions.locator import get_loc, is_loc
from .._functions.settings import Settings
from .._functions.tools import raise_error
@ -120,7 +121,7 @@ class ChromiumBase(BasePage):
self._driver.run('Page.enable')
self._driver.run('Emulation.setFocusEmulationEnabled', enabled=True)
r = self.run_cdp('Page.getFrameTree')
r = self._run_cdp('Page.getFrameTree')
for i in findall(r"'id': '(.*?)'", str(r)):
self.browser._frames[i] = self.tab_id
if not hasattr(self, '_frame_id'):
@ -146,10 +147,10 @@ class ChromiumBase(BasePage):
end_time = perf_counter() + timeout
while perf_counter() < end_time:
try:
b_id = self.run_cdp('DOM.getDocument', _timeout=timeout)['root']['backendNodeId']
b_id = self._run_cdp('DOM.getDocument', _timeout=timeout)['root']['backendNodeId']
timeout = end_time - perf_counter()
timeout = 1 if timeout <= 1 else timeout
self._root_id = self.run_cdp('DOM.resolveNode', backendNodeId=b_id,
self._root_id = self._run_cdp('DOM.resolveNode', backendNodeId=b_id,
_timeout=timeout)['object']['objectId']
result = True
break
@ -166,7 +167,7 @@ class ChromiumBase(BasePage):
result = False
if result:
r = self.run_cdp('Page.getFrameTree')
r = self._run_cdp('Page.getFrameTree')
for i in findall(r"'id': '(.*?)'", str(r)):
self.browser._frames[i] = self.tab_id
@ -203,7 +204,7 @@ class ChromiumBase(BasePage):
def _onDomContentEventFired(self, **kwargs):
"""在页面刷新、变化后重新读取页面内容"""
if self._load_mode == 'eager':
self.run_cdp('Page.stopLoading')
self._run_cdp('Page.stopLoading')
if self._get_document(self._load_end_time - perf_counter() - .1):
self._doc_got = True
self._ready_state = 'interactive'
@ -228,10 +229,10 @@ class ChromiumBase(BasePage):
if 'backendNodeId' not in kwargs:
raise TypeError('该输入框无法接管,请改用对<input>元素输入路径的方法设置。')
files = self._upload_list if kwargs['mode'] == 'selectMultiple' else self._upload_list[:1]
self.run_cdp('DOM.setFileInputFiles', files=files, backendNodeId=kwargs['backendNodeId'])
self._run_cdp('DOM.setFileInputFiles', files=files, backendNodeId=kwargs['backendNodeId'])
self.driver.set_callback('Page.fileChooserOpened', None)
self.run_cdp('Page.setInterceptFileChooserDialog', enabled=False)
self._run_cdp('Page.setInterceptFileChooserDialog', enabled=False)
self._upload_list = None
def __call__(self, locator, index=1, timeout=None):
@ -333,12 +334,12 @@ class ChromiumBase(BasePage):
@property
def title(self):
"""返回当前页面title"""
return self.run_cdp_loaded('Target.getTargetInfo', targetId=self._target_id)['targetInfo']['title']
return self._run_cdp_loaded('Target.getTargetInfo', targetId=self._target_id)['targetInfo']['title']
@property
def url(self):
"""返回当前页面url"""
return self.run_cdp_loaded('Target.getTargetInfo', targetId=self._target_id)['targetInfo']['url']
return self._run_cdp_loaded('Target.getTargetInfo', targetId=self._target_id)['targetInfo']['url']
@property
def _browser_url(self):
@ -349,7 +350,7 @@ class ChromiumBase(BasePage):
def html(self):
"""返回当前页面html文本"""
self.wait.doc_loaded()
return self.run_cdp('DOM.getOuterHTML', objectId=self._root_id)['outerHTML']
return self._run_cdp('DOM.getOuterHTML', objectId=self._root_id)['outerHTML']
@property
def json(self):
@ -372,7 +373,7 @@ class ChromiumBase(BasePage):
@property
def active_ele(self):
"""返回当前焦点所在元素"""
return self.run_js_loaded('return document.activeElement;')
return self._run_js_loaded('return document.activeElement;')
@property
def load_mode(self):
@ -382,7 +383,7 @@ class ChromiumBase(BasePage):
@property
def user_agent(self):
"""返回user agent"""
return self.run_cdp('Runtime.evaluate', expression='navigator.userAgent;')['result']['value']
return self._run_cdp('Runtime.evaluate', expression='navigator.userAgent;')['result']['value']
@property
def upload_list(self):
@ -393,7 +394,7 @@ class ChromiumBase(BasePage):
def _js_ready_state(self):
"""返回js获取的ready state信息"""
try:
return self.run_cdp('Runtime.evaluate', expression='document.readyState;', _timeout=3)['result']['value']
return self._run_cdp('Runtime.evaluate', expression='document.readyState;', _timeout=3)['result']['value']
except ContextLostError:
return None
except TimeoutError:
@ -405,9 +406,8 @@ class ChromiumBase(BasePage):
:param cmd_args: 参数
:return: 执行的结果
"""
ignore = cmd_args.pop('_ignore', None)
r = self.driver.run(cmd, **cmd_args)
return r if __ERROR__ not in r else raise_error(r, ignore)
return r if __ERROR__ not in r else raise_error(r, user=True)
def run_cdp_loaded(self, cmd, **cmd_args):
"""执行Chrome DevTools Protocol语句执行前等待页面加载完毕
@ -416,7 +416,27 @@ class ChromiumBase(BasePage):
:return: 执行的结果
"""
self.wait.doc_loaded()
return self.run_cdp(cmd, **cmd_args)
r = self.driver.run(cmd, **cmd_args)
return r if __ERROR__ not in r else raise_error(r, user=True)
def _run_cdp(self, cmd, **cmd_args):
"""执行Chrome DevTools Protocol语句
:param cmd: 协议项目
:param cmd_args: 参数
:return: 执行的结果
"""
ignore = cmd_args.pop('_ignore', None)
r = self.driver.run(cmd, **cmd_args)
return r if __ERROR__ not in r else raise_error(r, ignore)
def _run_cdp_loaded(self, cmd, **cmd_args):
"""执行Chrome DevTools Protocol语句执行前等待页面加载完毕
:param cmd: 协议项目
:param cmd_args: 参数
:return: 执行的结果
"""
self.wait.doc_loaded()
return self._run_cdp(cmd, **cmd_args)
def run_js(self, script, *args, as_expr=False, timeout=None):
"""运行javascript代码
@ -426,9 +446,30 @@ class ChromiumBase(BasePage):
:param timeout: js超时时间为None则使用页面timeouts.script设置
:return: 运行的结果
"""
return run_js(self, script, as_expr, self.timeouts.script if timeout is None else timeout, args)
return self._run_js(script, *args, as_expr=as_expr, timeout=timeout)
def run_js_loaded(self, script, *args, as_expr=False, timeout=None):
"""运行javascript代码执行前等待页面加载完毕
:param script: js文本或js文件路径
:param args: 参数按顺序在js文本中对应arguments[0]arguments[1]...
:param as_expr: 是否作为表达式运行为True时args无效
:param timeout: js超时时间为None则使用页面timeouts.script属性值
:return: 运行的结果
"""
self.wait.doc_loaded()
return self._run_js(script, *args, as_expr=as_expr, timeout=timeout)
def _run_js(self, script, *args, as_expr=False, timeout=None):
"""运行javascript代码
:param script: js文本或js文件路径
:param args: 参数按顺序在js文本中对应arguments[0]arguments[1]...
:param as_expr: 是否作为表达式运行为True时args无效
:param timeout: js超时时间为None则使用页面timeouts.script设置
:return: 运行的结果
"""
return run_js(self, script, as_expr, self.timeouts.script if timeout is None else timeout, args)
def _run_js_loaded(self, script, *args, as_expr=False, timeout=None):
"""运行javascript代码执行前等待页面加载完毕
:param script: js文本或js文件路径
:param args: 参数按顺序在js文本中对应arguments[0]arguments[1]...
@ -462,23 +503,21 @@ class ChromiumBase(BasePage):
show_errmsg=show_errmsg, timeout=timeout)
return self._url_available
def cookies(self, as_dict=False, all_domains=False, all_info=False):
def cookies(self, all_domains=False, all_info=False):
"""返回cookies信息
:param as_dict: 为True时以dict格式返回且all_info无效为False时返回list
:param all_domains: 是否返回所有域的cookies
:param all_info: 是否返回所有信息为False时只返回namevaluedomain
:return: cookies信息
"""
txt = 'Storage' if all_domains else 'Network'
cookies = self.run_cdp_loaded(f'{txt}.getCookies')['cookies']
cookies = self._run_cdp_loaded(f'{txt}.getCookies')['cookies']
if as_dict:
return {cookie['name']: cookie['value'] for cookie in cookies}
elif all_info:
return cookies
if all_info:
r = cookies
else:
return [{'name': cookie['name'], 'value': cookie['value'], 'domain': cookie['domain']}
for cookie in cookies]
r = [{'name': cookie['name'], 'value': cookie['value'], 'domain': cookie['domain']} for cookie in cookies]
return CookiesList(r)
def ele(self, locator, index=1, timeout=None):
"""获取一个符合条件的元素对象
@ -591,7 +630,7 @@ class ChromiumBase(BasePage):
:return: None
"""
self._is_loading = True
self.run_cdp('Page.reload', ignoreCache=ignore_cache)
self._run_cdp('Page.reload', ignoreCache=ignore_cache)
self.wait.load_start()
def forward(self, steps=1):
@ -616,7 +655,7 @@ class ChromiumBase(BasePage):
if steps == 0:
return
history = self.run_cdp('Page.getNavigationHistory')
history = self._run_cdp('Page.getNavigationHistory')
index = history['currentIndex']
history = history['entries']
direction = 1 if steps > 0 else -1
@ -632,12 +671,12 @@ class ChromiumBase(BasePage):
if nid:
self._is_loading = True
self.run_cdp('Page.navigateToHistoryEntry', entryId=nid)
self._run_cdp('Page.navigateToHistoryEntry', entryId=nid)
def stop_loading(self):
"""页面停止加载"""
try:
self.run_cdp('Page.stopLoading')
self._run_cdp('Page.stopLoading')
end_time = perf_counter() + 5
while self._ready_state != 'complete' and perf_counter() < end_time:
sleep(.1)
@ -655,7 +694,7 @@ class ChromiumBase(BasePage):
return
ele = self._ele(loc_or_ele, raise_err=False)
if ele:
self.run_cdp('DOM.removeNode', nodeId=ele._node_id, _ignore=ElementLostError)
self._run_cdp('DOM.removeNode', nodeId=ele._node_id, _ignore=ElementLostError)
def add_ele(self, html_or_info, insert_to=None, before=None):
"""新建一个元素
@ -711,7 +750,7 @@ class ChromiumBase(BasePage):
else:
raise TypeError('html_or_info参数必须是html文本或tupletuple格式为(tag, {name: value})。')
ele = self.run_js(js, *args)
ele = self._run_js(js, *args)
return ele
def get_frame(self, loc_ind_ele, timeout=None):
@ -772,7 +811,7 @@ class ChromiumBase(BasePage):
:return: sessionStorage一个或所有项内容
"""
js = f'sessionStorage.getItem("{item}")' if item else 'sessionStorage'
return self.run_js_loaded(js, as_expr=True)
return self._run_js_loaded(js, as_expr=True)
def local_storage(self, item=None):
"""返回localStorage信息不设置item则获取全部
@ -780,7 +819,7 @@ class ChromiumBase(BasePage):
:return: localStorage一个或所有项内容
"""
js = f'localStorage.getItem("{item}")' if item else 'localStorage'
return self.run_js_loaded(js, as_expr=True)
return self._run_js_loaded(js, as_expr=True)
def get_screenshot(self, path=None, name=None, as_bytes=None, as_base64=None,
full_page=False, left_top=None, right_bottom=None):
@ -802,7 +841,7 @@ class ChromiumBase(BasePage):
:param script: js文本
:return: 添加的脚本的id
"""
js_id = self.run_cdp('Page.addScriptToEvaluateOnNewDocument', source=script,
js_id = self._run_cdp('Page.addScriptToEvaluateOnNewDocument', source=script,
includeCommandLineAPI=True)['identifier']
self._init_jss.append(js_id)
return js_id
@ -814,11 +853,11 @@ class ChromiumBase(BasePage):
"""
if script_id is None:
for js_id in self._init_jss:
self.run_cdp('Page.removeScriptToEvaluateOnNewDocument', identifier=js_id)
self._run_cdp('Page.removeScriptToEvaluateOnNewDocument', identifier=js_id)
self._init_jss.clear()
elif script_id in self._init_jss:
self.run_cdp('Page.removeScriptToEvaluateOnNewDocument', identifier=script_id)
self._run_cdp('Page.removeScriptToEvaluateOnNewDocument', identifier=script_id)
self._init_jss.remove(script_id)
def clear_cache(self, session_storage=True, local_storage=True, cache=True, cookies=True):
@ -830,19 +869,19 @@ class ChromiumBase(BasePage):
:return: None
"""
if session_storage or local_storage:
self.run_cdp_loaded('DOMStorage.enable')
i = self.run_cdp('Storage.getStorageKeyForFrame', frameId=self._frame_id)['storageKey']
self._run_cdp_loaded('DOMStorage.enable')
i = self._run_cdp('Storage.getStorageKeyForFrame', frameId=self._frame_id)['storageKey']
if session_storage:
self.run_cdp('DOMStorage.clear', storageId={'storageKey': i, 'isLocalStorage': False})
self._run_cdp('DOMStorage.clear', storageId={'storageKey': i, 'isLocalStorage': False})
if local_storage:
self.run_cdp('DOMStorage.clear', storageId={'storageKey': i, 'isLocalStorage': True})
self.run_cdp_loaded('DOMStorage.disable')
self._run_cdp('DOMStorage.clear', storageId={'storageKey': i, 'isLocalStorage': True})
self._run_cdp_loaded('DOMStorage.disable')
if cache:
self.run_cdp_loaded('Network.clearBrowserCache')
self._run_cdp_loaded('Network.clearBrowserCache')
if cookies:
self.run_cdp_loaded('Network.clearBrowserCookies')
self._run_cdp_loaded('Network.clearBrowserCookies')
def disconnect(self):
"""断开与页面的连接,不关闭页面"""
@ -974,7 +1013,7 @@ class ChromiumBase(BasePage):
err = None
end_time = perf_counter() + timeout
try:
result = self.run_cdp('Page.navigate', frameId=self._frame_id, url=to_url, _timeout=timeout)
result = self._run_cdp('Page.navigate', frameId=self._frame_id, url=to_url, _timeout=timeout)
if 'errorText' in result:
err = ConnectionError(result['errorText'])
except TimeoutError:
@ -1074,8 +1113,8 @@ class ChromiumBase(BasePage):
v = not (location_in_viewport(self, x, y) and
location_in_viewport(self, right_bottom[0], right_bottom[1]))
if v and (self.run_js('return document.body.scrollHeight > window.innerHeight;') and
not self.run_js('return document.body.scrollWidth > window.innerWidth;')):
if v and (self._run_js('return document.body.scrollHeight > window.innerHeight;') and
not self._run_js('return document.body.scrollWidth > window.innerWidth;')):
x += 10
vp = {'x': x, 'y': y, 'width': w, 'height': h, 'scale': 1}
@ -1086,7 +1125,7 @@ class ChromiumBase(BasePage):
if pic_type == 'jpeg':
args['quality'] = 100
png = self.run_cdp_loaded('Page.captureScreenshot', **args)['data']
png = self._run_cdp_loaded('Page.captureScreenshot', **args)['data']
if as_base64:
return png

View File

@ -15,6 +15,7 @@ from .._base.driver import Driver
from .._elements.chromium_element import ChromiumElement
from .._elements.session_element import SessionElement
from .._functions.elements import SessionElementsList, ChromiumElementsList
from .._functions.web import CookiesList
from .._pages.chromium_frame import ChromiumFrame
from .._pages.chromium_page import ChromiumPage
from .._units.actions import Actions
@ -170,13 +171,16 @@ class ChromiumBase(BasePage):
def run_js_loaded(self, script: Union[str, Path], *args, as_expr: bool = False, timeout: float = None) -> Any: ...
def _run_js(self, script: Union[str, Path], *args, as_expr: bool = False, timeout: float = None) -> Any: ...
def _run_js_loaded(self, script: Union[str, Path], *args, as_expr: bool = False, timeout: float = None) -> Any: ...
def run_async_js(self, script: Union[str, Path], *args, as_expr: bool = False) -> None: ...
def get(self, url: str, show_errmsg: bool = False, retry: int = None,
interval: float = None, timeout: float = None) -> Union[None, bool]: ...
def cookies(self, as_dict: bool = False, all_domains: bool = False, all_info: bool = False) -> Union[
list, dict]: ...
def cookies(self, all_domains: bool = False, all_info: bool = False) -> CookiesList: ...
def ele(self,
locator: Union[Tuple[str, str], str, ChromiumElement, ChromiumFrame],
@ -225,6 +229,10 @@ class ChromiumBase(BasePage):
def run_cdp_loaded(self, cmd: str, **cmd_args) -> dict: ...
def _run_cdp(self, cmd: str, **cmd_args) -> dict: ...
def _run_cdp_loaded(self, cmd: str, **cmd_args) -> dict: ...
def session_storage(self, item: str = None) -> Union[str, dict, None]: ...
def local_storage(self, item: str = None) -> Union[str, dict, None]: ...

View File

@ -33,7 +33,7 @@ class ChromiumFrame(ChromiumBase):
self._frame_ele = ele
self._reloading = False
node = info['node'] if not info else owner.run_cdp('DOM.describeNode', backendNodeId=ele._backend_id)['node']
node = info['node'] if not info else owner._run_cdp('DOM.describeNode', backendNodeId=ele._backend_id)['node']
self._frame_id = node['frameId']
if self._is_inner_frame():
self._is_diff_domain = False
@ -43,7 +43,7 @@ class ChromiumFrame(ChromiumBase):
self._is_diff_domain = True
delattr(self, '_frame_id')
super().__init__(owner.browser, node['frameId'])
obj_id = super().run_js('document;', as_expr=True)['objectId']
obj_id = super()._run_js('document;', as_expr=True)['objectId']
self.doc_ele = ChromiumElement(self, obj_id=obj_id)
self._type = 'ChromiumFrame'
@ -100,7 +100,7 @@ class ChromiumFrame(ChromiumBase):
self._frame_ele = ChromiumElement(self._target_page, backend_id=self._backend_id)
end_time = perf_counter() + 2
while perf_counter() < end_time:
node = self._target_page.run_cdp('DOM.describeNode', backendNodeId=self._frame_ele._backend_id)['node']
node = self._target_page._run_cdp('DOM.describeNode', backendNodeId=self._frame_ele._backend_id)['node']
if 'frameId' in node:
break
sleep(.05)
@ -145,17 +145,17 @@ class ChromiumFrame(ChromiumBase):
self._is_reading = True
try:
if self._is_diff_domain is False:
node = self._target_page.run_cdp('DOM.describeNode', backendNodeId=self._backend_id)['node']
node = self._target_page._run_cdp('DOM.describeNode', backendNodeId=self._backend_id)['node']
self.doc_ele = ChromiumElement(self._target_page, backend_id=node['contentDocument']['backendNodeId'])
else:
timeout = max(timeout, 2)
b_id = self.run_cdp('DOM.getDocument', _timeout=timeout)['root']['backendNodeId']
b_id = self._run_cdp('DOM.getDocument', _timeout=timeout)['root']['backendNodeId']
self.doc_ele = ChromiumElement(self, backend_id=b_id)
self._root_id = self.doc_ele._obj_id
r = self.run_cdp('Page.getFrameTree')
r = self._run_cdp('Page.getFrameTree')
for i in findall(r"'id': '(.*?)'", str(r)):
self.browser._frames[i] = self.tab_id
return True
@ -253,7 +253,7 @@ class ChromiumFrame(ChromiumBase):
def url(self):
"""返回frame当前访问的url"""
try:
return self.doc_ele.run_js('return this.location.href;')
return self.doc_ele._run_js('return this.location.href;')
except JavaScriptError:
return None
@ -261,14 +261,14 @@ class ChromiumFrame(ChromiumBase):
def html(self):
"""返回元素outerHTML文本"""
tag = self.tag
out_html = self._target_page.run_cdp('DOM.getOuterHTML', backendNodeId=self.frame_ele._backend_id)['outerHTML']
out_html = self._target_page._run_cdp('DOM.getOuterHTML', backendNodeId=self.frame_ele._backend_id)['outerHTML']
sign = search(rf'<{tag}.*?>', out_html, DOTALL).group(0)
return f'{sign}{self.inner_html}</{tag}>'
@property
def inner_html(self):
"""返回元素innerHTML文本"""
return self.doc_ele.run_js('return this.documentElement.outerHTML;')
return self.doc_ele._run_js('return this.documentElement.outerHTML;')
@property
def title(self):
@ -284,7 +284,7 @@ class ChromiumFrame(ChromiumBase):
@property
def active_ele(self):
"""返回当前焦点所在元素"""
return self.doc_ele.run_js('return this.activeElement;')
return self.doc_ele._run_js('return this.activeElement;')
@property
def xpath(self):
@ -318,18 +318,18 @@ class ChromiumFrame(ChromiumBase):
else:
try:
return self.doc_ele.run_js('return this.readyState;')
return self.doc_ele._run_js('return this.readyState;')
except ContextLostError:
try:
node = self.run_cdp('DOM.describeNode', backendNodeId=self.frame_ele._backend_id)['node']
node = self._run_cdp('DOM.describeNode', backendNodeId=self.frame_ele._backend_id)['node']
doc = ChromiumElement(self._target_page, backend_id=node['contentDocument']['backendNodeId'])
return doc.run_js('return this.readyState;')
return doc._run_js('return this.readyState;')
except:
return None
def refresh(self):
"""刷新frame页面"""
self.doc_ele.run_js('this.location.reload();')
self.doc_ele._run_js('this.location.reload();')
def property(self, name):
"""返回frame元素一个property属性值
@ -353,6 +353,16 @@ class ChromiumFrame(ChromiumBase):
self.frame_ele.remove_attr(name)
def run_js(self, script, *args, as_expr=False, timeout=None):
"""运行javascript代码
:param script: js文本
:param args: 参数按顺序在js文本中对应arguments[0]arguments[1]...
:param as_expr: 是否作为表达式运行为True时args无效
:param timeout: js超时时间为None则使用页面timeouts.script设置
:return: 运行的结果
"""
return self._run_js(script, *args, as_expr=as_expr, timeout=timeout)
def _run_js(self, script, *args, as_expr=False, timeout=None):
"""运行javascript代码
:param script: js文本
:param args: 参数按顺序在js文本中对应arguments[0]arguments[1]...
@ -361,9 +371,9 @@ class ChromiumFrame(ChromiumBase):
:return: 运行的结果
"""
if script.startswith('this.scrollIntoView'):
return self.frame_ele.run_js(script, *args, as_expr=as_expr, timeout=timeout)
return self.frame_ele._run_js(script, *args, as_expr=as_expr, timeout=timeout)
else:
return self.doc_ele.run_js(script, *args, as_expr=as_expr, timeout=timeout)
return self.doc_ele._run_js(script, *args, as_expr=as_expr, timeout=timeout)
def parent(self, level_or_loc=1, index=1):
"""返回上面某一级父元素,可指定层数或用查询语法定位
@ -526,12 +536,12 @@ class ChromiumFrame(ChromiumBase):
img.style.setProperty("position","fixed");
arguments[0].insertBefore(img, this);
return img;'''
new_ele = first_child.run_js(js, body)
new_ele = first_child._run_js(js, body)
new_ele.scroll.to_see(center=True)
top = int(self.frame_ele.style('border-top').split('px')[0])
left = int(self.frame_ele.style('border-left').split('px')[0])
r = self.tab.run_cdp('Page.getLayoutMetrics')['visualViewport']
r = self.tab._run_cdp('Page.getLayoutMetrics')['visualViewport']
sx = r['pageX']
sy = r['pageY']
r = self.tab.get_screenshot(path=path, name=name, as_bytes=as_bytes, as_base64=as_base64,
@ -557,4 +567,4 @@ class ChromiumFrame(ChromiumBase):
def _is_inner_frame(self):
"""返回当前frame是否同域"""
return self._frame_id in str(self._target_page.run_cdp('Page.getFrameTree')['frameTree'])
return self._frame_id in str(self._target_page._run_cdp('Page.getFrameTree')['frameTree'])

View File

@ -142,6 +142,12 @@ class ChromiumFrame(ChromiumBase):
as_expr: bool = False,
timeout: float = None) -> Any: ...
def _run_js(self,
script: str,
*args,
as_expr: bool = False,
timeout: float = None) -> Any: ...
def parent(self,
level_or_loc: Union[Tuple[str, str], str, int] = 1,
index: int = 1) -> ChromiumElement: ...

View File

@ -6,7 +6,6 @@
@License : BSD 3-Clause.
"""
from pathlib import Path
from threading import Lock
from time import sleep, perf_counter
from requests import Session
@ -65,7 +64,7 @@ class ChromiumPage(ChromiumBase):
def _run_browser(self):
"""连接浏览器"""
self._browser = Browser(self._chromium_options.address, self._browser_id, self)
r = self._browser.run_cdp('Browser.getVersion')
r = self._browser._run_cdp('Browser.getVersion')
self._browser_version = r['product']
if self._is_exist and self._chromium_options._headless is False and 'headless' in r['userAgent'].lower():
self._browser.quit(3)

View File

@ -10,8 +10,9 @@ from time import sleep
from .._base.base import BasePage
from .._configs.session_options import SessionOptions
from .._functions.cookies import set_session_cookies, set_tab_cookies
from .._functions.settings import Settings
from .._functions.web import set_session_cookies, set_browser_cookies, save_page
from .._functions.web import save_page
from .._pages.chromium_base import ChromiumBase
from .._pages.session_page import SessionPage
from .._units.setter import TabSetter, WebPageTabSetter
@ -338,7 +339,7 @@ class MixTab(SessionPage, ChromiumTab, BasePage):
return
if copy_user_agent:
user_agent = self.run_cdp('Runtime.evaluate', expression='navigator.userAgent;')['result']['value']
user_agent = self._run_cdp('Runtime.evaluate', expression='navigator.userAgent;')['result']['value']
self._headers.update({"User-Agent": user_agent})
set_session_cookies(self.session, super(SessionPage, self).cookies())
@ -347,19 +348,18 @@ class MixTab(SessionPage, ChromiumTab, BasePage):
"""把session对象的cookies复制到浏览器"""
if not self._has_driver:
return
set_browser_cookies(self, super().cookies())
set_tab_cookies(self, super().cookies())
def cookies(self, as_dict=False, all_domains=False, all_info=False):
def cookies(self, all_domains=False, all_info=False):
"""返回cookies
:param as_dict: 为True时以dict格式返回为False时返回list且all_info无效
:param all_domains: 是否返回所有域的cookies
:param all_info: 是否返回所有信息False则只返回namevaluedomain
:return: cookies信息
"""
if self._mode == 's':
return super().cookies(as_dict, all_domains, all_info)
return super().cookies(all_domains, all_info)
elif self._mode == 'd':
return super(SessionPage, self).cookies(as_dict, all_domains, all_info)
return super(SessionPage, self).cookies(all_domains, all_info)
def close(self):
"""关闭当前标签页"""

View File

@ -17,6 +17,7 @@ from .._base.browser import Browser
from .._elements.chromium_element import ChromiumElement
from .._elements.session_element import SessionElement
from .._functions.elements import SessionElementsList, ChromiumElementsList
from .._functions.web import CookiesList
from .._units.rect import TabRect
from .._units.setter import TabSetter, WebPageTabSetter
from .._units.waiter import TabWaiter
@ -153,8 +154,7 @@ class MixTab(SessionPage, ChromiumTab):
def cookies_to_browser(self) -> None: ...
def cookies(self, as_dict: bool = False, all_domains: bool = False,
all_info: bool = False) -> Union[dict, list]: ...
def cookies(self, all_domains: bool = False, all_info: bool = False) -> CookiesList: ...
def close(self) -> None: ...

View File

@ -18,7 +18,8 @@ from tldextract import extract
from .._base.base import BasePage
from .._configs.session_options import SessionOptions
from .._elements.session_element import SessionElement, make_session_ele
from .._functions.web import cookie_to_dict, format_headers
from .._functions.cookies import cookie_to_dict, CookiesList
from .._functions.web import format_headers
from .._units.setter import SessionPageSetter
@ -216,9 +217,8 @@ class SessionPage(BasePage):
"""
return locator if isinstance(locator, SessionElement) else make_session_ele(self, locator, index=index)
def cookies(self, as_dict=False, all_domains=False, all_info=False):
def cookies(self, all_domains=False, all_info=False):
"""返回cookies
:param as_dict: 为True时以dict格式返回为False时返回list且all_info无效
:param all_domains: 是否返回所有域的cookies
:param all_info: 是否返回所有信息False则只返回namevaluedomain
:return: cookies信息
@ -229,21 +229,20 @@ class SessionPage(BasePage):
if self.url:
ex_url = extract(self._session_url)
domain = f'{ex_url.domain}.{ex_url.suffix}' if ex_url.suffix else ex_url.domain
cookies = tuple(x for x in self.session.cookies if domain in x.domain or x.domain == '')
cookies = tuple(c for c in self.session.cookies if domain in c.domain or c.domain == '')
else:
cookies = tuple(x for x in self.session.cookies)
cookies = tuple(c for c in self.session.cookies)
if as_dict:
return {x.name: x.value for x in cookies}
elif all_info:
return [cookie_to_dict(cookie) for cookie in cookies]
if all_info:
r = CookiesList()
for c in cookies:
r.append(cookie_to_dict(c))
else:
r = []
r = CookiesList()
for c in cookies:
c = cookie_to_dict(c)
r.append({'name': c['name'], 'value': c['value'], 'domain': c['domain']})
return r
return r
def close(self):
"""关闭Session对象"""

View File

@ -15,6 +15,7 @@ from .._base.base import BasePage
from .._configs.session_options import SessionOptions
from .._elements.session_element import SessionElement
from .._functions.elements import SessionElementsList
from .._functions.web import CookiesList
from .._units.setter import SessionPageSetter
@ -27,7 +28,7 @@ class SessionPage(BasePage):
self._url: str = ...
self._response: Response = ...
self._url_available: bool = ...
self.timeout: float = ...
self._timeout: float = ...
self.retry_times: int = ...
self.retry_interval: float = ...
self._set: SessionPageSetter = ...
@ -113,9 +114,8 @@ class SessionPage(BasePage):
raise_err: bool = None) -> Union[SessionElement, SessionElementsList]: ...
def cookies(self,
as_dict: bool = False,
all_domains: bool = False,
all_info: bool = False) -> Union[dict, list]: ...
all_info: bool = False) -> CookiesList: ...
# ----------------session独有属性和方法-----------------------
@property

View File

@ -10,7 +10,7 @@ from .chromium_tab import MixTab
from .session_page import SessionPage
from .._base.base import BasePage
from .._configs.chromium_options import ChromiumOptions
from .._functions.web import set_session_cookies, set_browser_cookies
from .._functions.cookies import set_session_cookies, set_tab_cookies
from .._units.setter import WebPageSetter
@ -276,7 +276,7 @@ class WebPage(SessionPage, ChromiumPage, BasePage):
return
if copy_user_agent:
user_agent = self.run_cdp('Runtime.evaluate', expression='navigator.userAgent;')['result']['value']
user_agent = self._run_cdp('Runtime.evaluate', expression='navigator.userAgent;')['result']['value']
self._headers.update({"User-Agent": user_agent})
set_session_cookies(self.session, super(SessionPage, self).cookies())
@ -285,7 +285,7 @@ class WebPage(SessionPage, ChromiumPage, BasePage):
"""把session对象的cookies复制到浏览器"""
if not self._has_driver:
return
set_browser_cookies(self, super().cookies())
set_tab_cookies(self, super().cookies())
def cookies(self, as_dict=False, all_domains=False, all_info=False):
"""返回cookies

View File

@ -7,9 +7,9 @@
"""
from time import sleep, perf_counter
from ..errors import AlertExistsError
from .._functions.keys import modifierBit, keyDescriptionForString, input_text_or_keys, Keys, keyDefinitions
from .._functions.web import location_in_viewport
from ..errors import AlertExistsError
class Actions:
@ -51,8 +51,8 @@ class Actions:
if not location_in_viewport(self.owner, lx, ly):
# 把坐标滚动到页面中间
clientWidth = self.owner.run_js('return document.body.clientWidth;')
clientHeight = self.owner.run_js('return document.body.clientHeight;')
clientWidth = self.owner._run_js('return document.body.clientWidth;')
clientHeight = self.owner._run_js('return document.body.clientHeight;')
self.owner.scroll.to_location(lx - clientWidth // 2, ly - clientHeight // 2)
# 这样设计为了应付那些不随滚动条滚动的元素
@ -258,7 +258,7 @@ class Actions:
data = self._get_key_data(key, 'keyDown')
data['_ignore'] = AlertExistsError
self.owner.run_cdp('Input.dispatchKeyEvent', **data)
self.owner._run_cdp('Input.dispatchKeyEvent', **data)
return self
def key_up(self, key):
@ -273,7 +273,7 @@ class Actions:
data = self._get_key_data(key, 'keyUp')
data['_ignore'] = AlertExistsError
self.owner.run_cdp('Input.dispatchKeyEvent', **data)
self.owner._run_cdp('Input.dispatchKeyEvent', **data)
return self
def type(self, keys):
@ -292,7 +292,7 @@ class Actions:
self.key_up(character)
else:
self.owner.run_cdp('Input.dispatchKeyEvent', type='char', text=character)
self.owner._run_cdp('Input.dispatchKeyEvent', type='char', text=character)
for m in modifiers:
self.key_up(m)
@ -339,6 +339,6 @@ class Actions:
def location_to_client(page, lx, ly):
"""绝对坐标转换为视口坐标"""
scroll_x = page.run_js('return document.documentElement.scrollLeft;')
scroll_y = page.run_js('return document.documentElement.scrollTop;')
scroll_x = page._run_js('return document.documentElement.scrollLeft;')
scroll_y = page._run_js('return document.documentElement.scrollTop;')
return lx - scroll_x, ly - scroll_y

View File

@ -87,7 +87,7 @@ class Clicker(object):
x = rect[1][0] - (rect[1][0] - rect[0][0]) / 2
y = rect[0][0] + 3
try:
r = self._ele.owner.run_cdp('DOM.getNodeForLocation', x=int(x), y=int(y),
r = self._ele.owner._run_cdp('DOM.getNodeForLocation', x=int(x), y=int(y),
includeUserAgentShadowDOM=True, ignorePointerEventsNone=True)
if r['backendNodeId'] != self._ele._backend_id:
vx, vy = self._ele.rect.viewport_midpoint
@ -101,7 +101,7 @@ class Clicker(object):
return True
if by_js is not False:
self._ele.run_js('this.click();')
self._ele._run_js('this.click();')
return True
if Settings.raise_when_click_failed:
raise CanNotClickError
@ -204,7 +204,7 @@ class Clicker(object):
:param count: 点击次数
:return: None
"""
self._ele.owner.run_cdp('Input.dispatchMouseEvent', type='mousePressed', x=client_x,
self._ele.owner._run_cdp('Input.dispatchMouseEvent', type='mousePressed', x=client_x,
y=client_y, button=button, clickCount=count, _ignore=AlertExistsError)
self._ele.owner.run_cdp('Input.dispatchMouseEvent', type='mouseReleased', x=client_x,
self._ele.owner._run_cdp('Input.dispatchMouseEvent', type='mouseReleased', x=client_x,
y=client_y, button=button, _ignore=AlertExistsError)

View File

@ -5,7 +5,7 @@
@Copyright: (c) 2024 by g1879, Inc. All Rights Reserved.
@License : BSD 3-Clause.
"""
from .._functions.web import set_browser_cookies, set_session_cookies
from .._functions.cookies import set_tab_cookies, set_session_cookies, set_browser_cookies
class CookiesSetter(object):
@ -20,7 +20,7 @@ class CookiesSetter(object):
:param cookies: cookies信息
:return: None
"""
set_browser_cookies(self._owner, cookies)
set_tab_cookies(self._owner, cookies)
def remove(self, name, url=None, domain=None, path=None):
"""删除一个cookie
@ -39,11 +39,21 @@ class CookiesSetter(object):
d['url'] = self._owner.url
if path is not None:
d['path'] = path
self._owner.run_cdp('Network.deleteCookies', **d)
self._owner._run_cdp('Storage.deleteCookies', **d)
def clear(self):
"""清除cookies"""
self._owner.run_cdp('Network.clearBrowserCookies')
self._owner._run_cdp('Storage.clearBrowserCookies')
class BrowserCookiesSetter(CookiesSetter):
def __call__(self, cookies):
"""设置一个或多个cookie
:param cookies: cookies信息
:return: None
"""
set_browser_cookies(self._owner, cookies)
class SessionCookiesSetter(object):

View File

@ -8,6 +8,7 @@
from http.cookiejar import Cookie, CookieJar
from typing import Union
from .._base.browser import Browser
from .._pages.chromium_base import ChromiumBase
from .._pages.chromium_tab import MixTab
from .._pages.session_page import SessionPage
@ -26,8 +27,14 @@ class CookiesSetter(object):
def clear(self) -> None: ...
class BrowserCookiesSetter(CookiesSetter):
_owner: Browser = ...
def __init__(self, page: Browser): ...
class SessionCookiesSetter(object):
_owner: SessionPage
_owner: SessionPage = ...
def __init__(self, page: SessionPage): ...
@ -39,7 +46,7 @@ class SessionCookiesSetter(object):
class WebPageCookiesSetter(CookiesSetter, SessionCookiesSetter):
_owner: Union[WebPage, MixTab]
_owner: Union[WebPage, MixTab] = ...
def __init__(self, page: SessionPage): ...

View File

@ -55,7 +55,7 @@ class DownloadManager(object):
if not self._running or tid == 'browser':
self._browser._driver.set_callback('Browser.downloadProgress', self._onDownloadProgress)
self._browser._driver.set_callback('Browser.downloadWillBegin', self._onDownloadWillBegin)
r = self._browser.run_cdp('Browser.setDownloadBehavior', downloadPath=self._browser._download_path,
r = self._browser._run_cdp('Browser.setDownloadBehavior', downloadPath=self._browser._download_path,
behavior='allowAndName', eventsEnabled=True)
if 'error' in r:
print('浏览器版本太低无法使用下载管理功能。')
@ -124,7 +124,7 @@ class DownloadManager(object):
"""
mission.state = 'canceled'
try:
self._browser.run_cdp('Browser.cancelDownload', guid=mission.id)
self._browser._run_cdp('Browser.cancelDownload', guid=mission.id)
except:
pass
if mission.final_path:
@ -137,7 +137,7 @@ class DownloadManager(object):
"""
mission.state = 'skipped'
try:
self._browser.run_cdp('Browser.cancelDownload', guid=mission.id)
self._browser._run_cdp('Browser.cancelDownload', guid=mission.id)
except:
pass

View File

@ -26,7 +26,7 @@ class Listener(object):
:param owner: ChromiumBase对象
"""
self._owner = owner
self._address = owner.address
self._address = owner.browser.address
self._target_id = owner._target_id
self._driver = None
self._running_requests = 0

View File

@ -18,7 +18,7 @@ class ElementRect(object):
def corners(self):
"""返回元素四个角坐标顺序左上、右上、右下、左下没有大小的元素抛出NoRectError"""
vr = self._get_viewport_rect('border')
r = self._ele.owner.run_cdp_loaded('Page.getLayoutMetrics')['visualViewport']
r = self._ele.owner._run_cdp_loaded('Page.getLayoutMetrics')['visualViewport']
sx = r['pageX']
sy = r['pageY']
return [(vr[0] + sx, vr[1] + sy), (vr[2] + sx, vr[3] + sy), (vr[4] + sx, vr[5] + sy), (vr[6] + sx, vr[7] + sy)]
@ -32,7 +32,7 @@ class ElementRect(object):
@property
def size(self):
"""返回元素大小,格式(宽, 高)"""
border = self._ele.owner.run_cdp('DOM.getBoxModel', backendNodeId=self._ele._backend_id,
border = self._ele.owner._run_cdp('DOM.getBoxModel', backendNodeId=self._ele._backend_id,
nodeId=self._ele._node_id, objectId=self._ele._obj_id)['model']['border']
return border[2] - border[0], border[5] - border[1]
@ -77,7 +77,7 @@ class ElementRect(object):
"""返回元素左上角在屏幕上坐标,左上角为(0, 0)"""
vx, vy = self._ele.owner.rect.viewport_location
ex, ey = self.viewport_location
pr = self._ele.owner.run_js('return window.devicePixelRatio;')
pr = self._ele.owner._run_js('return window.devicePixelRatio;')
return (vx + ex) * pr, (ey + vy) * pr
@property
@ -85,7 +85,7 @@ class ElementRect(object):
"""返回元素中点在屏幕上坐标,左上角为(0, 0)"""
vx, vy = self._ele.owner.rect.viewport_location
ex, ey = self.viewport_midpoint
pr = self._ele.owner.run_js('return window.devicePixelRatio;')
pr = self._ele.owner._run_js('return window.devicePixelRatio;')
return (vx + ex) * pr, (ey + vy) * pr
@property
@ -93,7 +93,7 @@ class ElementRect(object):
"""返回元素中点在屏幕上坐标,左上角为(0, 0)"""
vx, vy = self._ele.owner.rect.viewport_location
ex, ey = self.viewport_click_point
pr = self._ele.owner.run_js('return window.devicePixelRatio;')
pr = self._ele.owner._run_js('return window.devicePixelRatio;')
return (vx + ex) * pr, (ey + vy) * pr
def _get_viewport_rect(self, quad):
@ -101,13 +101,13 @@ class ElementRect(object):
:param quad: 方框类型margin border padding
:return: 四个角坐标
"""
return self._ele.owner.run_cdp('DOM.getBoxModel', backendNodeId=self._ele._backend_id,
return self._ele.owner._run_cdp('DOM.getBoxModel', backendNodeId=self._ele._backend_id,
# nodeId=self._ele._node_id, objectId=self._ele._obj_id
)['model'][quad]
def _get_page_coord(self, x, y):
"""根据视口坐标获取绝对坐标"""
r = self._ele.owner.run_cdp_loaded('Page.getLayoutMetrics')['visualViewport']
r = self._ele.owner._run_cdp_loaded('Page.getLayoutMetrics')['visualViewport']
sx = r['pageX']
sy = r['pageY']
return x + sx, y + sy
@ -174,13 +174,13 @@ class TabRect(object):
@property
def viewport_size_with_scrollbar(self):
"""返回视口宽高,包括滚动条,格式:(宽, 高)"""
r = self._owner.run_js('return window.innerWidth.toString() + " " + window.innerHeight.toString();')
r = self._owner._run_js('return window.innerWidth.toString() + " " + window.innerHeight.toString();')
w, h = r.split(' ')
return int(w), int(h)
def _get_page_rect(self):
"""获取页面范围信息"""
return self._owner.run_cdp_loaded('Page.getLayoutMetrics')
return self._owner._run_cdp_loaded('Page.getLayoutMetrics')
def _get_window_rect(self):
"""获取窗口范围信息"""
@ -214,8 +214,8 @@ class FrameRect(object):
@property
def size(self):
"""返回frame内页面尺寸格式(宽, 高)"""
w = self._frame.doc_ele.run_js('return this.body.scrollWidth')
h = self._frame.doc_ele.run_js('return this.body.scrollHeight')
w = self._frame.doc_ele._run_js('return this.body.scrollWidth')
h = self._frame.doc_ele._run_js('return this.body.scrollHeight')
return w, h
@property

View File

@ -48,7 +48,7 @@ class Screencast(object):
if self._mode.startswith('frugal'):
self._owner.driver.set_callback('Page.screencastFrame', self._onScreencastFrame)
self._owner.run_cdp('Page.startScreencast', everyNthFrame=1, quality=100)
self._owner._run_cdp('Page.startScreencast', everyNthFrame=1, quality=100)
elif not self._mode.startswith('js'):
self._running = True
@ -79,8 +79,8 @@ class Screencast(object):
}
'''
print('请手动选择要录制的目标。')
self._owner.run_js('var DrissionPage_Screencast_blob;var DrissionPage_Screencast_blob_ok=false;')
self._owner.run_js(js)
self._owner._run_js('var DrissionPage_Screencast_blob;var DrissionPage_Screencast_blob_ok=false;')
self._owner._run_js(js)
def stop(self, video_name=None):
"""停止录屏
@ -93,19 +93,19 @@ class Screencast(object):
path = f'{self._path}{sep}{name}'
if self._mode.startswith('js'):
self._owner.run_js('mediaRecorder.stop();', as_expr=True)
while not self._owner.run_js('return DrissionPage_Screencast_blob_ok;'):
self._owner._run_js('mediaRecorder.stop();', as_expr=True)
while not self._owner._run_js('return DrissionPage_Screencast_blob_ok;'):
sleep(.1)
blob = self._owner.run_js('return DrissionPage_Screencast_blob;')
uuid = self._owner.run_cdp('IO.resolveBlob', objectId=blob['result']['objectId'])['uuid']
data = self._owner.run_cdp('IO.read', handle=f'blob:{uuid}')['data']
blob = self._owner._run_js('return DrissionPage_Screencast_blob;')
uuid = self._owner._run_cdp('IO.resolveBlob', objectId=blob['result']['objectId'])['uuid']
data = self._owner._run_cdp('IO.read', handle=f'blob:{uuid}')['data']
with open(path, 'wb') as f:
f.write(b64decode(data))
return path
if self._mode.startswith('frugal'):
self._owner.driver.set_callback('Page.screencastFrame', None)
self._owner.run_cdp('Page.stopScreencast')
self._owner._run_cdp('Page.stopScreencast')
else:
self._enable = False
while self._running:
@ -164,7 +164,7 @@ class Screencast(object):
path = self._tmp_path or self._path
with open(f'{path}{sep}{kwargs["metadata"]["timestamp"]}.jpg', 'wb') as f:
f.write(b64decode(kwargs['data']))
self._owner.run_cdp('Page.screencastFrameAck', sessionId=kwargs['sessionId'])
self._owner._run_cdp('Page.screencastFrameAck', sessionId=kwargs['sessionId'])
class ScreencastMode(object):

View File

@ -21,7 +21,7 @@ class Scroller(object):
def _run_js(self, js):
js = js.format(self.t1, self.t2, self.t2)
self._driver.run_js(js)
self._driver._run_js(js)
self._wait_scrolled()
def to_top(self):
@ -88,14 +88,14 @@ class Scroller(object):
return
owner = self._driver.owner if self._driver._type == 'ChromiumElement' else self._driver
r = owner.run_cdp('Page.getLayoutMetrics')
r = owner._run_cdp('Page.getLayoutMetrics')
x = r['layoutViewport']['pageX']
y = r['layoutViewport']['pageY']
end_time = perf_counter() + owner.timeout
while perf_counter() < end_time:
sleep(.1)
r = owner.run_cdp('Page.getLayoutMetrics')
r = owner._run_cdp('Page.getLayoutMetrics')
x1 = r['layoutViewport']['pageX']
y1 = r['layoutViewport']['pageY']
@ -144,9 +144,9 @@ class PageScroller(Scroller):
:return: None
"""
txt = 'true' if center else 'false'
ele.run_js(f'this.scrollIntoViewIfNeeded({txt});')
ele._run_js(f'this.scrollIntoViewIfNeeded({txt});')
if center or (center is not False and ele.states.is_covered):
ele.run_js('''function getWindowScrollTop() {let scroll_top = 0;
ele._run_js('''function getWindowScrollTop() {let scroll_top = 0;
if (document.documentElement && document.documentElement.scrollTop) {
scroll_top = document.documentElement.scrollTop;
} else if (document.body) {scroll_top = document.body.scrollTop;}

View File

@ -45,7 +45,7 @@ class SelectElement(object):
"""返回第一个被选中的option元素
:return: ChromiumElement对象或None
"""
ele = self._ele.run_js('return this.options[this.selectedIndex];')
ele = self._ele._run_js('return this.options[this.selectedIndex];')
return ele
@property
@ -69,7 +69,7 @@ class SelectElement(object):
for i in self.options:
change = True
mode = 'false' if i.states.is_selected else 'true'
i.run_js(f'this.selected={mode};')
i._run_js(f'this.selected={mode};')
if change:
self._dispatch_change()
@ -258,12 +258,12 @@ class SelectElement(object):
if not self.is_multi and len(option) > 1:
option = option[:1]
for o in option:
o.run_js(f'this.selected={mode};')
o._run_js(f'this.selected={mode};')
self._dispatch_change()
else:
option.run_js(f'this.selected={mode};')
option._run_js(f'this.selected={mode};')
self._dispatch_change()
def _dispatch_change(self):
"""触发修改动作"""
self._ele.run_js('this.dispatchEvent(new CustomEvent("change", {bubbles: true}));')
self._ele._run_js('this.dispatchEvent(new CustomEvent("change", {bubbles: true}));')

View File

@ -10,7 +10,7 @@ from time import sleep
from requests.structures import CaseInsensitiveDict
from .cookies_setter import SessionCookiesSetter, CookiesSetter, WebPageCookiesSetter
from .cookies_setter import SessionCookiesSetter, CookiesSetter, WebPageCookiesSetter, BrowserCookiesSetter
from .._functions.settings import Settings
from .._functions.tools import show_or_hide_browser
from .._functions.web import format_headers
@ -54,6 +54,13 @@ class BasePageSetter(object):
class BrowserBaseSetter(BasePageSetter):
"""Browser和ChromiumBase设置"""
def __init__(self, owner):
"""
:param owner: ChromiumBase对象
"""
super().__init__(owner)
self._cookies_setter = None
@property
def load_mode(self):
"""返回用于设置页面加载策略的对象"""
@ -78,8 +85,12 @@ class BrowserBaseSetter(BasePageSetter):
class BrowserSetter(BrowserBaseSetter):
def cookies(self, cookies):
pass # todo: 研究Storage.setCookies和Network.setCookies差别
@property
def cookies(self):
"""返回用于设置cookies的对象"""
if self._cookies_setter is None:
self._cookies_setter = BrowserCookiesSetter(self._owner)
return self._cookies_setter
def tab_to_front(self, tab_or_id):
"""激活标签页使其处于最前面
@ -128,12 +139,6 @@ class BrowserSetter(BrowserBaseSetter):
class ChromiumBaseSetter(BrowserBaseSetter):
def __init__(self, owner):
"""
:param owner: ChromiumBase对象
"""
super().__init__(owner)
self._cookies_setter = None
@property
def scroll(self):
@ -156,7 +161,7 @@ class ChromiumBaseSetter(BrowserBaseSetter):
keys = {'userAgent': ua}
if platform:
keys['platform'] = platform
self._owner.run_cdp('Emulation.setUserAgentOverride', **keys)
self._owner._run_cdp('Emulation.setUserAgentOverride', **keys)
def session_storage(self, item, value):
"""设置或删除某项sessionStorage信息
@ -164,15 +169,15 @@ class ChromiumBaseSetter(BrowserBaseSetter):
:param value: 项的值设置为False时删除该项
:return: None
"""
self._owner.run_cdp_loaded('DOMStorage.enable')
i = self._owner.run_cdp('Storage.getStorageKeyForFrame', frameId=self._owner._frame_id)['storageKey']
self._owner._run_cdp_loaded('DOMStorage.enable')
i = self._owner._run_cdp('Storage.getStorageKeyForFrame', frameId=self._owner._frame_id)['storageKey']
if value is False:
self._owner.run_cdp('DOMStorage.removeDOMStorageItem',
self._owner._run_cdp('DOMStorage.removeDOMStorageItem',
storageId={'storageKey': i, 'isLocalStorage': False}, key=item)
else:
self._owner.run_cdp('DOMStorage.setDOMStorageItem', storageId={'storageKey': i, 'isLocalStorage': False},
self._owner._run_cdp('DOMStorage.setDOMStorageItem', storageId={'storageKey': i, 'isLocalStorage': False},
key=item, value=value)
self._owner.run_cdp_loaded('DOMStorage.disable')
self._owner._run_cdp_loaded('DOMStorage.disable')
def local_storage(self, item, value):
"""设置或删除某项localStorage信息
@ -180,15 +185,15 @@ class ChromiumBaseSetter(BrowserBaseSetter):
:param value: 项的值设置为False时删除该项
:return: None
"""
self._owner.run_cdp_loaded('DOMStorage.enable')
i = self._owner.run_cdp('Storage.getStorageKeyForFrame', frameId=self._owner._frame_id)['storageKey']
self._owner._run_cdp_loaded('DOMStorage.enable')
i = self._owner._run_cdp('Storage.getStorageKeyForFrame', frameId=self._owner._frame_id)['storageKey']
if value is False:
self._owner.run_cdp('DOMStorage.removeDOMStorageItem',
self._owner._run_cdp('DOMStorage.removeDOMStorageItem',
storageId={'storageKey': i, 'isLocalStorage': True}, key=item)
else:
self._owner.run_cdp('DOMStorage.setDOMStorageItem', storageId={'storageKey': i, 'isLocalStorage': True},
self._owner._run_cdp('DOMStorage.setDOMStorageItem', storageId={'storageKey': i, 'isLocalStorage': True},
key=item, value=value)
self._owner.run_cdp_loaded('DOMStorage.disable')
self._owner._run_cdp_loaded('DOMStorage.disable')
def upload_files(self, files):
"""等待上传的文件路径
@ -197,7 +202,7 @@ class ChromiumBaseSetter(BrowserBaseSetter):
"""
if not self._owner._upload_list:
self._owner.driver.set_callback('Page.fileChooserOpened', self._owner._onFileChooserOpened)
self._owner.run_cdp('Page.setInterceptFileChooserDialog', enabled=True)
self._owner._run_cdp('Page.setInterceptFileChooserDialog', enabled=True)
if isinstance(files, str):
files = files.split('\n')
@ -210,8 +215,8 @@ class ChromiumBaseSetter(BrowserBaseSetter):
:param headers: dict格式的headers数据
:return: None
"""
self._owner.run_cdp('Network.enable')
self._owner.run_cdp('Network.setExtraHTTPHeaders', headers=format_headers(headers))
self._owner._run_cdp('Network.enable')
self._owner._run_cdp('Network.setExtraHTTPHeaders', headers=format_headers(headers))
def auto_handle_alert(self, on_off=True, accept=True):
"""设置是否启用自动处理弹窗
@ -232,8 +237,8 @@ class ChromiumBaseSetter(BrowserBaseSetter):
urls = (urls,)
if not isinstance(urls, (list, tuple)):
raise TypeError('urls需传入str、list或tuple类型。')
self._owner.run_cdp('Network.enable')
self._owner.run_cdp('Network.setBlockedURLs', urls=urls)
self._owner._run_cdp('Network.enable')
self._owner._run_cdp('Network.setBlockedURLs', urls=urls)
class TabSetter(ChromiumBaseSetter):
@ -522,11 +527,11 @@ class ChromiumElementSetter(object):
:return: None
"""
try:
self._ele.owner.run_cdp('DOM.setAttributeValue',
self._ele.owner._run_cdp('DOM.setAttributeValue',
nodeId=self._ele._node_id, name=name, value=str(value))
except ElementLostError:
self._ele._refresh_id()
self._ele.owner.run_cdp('DOM.setAttributeValue',
self._ele.owner._run_cdp('DOM.setAttributeValue',
nodeId=self._ele._node_id, name=name, value=str(value))
def property(self, name, value):
@ -536,7 +541,7 @@ class ChromiumElementSetter(object):
:return: None
"""
value = value.replace('"', r'\"')
self._ele.run_js(f'this.{name}="{value}";')
self._ele._run_js(f'this.{name}="{value}";')
def style(self, name, value):
"""设置元素style样式
@ -545,7 +550,7 @@ class ChromiumElementSetter(object):
:return: None
"""
try:
self._ele.run_js(f'this.style.{name}="{value}";')
self._ele._run_js(f'this.style.{name}="{value}";')
except JavaScriptError:
raise ValueError(f'设置失败,请检查属性名{name}')
@ -629,7 +634,7 @@ class PageScrollSetter(object):
if not isinstance(on_off, bool):
raise TypeError('on_off必须为bool。')
b = 'smooth' if on_off else 'auto'
self._scroll._driver.run_js(f'document.documentElement.style.setProperty("scroll-behavior","{b}");')
self._scroll._driver._run_js(f'document.documentElement.style.setProperty("scroll-behavior","{b}");')
self._scroll._wait_complete = on_off
@ -703,7 +708,7 @@ class WindowSetter(object):
"""获取窗口位置及大小信息"""
for _ in range(50):
try:
return self._owner.run_cdp('Browser.getWindowForTarget')
return self._owner._run_cdp('Browser.getWindowForTarget')
except:
sleep(.1)
@ -713,7 +718,7 @@ class WindowSetter(object):
:return: None
"""
try:
self._owner.run_cdp('Browser.setWindowBounds', windowId=self._window_id, bounds=bounds)
self._owner._run_cdp('Browser.setWindowBounds', windowId=self._window_id, bounds=bounds)
except:
raise RuntimeError('浏览器全屏或最小化状态时请先调用set.window.normal()恢复正常状态。')

View File

@ -11,7 +11,7 @@ from typing import Union, Tuple, Literal, Any, Optional
from requests.adapters import HTTPAdapter
from requests.auth import HTTPBasicAuth
from .cookies_setter import SessionCookiesSetter, CookiesSetter, WebPageCookiesSetter
from .cookies_setter import SessionCookiesSetter, CookiesSetter, WebPageCookiesSetter, BrowserCookiesSetter
from .scroller import PageScroller
from .._base.base import BasePage
from .._base.browser import Browser
@ -40,6 +40,7 @@ class BasePageSetter(object):
class BrowserBaseSetter(BasePageSetter):
_cookies_setter: Optional[CookiesSetter] = ...
@property
def load_mode(self) -> LoadMode: ...
@ -49,10 +50,12 @@ class BrowserBaseSetter(BasePageSetter):
class BrowserSetter(BasePageSetter):
_owner: Browser = ...
_cookies_setter: BrowserCookiesSetter = ...
def tab_to_front(self, tab_or_id: Union[str, ChromiumTab]) -> None: ...
def cookies(self, cookies): ...
@property
def cookies(self) -> BrowserCookiesSetter: ...
def auto_handle_alert(self, on_off: bool = True, accept: bool = True): ...
@ -64,9 +67,10 @@ class BrowserSetter(BasePageSetter):
class ChromiumBaseSetter(BasePageSetter):
def __init__(self, owner):
self._owner: ChromiumBase = ...
self._cookies_setter: CookiesSetter = ...
_owner: ChromiumBase = ...
_cookies_setter: CookiesSetter = ...
def __init__(self, owner): ...
@property
def load_mode(self) -> LoadMode: ...

View File

@ -19,30 +19,30 @@ class ElementStates(object):
@property
def is_selected(self):
"""返回列表元素是否被选择"""
return self._ele.run_js('return this.selected;')
return self._ele._run_js('return this.selected;')
@property
def is_checked(self):
"""返回元素是否被选择"""
return self._ele.run_js('return this.checked;')
return self._ele._run_js('return this.checked;')
@property
def is_displayed(self):
"""返回元素是否显示"""
return not (self._ele.style('visibility') == 'hidden' or
self._ele.run_js('return this.offsetParent === null;')
self._ele._run_js('return this.offsetParent === null;')
or self._ele.style('display') == 'none' or self._ele.property('hidden'))
@property
def is_enabled(self):
"""返回元素是否可用"""
return not self._ele.run_js('return this.disabled;')
return not self._ele._run_js('return this.disabled;')
@property
def is_alive(self):
"""返回元素是否仍在DOM中"""
try:
return self._ele.owner.run_cdp('DOM.describeNode',
return self._ele.owner._run_cdp('DOM.describeNode',
backendNodeId=self._ele._backend_id)['node']['nodeId'] != 0
except ElementLostError:
return False
@ -66,7 +66,7 @@ class ElementStates(object):
"""返回元素是否被覆盖与是否在视口中无关如被覆盖返回覆盖元素的backend id否则返回False"""
lx, ly = self._ele.rect.click_point
try:
bid = self._ele.owner.run_cdp('DOM.getNodeForLocation', x=int(lx), y=int(ly)).get('backendNodeId')
bid = self._ele.owner._run_cdp('DOM.getNodeForLocation', x=int(lx), y=int(ly)).get('backendNodeId')
return bid if bid != self._ele._backend_id else False
except CDPError:
return False
@ -95,13 +95,13 @@ class ShadowRootStates(object):
@property
def is_enabled(self):
"""返回元素是否可用"""
return not self._ele.run_js('return this.disabled;')
return not self._ele._run_js('return this.disabled;')
@property
def is_alive(self):
"""返回元素是否仍在DOM中"""
try:
return self._ele.owner.run_cdp('DOM.describeNode',
return self._ele.owner._run_cdp('DOM.describeNode',
backendNodeId=self._ele._backend_id)['node']['nodeId'] != 0
except ElementLostError:
return False
@ -125,7 +125,7 @@ class PageStates(object):
def is_alive(self):
"""返回页面对象是否仍然可用"""
try:
self._owner.run_cdp('Page.getLayoutMetrics')
self._owner._run_cdp('Page.getLayoutMetrics')
return True
except PageDisconnectedError:
return False
@ -157,7 +157,7 @@ class FrameStates(object):
def is_alive(self):
"""返回frame元素是否可用且里面仍挂载有frame"""
try:
node = self._frame._target_page.run_cdp('DOM.describeNode',
node = self._frame._target_page._run_cdp('DOM.describeNode',
backendNodeId=self._frame._frame_ele._backend_id)['node']
except (ElementLostError, PageDisconnectedError):
return False
@ -172,7 +172,7 @@ class FrameStates(object):
def is_displayed(self):
"""返回iframe是否显示"""
return not (self._frame.frame_ele.style('visibility') == 'hidden'
or self._frame.frame_ele.run_js('return this.offsetParent === null;')
or self._frame.frame_ele._run_js('return this.offsetParent === null;')
or self._frame.frame_ele.style('display') == 'none')
@property