mirror of
https://gitee.com/g1879/DrissionPage.git
synced 2024-12-10 04:00:23 +08:00
修复异域iframe跳转到同域时问题;其它修改,未完成
This commit is contained in:
parent
139bee5a91
commit
6d552330cd
@ -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
|
||||
|
@ -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): ...
|
||||
|
||||
|
@ -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: 新标签页跳转到的网址
|
||||
|
@ -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,
|
||||
|
@ -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'])
|
||||
|
@ -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):
|
||||
|
@ -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:
|
||||
|
@ -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: ...
|
||||
|
@ -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)
|
||||
|
233
DrissionPage/_functions/cookies.py
Normal file
233
DrissionPage/_functions/cookies.py
Normal 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)
|
44
DrissionPage/_functions/cookies.pyi
Normal file
44
DrissionPage/_functions/cookies.pyi
Normal 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: ...
|
@ -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)
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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: ...
|
||||
|
@ -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)
|
||||
|
@ -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: ...
|
||||
|
||||
|
||||
|
@ -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时只返回name、value、domain
|
||||
: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文本或tuple,tuple格式为(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
|
||||
|
@ -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]: ...
|
||||
|
@ -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'])
|
||||
|
@ -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: ...
|
||||
|
@ -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)
|
||||
|
@ -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则只返回name、value、domain
|
||||
: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):
|
||||
"""关闭当前标签页"""
|
||||
|
@ -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: ...
|
||||
|
||||
|
@ -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则只返回name、value、domain
|
||||
: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对象"""
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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):
|
||||
|
@ -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): ...
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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):
|
||||
|
@ -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;}
|
||||
|
@ -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}));')
|
||||
|
@ -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()恢复正常状态。')
|
||||
|
||||
|
@ -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: ...
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user