Tab可处理自己的alert;重构处理alert逻辑,alert存在时也可处理非js命令

This commit is contained in:
g1879 2023-10-24 23:50:16 +08:00
parent d587ca6095
commit 2939e4d42b
10 changed files with 115 additions and 91 deletions

View File

@ -24,7 +24,7 @@ class ChromiumDriver(object):
self.address = address
self.type = tab_type
self._debug = False
self.has_alert = False
self.alert_flag = False # 标记alert出现跳过一条请求后复原
self._websocket_url = f'ws://{address}/devtools/{tab_type}/{tab_id}'
self._cur_id = 0
@ -77,8 +77,9 @@ class ChromiumDriver(object):
return self.method_results[message['id']].get_nowait()
except Empty:
if self.has_alert:
return {'error': {'message': 'alert exists'}, 'type': 'alert_exists'}
if self.alert_flag:
self.alert_flag = False
return {'result': []}
if timeout is not None and perf_counter() > timeout:
return {'error': {'message': 'timeout'}}
@ -114,7 +115,10 @@ class ChromiumDriver(object):
print(f'<收 {msg_json}')
break
if "method" in msg:
if 'method' in msg:
if msg['method'].startswith('Page.javascriptDialog'):
self.alert_flag = msg['method'].endswith('Opening')
self.event_queue.put(msg)
elif msg.get('id') in self.method_results:

View File

@ -23,7 +23,7 @@ class ChromiumDriver(object):
address: str
type: str
_debug: bool
has_alert: bool
alert_flag: bool
_websocket_url: str
_cur_id: int
_ws: Optional[WebSocket]

View File

@ -5,7 +5,6 @@
"""
from ..errors import ElementNotFoundError
HANDLE_ALERT_METHOD = 'Page.handleJavaScriptDialog'
FRAME_ELEMENT = ('iframe', 'frame')
ERROR = 'error'

View File

@ -18,7 +18,7 @@ from .._units.clicker import Clicker
from .._units.setter import ChromiumElementSetter
from .._units.waiter import ChromiumElementWaiter
from ..errors import ContextLossError, ElementLossError, JavaScriptError, ElementNotFoundError, \
CDPError, NoResourceError, NoRectError
CDPError, NoResourceError, NoRectError, AlertExistsError
class ChromiumElement(DrissionElement):
@ -206,6 +206,14 @@ class ChromiumElement(DrissionElement):
return self._select
def check(self, uncheck=False):
"""选中或取消选中当前元素
:param uncheck: 是否取消选中
:return: None
"""
js = 'this.checked=false' if uncheck else 'this.checked=true'
self.run_js(js)
def parent(self, level_or_loc=1, index=1):
"""返回上面某一级父元素,可指定层数或用查询语法定位
:param level_or_loc: 第几级父元素或定位符
@ -1303,6 +1311,9 @@ def run_js(page_or_ele, script, as_expr=False, timeout=None, args=None):
obj_id = page_or_ele._root_id
is_page = True
if page.has_alert:
raise AlertExistsError
try:
if as_expr:
res = page.run_cdp('Runtime.evaluate', expression=script, returnByValue=False,

View File

@ -150,6 +150,8 @@ class ChromiumElement(DrissionElement):
@property
def select(self) -> ChromiumSelect: ...
def check(self, uncheck: bool = False) -> None: ...
def attr(self, attr: str) -> Union[str, None]: ...
def remove_attr(self, attr: str) -> None: ...

View File

@ -15,7 +15,7 @@ from requests import get
from .._base.base import BasePage
from .._base.chromium_driver import ChromiumDriver
from .._commons.constants import HANDLE_ALERT_METHOD, ERROR, NoneElement
from .._commons.constants import ERROR, NoneElement
from .._commons.locator import get_loc
from .._commons.tools import get_usable_path, clean_folder
from .._commons.web import location_in_viewport
@ -47,6 +47,7 @@ class ChromiumBase(BasePage):
self._screencast = None
self._actions = None
self._listener = None
self._has_alert = False
self._download_path = str(Path('../..').absolute())
@ -103,6 +104,9 @@ class ChromiumBase(BasePage):
if is_init and hasattr(self, '_driver'):
return # ChromiumPage接收ChromiumDriver方式启动时
self._driver = ChromiumDriver(tab_id=tab_id, tab_type='page', address=self.address)
self._alert = Alert()
self._driver.set_listener('Page.javascriptDialogOpening', self._on_alert_open)
self._driver.set_listener('Page.javascriptDialogClosed', self._on_alert_close)
self._driver.call_method('DOM.enable')
self._driver.call_method('Page.enable')
@ -429,14 +433,19 @@ class ChromiumBase(BasePage):
self._listener = NetworkListener(self)
return self._listener
@property
def has_alert(self):
"""返回是否存在提示框"""
return self._has_alert
def run_cdp(self, cmd, **cmd_args):
"""执行Chrome DevTools Protocol语句
:param cmd: 协议项目
:param cmd_args: 参数
:return: 执行的结果
"""
if self.driver.has_alert and cmd != HANDLE_ALERT_METHOD:
raise AlertExistsError
# if self.driver.has_alert and cmd != HANDLE_ALERT_METHOD:
# raise AlertExistsError
r = self.driver.call_method(cmd, **cmd_args)
if ERROR not in r:
@ -824,6 +833,48 @@ class ChromiumBase(BasePage):
if cookies:
self.run_cdp_loaded('Network.clearBrowserCookies')
def handle_alert(self, accept=True, send=None, timeout=None):
"""处理提示框,可以自动等待提示框出现
:param accept: True表示确认False表示取消其它值不会按按钮但依然返回文本值
:param send: 处理prompt提示框时可输入文本
:param timeout: 等待提示框出现的超时时间为None则使用self.timeout属性的值
:return: 提示框内容文本未等到提示框则返回False
"""
timeout = self.timeout if timeout is None else timeout
timeout = .1 if timeout <= 0 else timeout
end_time = perf_counter() + timeout
while not self._alert.activated and perf_counter() < end_time:
sleep(.1)
if not self._alert.activated:
return False
res_text = self._alert.text
if self._alert.type == 'prompt':
self.driver.call_method('Page.handleJavaScriptDialog', accept=accept, promptText=send)
else:
self.driver.call_method('Page.handleJavaScriptDialog', accept=accept)
return res_text
def _on_alert_close(self, **kwargs):
"""alert关闭时触发的方法"""
self._alert.activated = False
self._alert.text = None
self._alert.type = None
self._alert.defaultPrompt = None
self._alert.response_accept = kwargs.get('result')
self._alert.response_text = kwargs['userInput']
self._has_alert = False
def _on_alert_open(self, **kwargs):
"""alert出现时触发的方法"""
self._alert.activated = True
self._alert.text = kwargs['message']
self._alert.type = kwargs['message']
self._alert.defaultPrompt = kwargs.get('defaultPrompt', None)
self._alert.response_accept = None
self._alert.response_text = None
self._has_alert = True
def _d_connect(self, to_url, times=0, interval=1, show_errmsg=False, timeout=None):
"""尝试连接,重试若干次
:param to_url: 要访问的url
@ -1166,3 +1217,15 @@ class ScreencastMode(object):
def imgs_mode(self):
self._screencast._mode = 'imgs'
class Alert(object):
"""用于保存alert信息的类"""
def __init__(self):
self.activated = False
self.text = None
self.type = None
self.defaultPrompt = None
self.response_accept = None
self.response_text = None

View File

@ -47,10 +47,12 @@ class ChromiumBase(BasePage):
self._screencast: Screencast = ...
self._actions: ActionChains = ...
self._listener: NetworkListener = ...
self._alert: Alert = ...
self._has_alert: bool = ...
def _connect_browser(self, tab_id: str = None) -> None: ...
def _driver_init(self, tab_id: str, is_init:bool=True) -> None: ...
def _driver_init(self, tab_id: str, is_init: bool = True) -> None: ...
def _get_document(self) -> None: ...
@ -156,6 +158,9 @@ class ChromiumBase(BasePage):
@property
def listen(self) -> NetworkListener: ...
@property
def has_alert(self) -> bool: ...
def run_js(self, script: str, *args: Any, as_expr: bool = False) -> Any: ...
def run_js_loaded(self, script: str, *args: Any, as_expr: bool = False) -> Any: ...
@ -226,6 +231,12 @@ class ChromiumBase(BasePage):
cache: bool = True,
cookies: bool = True) -> None: ...
def handle_alert(self, accept: bool = True, send: str = None, timeout: float = None) -> Union[str, False]: ...
def _on_alert_close(self, **kwargs): ...
def _on_alert_open(self, **kwargs): ...
def _d_connect(self,
to_url: str,
times: int = 0,
@ -286,3 +297,14 @@ class ScreencastMode(object):
def frugal_imgs_mode(self) -> None: ...
def imgs_mode(self) -> None: ...
class Alert(object):
def __init__(self):
self.activated: bool = ...
self.text: str = ...
self.type: str = ...
self.defaultPrompt: str = ...
self.response_accept: str = ...
self.response_text: str = ...

View File

@ -4,7 +4,7 @@
@Contact : g1879@qq.com
"""
from pathlib import Path
from time import perf_counter, sleep
from time import sleep
from requests import get
@ -85,13 +85,8 @@ class ChromiumPage(ChromiumBase):
def _page_init(self):
"""浏览器相关设置"""
self._alert = Alert()
self._driver.set_listener('Page.javascriptDialogOpening', self._on_alert_open)
self._driver.set_listener('Page.javascriptDialogClosed', self._on_alert_close)
self._rect = None
self._main_tab = self.tab_id
self._browser.connect_to_page()
@property
@ -284,64 +279,10 @@ class ChromiumPage(ChromiumBase):
"""
self.close_tabs(tabs_or_ids, True)
def handle_alert(self, accept=True, send=None, timeout=None):
"""处理提示框,可以自动等待提示框出现
:param accept: True表示确认False表示取消其它值不会按按钮但依然返回文本值
:param send: 处理prompt提示框时可输入文本
:param timeout: 等待提示框出现的超时时间为None则使用self.timeout属性的值
:return: 提示框内容文本未等到提示框则返回False
"""
timeout = self.timeout if timeout is None else timeout
timeout = .1 if timeout <= 0 else timeout
end_time = perf_counter() + timeout
while not self._alert.activated and perf_counter() < end_time:
sleep(.1)
if not self._alert.activated:
return False
res_text = self._alert.text
if self._alert.type == 'prompt':
self.driver.call_method('Page.handleJavaScriptDialog', accept=accept, promptText=send)
else:
self.driver.call_method('Page.handleJavaScriptDialog', accept=accept)
return res_text
def quit(self):
"""关闭浏览器"""
self.browser.quit()
def _on_alert_close(self, **kwargs):
"""alert关闭时触发的方法"""
self._alert.activated = False
self._alert.text = None
self._alert.type = None
self._alert.defaultPrompt = None
self._alert.response_accept = kwargs.get('result')
self._alert.response_text = kwargs['userInput']
self._driver.has_alert = False
def _on_alert_open(self, **kwargs):
"""alert出现时触发的方法"""
self._alert.activated = True
self._alert.text = kwargs['message']
self._alert.type = kwargs['message']
self._alert.defaultPrompt = kwargs.get('defaultPrompt', None)
self._alert.response_accept = None
self._alert.response_text = None
self._driver.has_alert = True
class Alert(object):
"""用于保存alert信息的类"""
def __init__(self):
self.activated = False
self.text = None
self.type = None
self.defaultPrompt = None
self.response_accept = None
self.response_text = None
def get_rename(original, rename):
if '.' in rename:

View File

@ -23,7 +23,6 @@ class ChromiumPage(ChromiumBase):
timeout: float = None):
self._driver_options: ChromiumOptions = ...
self._main_tab: str = ...
self._alert: Alert = ...
self._browser: Browser = ...
self._rect: Optional[ChromiumTabRect] = ...
@ -83,24 +82,7 @@ class ChromiumPage(ChromiumBase):
def close_other_tabs(self, tabs_or_ids: Union[
str, ChromiumTab, List[Union[str, ChromiumTab]], Tuple[Union[str, ChromiumTab]]] = None) -> None: ...
def handle_alert(self, accept: bool = True, send: str = None, timeout: float = None) -> Union[str, False]: ...
def quit(self) -> None: ...
def _on_alert_close(self, **kwargs): ...
def _on_alert_open(self, **kwargs): ...
class Alert(object):
def __init__(self):
self.activated: bool = ...
self.text: str = ...
self.type: str = ...
self.defaultPrompt: str = ...
self.response_accept: str = ...
self.response_text: str = ...
def get_rename(original: str, rename: str) -> str: ...

View File

@ -30,9 +30,9 @@ python 版本3.6 及以上
# 🛠 如何使用
**📖 使用文档:** [点击查看](http://g1879.gitee.io/drissionpagedocs)
**📖 使用文档:** [点击查看](https://g1879.gitee.io/drissionpagedocs)
**交流 QQ 群:** 897838127[已满]、558778073
**交流 QQ 群:** 897838127[已满]、558778073[已满]、636361957
---