diff --git a/DrissionPage/__init__.py b/DrissionPage/__init__.py index bda0836..ec1a2e6 100644 --- a/DrissionPage/__init__.py +++ b/DrissionPage/__init__.py @@ -14,7 +14,7 @@ from .configs.session_options import SessionOptions # 常用工具 from .action_chains import ActionChains -from .keys import Keys +from .common.keys import Keys # 旧版页面类和启动配置类 from .mixpage.mix_page import MixPage diff --git a/DrissionPage/action_chains.py b/DrissionPage/action_chains.py index d0caab5..67ac4ad 100644 --- a/DrissionPage/action_chains.py +++ b/DrissionPage/action_chains.py @@ -5,8 +5,8 @@ """ from time import sleep +from .common.keys import modifierBit, keyDescriptionForString from .common.web import location_in_viewport -from .keys import _modifierBit, _keyDescriptionForString class ActionChains: @@ -230,7 +230,7 @@ class ActionChains: :return: self """ if key in ('\ue009', '\ue008', '\ue00a', '\ue03d'): # 如果上修饰符,添加到变量 - self.modifier |= _modifierBit.get(key, 0) + self.modifier |= modifierBit.get(key, 0) return self data = self._get_key_data(key, 'keyDown') @@ -243,7 +243,7 @@ class ActionChains: :return: self """ if key in ('\ue009', '\ue008', '\ue00a', '\ue03d'): # 如果上修饰符,添加到变量 - self.modifier ^= _modifierBit.get(key, 0) + self.modifier ^= modifierBit.get(key, 0) return self data = self._get_key_data(key, 'keyUp') @@ -272,7 +272,7 @@ class ActionChains: :param action: 'keyDown' 或 'keyUp' :return: 按键信息 """ - description = _keyDescriptionForString(self.modifier, key) + description = keyDescriptionForString(self.modifier, key) text = description['text'] if action != 'keyUp': action = 'keyDown' if text else 'rawKeyDown' diff --git a/DrissionPage/chromium_base.py b/DrissionPage/chromium_base.py index c2cdfbd..24df257 100644 --- a/DrissionPage/chromium_base.py +++ b/DrissionPage/chromium_base.py @@ -937,8 +937,17 @@ class ChromiumPageScroll(ChromiumScroll): :param loc_or_ele: 元素的定位信息,可以是loc元组,或查询字符串 :return: None """ + ID = None ele = self._driver.ele(loc_or_ele) - ele.run_js('this.scrollIntoView({behavior: "smooth", block: "center", inline: "center"});') + ele.run_js('this.scrollIntoView({behavior: "auto", block: "nearest", inline: "nearest"});') + x, y = ele.location + try: + ID = ele.page.run_cdp('DOM.getNodeForLocation', x=x, y=y).get('nodeId', None) + except Exception: + pass + + if ID != ele.ids.node_id: + ele.run_js('this.scrollIntoView({behavior: "auto", block: "center", inline: "center"});') class Timeout(object): diff --git a/DrissionPage/chromium_element.py b/DrissionPage/chromium_element.py index 0fd2c8e..d90761c 100644 --- a/DrissionPage/chromium_element.py +++ b/DrissionPage/chromium_element.py @@ -12,9 +12,9 @@ from warnings import warn from .base import DrissionElement, BaseElement from .common.constants import FRAME_ELEMENT, NoneElement from .common.errors import ContextLossError, ElementLossError, JavaScriptError, NoRectError +from .common.keys import keys_to_typing, keyDescriptionForString, keyDefinitions from .common.locator import get_loc from .common.web import make_absolute_link, get_ele_txt, format_html, is_js_func, location_in_viewport, offset_scroll -from .keys import _keys_to_typing, _keyDescriptionForString, _keyDefinitions from .session_element import make_session_ele @@ -457,6 +457,7 @@ class ChromiumElement(DrissionElement): sleep(.1) self.page.scroll.to_see(self) + sleep(1) left, top = self.location height, width = self.size left_top = (left, top) @@ -486,7 +487,7 @@ class ChromiumElement(DrissionElement): # ------------处理字符------------- if not isinstance(vals, (tuple, list)): vals = (str(vals),) - modifier, vals = _keys_to_typing(vals) + modifier, vals = keys_to_typing(vals) if modifier != 0: # 包含修饰符 for key in vals: @@ -1386,11 +1387,11 @@ def send_enter(ele): def send_key(ele, modifier, key): """发送一个字,在键盘中的字符触发按键,其它直接发送文本""" - if key not in _keyDefinitions: + if key not in keyDefinitions: ele.page.run_cdp('Input.insertText', text=key) else: - description = _keyDescriptionForString(modifier, key) + description = keyDescriptionForString(modifier, key) text = description['text'] data = {'type': 'keyDown' if text else 'rawKeyDown', 'modifiers': modifier, @@ -1574,11 +1575,14 @@ class Locations(object): return self._ele.page.run_cdp('DOM.getBoxModel', nodeId=self._ele.ids.node_id)['model'][quad] def _get_page_coord(self, x, y): - """根据绝对坐标获取窗口坐标""" - js = 'return document.documentElement.scrollLeft+" "+document.documentElement.scrollTop;' - xy = self._ele.run_js(js) - sx, sy = xy.split(' ') - return int(x + float(sx)), int(y + float(sy)) + """根据视口坐标获取绝对坐标""" + # js = 'return document.documentElement.scrollLeft+" "+document.documentElement.scrollTop;' + # xy = self._ele.run_js(js) + # sx, sy = xy.split(' ') + r = self._ele.page.run_cdp_loaded('Page.getLayoutMetrics')['visualViewport'] + sx = r['pageX'] + sy = r['pageY'] + return x + sx, y + sy class Click(object): diff --git a/DrissionPage/chromium_frame.py b/DrissionPage/chromium_frame.py index b549d9b..a3e7fdc 100644 --- a/DrissionPage/chromium_frame.py +++ b/DrissionPage/chromium_frame.py @@ -220,6 +220,11 @@ class ChromiumFrame(ChromiumBase): self._check_ok() return self.frame_ele.location + @property + def locations(self): + """返回用于获取元素位置的对象""" + return self.frame_ele.locations + @property def xpath(self): """返回frame的xpath绝对路径""" @@ -296,7 +301,10 @@ class ChromiumFrame(ChromiumBase): :return: 运行的结果 """ self._check_ok() - return self.doc_ele.run_js(script, *args, as_expr=as_expr) + if script.startswith('this.scrollIntoView'): + return self.frame_ele.run_js(script, *args, as_expr=as_expr) + else: + return self.doc_ele.run_js(script, *args, as_expr=as_expr) def parent(self, level_or_loc=1): """返回上面某一级父元素,可指定层数或用查询语法定位 @@ -382,6 +390,22 @@ class ChromiumFrame(ChromiumBase): self._check_ok() return self.frame_ele.afters(filter_loc, timeout) + def get_screenshot(self, path=None, as_bytes=None, full_page=False, left_top=None, right_bottom=None): + """对页面进行截图,可对整个网页、可见网页、指定范围截图。对可视范围外截图需要90以上版本浏览器支持 + :param path: 完整路径,后缀可选 'jpg','jpeg','png','webp' + :param as_bytes: 是否已字节形式返回图片,可选 'jpg','jpeg','png','webp',生效时path参数无效 + :param full_page: 是否整页截图,为True截取整个网页,为False截取可视窗口 + :param left_top: 截取范围左上角坐标 + :param right_bottom: 截取范围右下角角坐标 + :return: 图片完整路径或字节文本 + """ + if full_page: + raise RuntimeError('暂未实现对iframe全页截图功能。') + if left_top is None and right_bottom is None: + return self.frame_ele.get_screenshot(path=path, as_bytes=as_bytes) + else: + raise RuntimeError('暂未实现对异域iframe内元素截图功能。') + def _ele(self, loc_or_ele, timeout=None, single=True, relative=False): """在frame内查找单个元素 :param loc_or_ele: 定位符或元素对象 diff --git a/DrissionPage/chromium_frame.pyi b/DrissionPage/chromium_frame.pyi index a7c17aa..ba3d181 100644 --- a/DrissionPage/chromium_frame.pyi +++ b/DrissionPage/chromium_frame.pyi @@ -6,7 +6,7 @@ from typing import Union, Tuple, List, Any from .chromium_base import ChromiumBase, ChromiumPageScroll, ChromiumBaseSetter -from .chromium_element import ChromiumElement +from .chromium_element import ChromiumElement, Locations class ChromiumFrame(ChromiumBase): @@ -83,6 +83,9 @@ class ChromiumFrame(ChromiumBase): @property def location(self) -> Tuple[int, int]: ... + @property + def locations(self) -> Locations: ... + @property def xpath(self) -> str: ... diff --git a/DrissionPage/cli.py b/DrissionPage/common/cli.py similarity index 100% rename from DrissionPage/cli.py rename to DrissionPage/common/cli.py diff --git a/DrissionPage/keys.py b/DrissionPage/common/keys.py similarity index 98% rename from DrissionPage/keys.py rename to DrissionPage/common/keys.py index de53373..a7a3c69 100644 --- a/DrissionPage/keys.py +++ b/DrissionPage/common/keys.py @@ -81,7 +81,7 @@ class Keys: # ZENKAKU_HANKAKU = '\ue040' -_keyDefinitions = { +keyDefinitions = { '0': {'keyCode': 48, 'key': '0', 'code': 'Digit0'}, '1': {'keyCode': 49, 'key': '1', 'code': 'Digit1'}, '2': {'keyCode': 50, 'key': '2', 'code': 'Digit2'}, @@ -333,20 +333,20 @@ _keyDefinitions = { '}': {'keyCode': 221, 'key': '}', 'code': 'BracketRight'}, '"': {'keyCode': 222, 'key': '"', 'code': 'Quote'}, } -_modifierBit = {'\ue00a': 1, - '\ue009': 2, - '\ue03d': 4, - '\ue008': 8} +modifierBit = {'\ue00a': 1, + '\ue009': 2, + '\ue03d': 4, + '\ue008': 8} -def _keys_to_typing(value) -> Tuple[int, str]: +def keys_to_typing(value) -> Tuple[int, str]: """把要输入的内容连成字符串,去掉其中 ctrl 等键。 返回的modifier表示是否有按下组合键""" typing: List[str] = [] modifier = 0 for val in value: if val in ('\ue009', '\ue008', '\ue00a', '\ue03d'): - modifier |= _modifierBit.get(val, 0) + modifier |= modifierBit.get(val, 0) continue if isinstance(val, (int, float)): val = str(val) @@ -359,7 +359,7 @@ def _keys_to_typing(value) -> Tuple[int, str]: return modifier, ''.join(typing) -def _keyDescriptionForString(_modifiers: int, keyString: str) -> Dict: # noqa: C901 +def keyDescriptionForString(_modifiers: int, keyString: str) -> Dict: # noqa: C901 shift = _modifiers & 8 description = {'key': '', 'keyCode': 0, diff --git a/setup.py b/setup.py index 7ba3aa3..626cae7 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ with open("README.md", "r", encoding='utf-8') as fh: setup( name="DrissionPage", - version="3.1.4", + version="3.2.0", author="g1879", author_email="g1879@qq.com", description="A module that integrates selenium and requests session, encapsulates common page operations.", @@ -37,7 +37,7 @@ setup( python_requires='>=3.6', entry_points={ 'console_scripts': [ - 'dp = DrissionPage.cli:main', + 'dp = DrissionPage.common.cli:main', ], }, )