From 2d9e101c698f8b8c56fe383c0dd14decaf777bb2 Mon Sep 17 00:00:00 2001 From: g1879 Date: Tue, 1 Nov 2022 00:38:42 +0800 Subject: [PATCH] =?UTF-8?q?=E7=BB=A7=E7=BB=AD=E5=BC=80=E5=8F=91=E6=96=B0?= =?UTF-8?q?=E7=89=88=EF=BC=8C=E6=9C=AA=E5=AE=8C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DrissionPage/chrome_element.py | 5 +++ DrissionPage/chrome_page.py | 79 ++++++++++++++++++++++++++++++++-- DrissionPage/common.py | 10 +++++ DrissionPage/drission.py | 1 - 4 files changed, 90 insertions(+), 5 deletions(-) diff --git a/DrissionPage/chrome_element.py b/DrissionPage/chrome_element.py index 2cabcf9..07b976b 100644 --- a/DrissionPage/chrome_element.py +++ b/DrissionPage/chrome_element.py @@ -107,6 +107,11 @@ function getElementPagePosition(element){ self._scroll = ChromeScroll(self) return self._scroll + def run_script(self, arg: 'ChromeElement'): + js = 'function(){alert(arguments[0].value);}' + return self.page.driver.Runtime.callFunctionOn(functionDeclaration=js, objectId=self.obj_id, + arguments=[{'objectId': arg.obj_id}]) + def ele(self, loc_or_str: Union[Tuple[str, str], str], timeout: float = None) -> Union['ChromeElement', str, None]: diff --git a/DrissionPage/chrome_page.py b/DrissionPage/chrome_page.py index e73d177..b8bcdc4 100644 --- a/DrissionPage/chrome_page.py +++ b/DrissionPage/chrome_page.py @@ -1,15 +1,16 @@ # -*- coding:utf-8 -*- from base64 import b64decode +from math import inf from pathlib import Path from time import perf_counter, sleep -from typing import Union, Tuple, List +from typing import Union, Tuple, List, Any from pychrome import Tab from requests import get as requests_get from json import loads from .base import BasePage -from .common import get_loc +from .common import get_loc, is_js_func from .drission import connect_chrome from .chrome_element import ChromeElement, ChromeScroll @@ -19,14 +20,21 @@ class ChromePage(BasePage): def __init__(self, address: str, path: str = 'chrome', tab_handle: str = None, + args: list = None, timeout: float = 10): super().__init__(timeout) self.debugger_address = address[7:] if address.startswith('http://') else address - connect_chrome(path, self.debugger_address) + connect_chrome(path, self.debugger_address, args) tab_handle = self.tab_handles[0] if not tab_handle else tab_handle self._connect_debugger(tab_handle) + self.version = self._get_version() + self._main_version = int(self.version.split('.')[0]) self._scroll = None + def _get_version(self): + browser = requests_get(f'http://{self.debugger_address}/json/version').json()['Browser'] + return browser.split('/')[1] + def __call__(self, loc_or_str: Union[Tuple[str, str], str, 'ChromeElement'], timeout: float = None) -> Union['ChromeElement', str, None]: """在内部查找元素 \n @@ -97,6 +105,28 @@ class ChromePage(BasePage): h = self.driver.Runtime.evaluate(expression='document.body.scrollHeight;')['result']['value'] return {'height': h, 'width': w} + def run_script(self, script: str, *args: Any) -> Any: + if not args and not is_js_func(script): + res = self.run_cdp('Runtime.evaluate', + expression=script, + returnByValue=False, + awaitPromise=True, + userGesture=True) + + else: + res = self.run_cdp('Runtime.callFunctionOn', + functionDeclaration=script, + # 'executionContextId': self._contextId, + arguments=[convert_argument(arg) for arg in args], + returnByValue=False, + awaitPromise=True, + userGesture=True) + + exceptionDetails = res.get('exceptionDetails') + if exceptionDetails: + raise RuntimeError(f'Evaluation failed: {exceptionDetails}') + return _parse_js_result(self, res.get('result')) + def get(self, url: str, show_errmsg: bool = False, @@ -204,6 +234,10 @@ class ChromePage(BasePage): raise TypeError(f'不支持的文件格式:{pic_type}。') pic_type = 'jpeg' if pic_type == '.jpg' else pic_type[1:] + if full_page and self._main_version < 90: + print('注意:版本号大于90的chrome才支持整页截图。') + full_page = False + hw = self.size if full_page: vp = {'x': 0, 'y': 0, 'width': hw['width'], 'height': hw['height'], 'scale': 1} @@ -224,7 +258,7 @@ class ChromePage(BasePage): return png path.parent.mkdir(parents=True, exist_ok=True) - with open(path, 'wb')as f: + with open(path, 'wb') as f: f.write(png) return str(path.absolute()) @@ -476,3 +510,40 @@ def _get_tabs(handles: list, num_or_handles: Union[int, str, list, tuple, set]) raise TypeError('num_or_handle参数只能是int、str、list、set 或 tuple类型。') return set(i if isinstance(i, str) else handles[i] for i in num_or_handles) + + +def _parse_js_result(page: ChromePage, result: dict): + """解析js返回的结果""" + the_type = result['type'] + if the_type in ('string', 'number', 'boolean'): + return result['value'] + elif the_type == 'undefined': + return None + elif the_type == 'object': + sub_type = result['subtype'] + if sub_type == 'null': + return None + elif sub_type == 'node': + return ChromeElement(page, obj_id=result['objectId']) + elif sub_type == 'array': + + +def convert_argument(arg: Any) -> dict: + pass + """把参数转换成js能够接收的形式""" + # if arg == inf: + # return {'unserializableValue': 'Infinity'} + # if arg == -inf: + # return {'unserializableValue': '-Infinity'} + # objectHandle = arg if isinstance(arg, JSHandle) else None + # if objectHandle: + # if objectHandle._context != self: + # raise ElementHandleError('JSHandles can be evaluated only in the context they were created!') + # if objectHandle._disposed: + # raise ElementHandleError('JSHandle is disposed!') + # if objectHandle._remoteObject.get('unserializableValue'): + # return {'unserializableValue': objectHandle._remoteObject.get('unserializableValue')} # noqa: E501 + # if not objectHandle._remoteObject.get('objectId'): + # return {'value': objectHandle._remoteObject.get('value')} + # return {'objectId': objectHandle._remoteObject.get('objectId')} + # return {'value': arg} diff --git a/DrissionPage/common.py b/DrissionPage/common.py index 815305f..0ba39be 100644 --- a/DrissionPage/common.py +++ b/DrissionPage/common.py @@ -477,3 +477,13 @@ def make_absolute_link(link, page=None) -> str: # 绝对路径且不缺协议,直接返回 return link + + +def is_js_func(func: str) -> bool: + """检查文本是否js函数""" + func = func.strip() + if func.startswith('function') or func.startswith('async '): + return True + elif '=>' in func: + return True + return False diff --git a/DrissionPage/drission.py b/DrissionPage/drission.py index 88f6f35..0578285 100644 --- a/DrissionPage/drission.py +++ b/DrissionPage/drission.py @@ -428,7 +428,6 @@ def connect_chrome(chrome_path: str, debugger_address: str, args: list = None, p chrome_path = get_exe_path_from_port(port) if chrome_path == 'chrome.exe' else chrome_path return chrome_path, None - # ----------为路径加上双引号,避免路径中的空格产生异常---------- args = [] if args is None else args args1 = [] for arg in args: