4.0.4.13(+)

动作链wait()增加随机功能;
latest_tab当tab设置为单例时返回Tab对象,否则返回tab id;
修复新标签页重复创建连接问题;
修复等待新tab不正确问题
This commit is contained in:
g1879 2024-03-17 01:20:06 +08:00
parent 4cbbf1dee1
commit cfed80e196
10 changed files with 74 additions and 65 deletions

View File

@ -14,4 +14,4 @@ from ._configs.chromium_options import ChromiumOptions
from ._configs.session_options import SessionOptions
__all__ = ['ChromiumPage', 'ChromiumOptions', 'SessionOptions', 'SessionPage', 'WebPage', '__version__']
__version__ = '4.0.4.12'
__version__ = '4.0.4.13'

View File

@ -72,7 +72,9 @@ class Browser(object):
:param owner: 使用该驱动的对象
:return: Driver对象
"""
d = self._drivers.pop(tab_id, Driver(tab_id, 'page', self.address))
d = self._drivers.pop(tab_id, None)
if not d:
d = Driver(tab_id, 'page', self.address)
d.owner = owner
self._all_drivers.setdefault(tab_id, set()).add(d)
return d
@ -188,6 +190,30 @@ class Browser(object):
"""
return self.run_cdp('Browser.getWindowForTarget', targetId=tab_id or self.id)['bounds']
def new_tab(self, new_window=False, background=False, new_context=False):
"""新建一个标签页
:param new_window: 是否在新窗口打开标签页
:param background: 是否不激活新标签页如new_window为True则无效
:param new_context: 是否创建新的上下文
:return: 新标签页id
"""
bid = None
if new_context:
bid = self.run_cdp('Target.createBrowserContext')['browserContextId']
kwargs = {'url': ''}
if new_window:
kwargs['newWindow'] = True
if background:
kwargs['background'] = True
if bid:
kwargs['browserContextId'] = bid
tid = self.run_cdp('Target.createTarget', **kwargs)['targetId']
while tid not in self._drivers:
sleep(.1)
return tid
def reconnect(self):
"""断开重连"""
self._driver.stop()

View File

@ -56,6 +56,8 @@ class Browser(object):
def get_window_bounds(self, tab_id: str = None) -> dict: ...
def new_tab(self, new_window: bool = False, background: bool = False, new_context: bool = False) -> str: ...
def reconnect(self) -> None: ...
def connect_to_page(self) -> None: ...

View File

@ -14,6 +14,7 @@ from requests import get
from .._base.browser import Browser
from .._configs.chromium_options import ChromiumOptions
from .._functions.browser import connect_browser
from .._functions.settings import Settings
from .._functions.tools import PortFinder
from .._pages.chromium_base import ChromiumBase, get_mhtml, get_pdf, Timeout
from .._pages.chromium_tab import ChromiumTab
@ -130,8 +131,9 @@ class ChromiumPage(ChromiumBase):
@property
def latest_tab(self):
"""返回最新的标签页对象,最新标签页指最后创建或最后被激活的"""
return self.get_tab(self.tab_ids[0])
"""返回最新的标签页,最新标签页指最后创建或最后被激活的
当Settings.singleton_tab_obj==True时返回Tab对象否则返回tab id"""
return self.get_tab(self.tab_ids[0], as_id=not Settings.singleton_tab_obj)
@property
def process_id(self):
@ -163,7 +165,12 @@ class ChromiumPage(ChromiumBase):
elif isinstance(id_or_num, int):
id_or_num = self.tab_ids[id_or_num - 1 if id_or_num > 0 else id_or_num]
elif isinstance(id_or_num, ChromiumTab):
return id_or_num.tab_id if as_id else id_or_num
if as_id:
return id_or_num.tab_id
elif Settings.singleton_tab_obj:
return id_or_num
else:
return self.get_tab(id_or_num.tab_id)
elif title == url == tab_type is None:
id_or_num = self.tab_id
@ -202,32 +209,11 @@ class ChromiumPage(ChromiumBase):
:param new_context: 是否创建新的上下文
:return: 新标签页对象
"""
tab = ChromiumTab(self, tab_id=self._new_tab(new_window, background, new_context))
tab = ChromiumTab(self, tab_id=self.browser.new_tab(new_window, background, new_context))
if url:
tab.get(url)
return tab
def _new_tab(self, new_window=False, background=False, new_context=False):
"""新建一个标签页
:param new_window: 是否在新窗口打开标签页
:param background: 是否不激活新标签页如new_window为True则无效
:param new_context: 是否创建新的上下文
:return: 新标签页对象
"""
bid = None
if new_context:
bid = self.browser.run_cdp('Target.createBrowserContext')['browserContextId']
kwargs = {'url': ''}
if new_window:
kwargs['newWindow'] = True
if background:
kwargs['background'] = True
if bid:
kwargs['browserContextId'] = bid
return self.browser.run_cdp('Target.createTarget', **kwargs)['targetId']
def close(self):
"""关闭Page管理的标签页"""
self.close_tabs(self.tab_id)

View File

@ -57,7 +57,7 @@ class ChromiumPage(ChromiumBase):
def wait(self) -> PageWaiter: ...
@property
def latest_tab(self) -> Union[ChromiumTab, ChromiumPage]: ...
def latest_tab(self) -> Union[ChromiumTab, ChromiumPage, str]: ...
@property
def process_id(self) -> Optional[int]: ...
@ -102,8 +102,6 @@ class ChromiumPage(ChromiumBase):
def new_tab(self, url: str = None, new_window: bool = False, background: bool = False,
new_context: bool = False) -> ChromiumTab: ...
def _new_tab(self, new_window: bool = False, background: bool = False, new_context: bool = False) -> str: ...
def close(self) -> None: ...
def close_tabs(self, tabs_or_ids: Union[str, ChromiumTab, List[Union[str, ChromiumTab]],

View File

@ -362,7 +362,7 @@ class WebPage(SessionPage, ChromiumPage, BasePage):
:param new_context: 是否创建新的上下文
:return: 新标签页对象
"""
tab = WebPageTab(self, tab_id=self._new_tab(new_window, background, new_context))
tab = WebPageTab(self, tab_id=self.browser.new_tab(new_window, background, new_context))
if url:
tab.get(url)
return tab

View File

@ -298,10 +298,13 @@ class Actions:
input_text_or_keys(self.owner, text)
return self
def wait(self, second):
"""等待若干秒"""
sleep(second)
return self
def wait(self, second, scope=None):
"""等待若干秒,如传入两个参数,等待时间为这两个数间的一个随机数
:param second: 秒数
:param scope: 随机数范围
:return: None
"""
self.owner.wait(second=second, scope=scope)
def _get_key_data(self, key, action):
"""获取用于发送的按键信息

View File

@ -100,7 +100,7 @@ class Actions:
def input(self, text: Any) -> Actions: ...
def wait(self, second: float) -> Actions: ...
def wait(self, second: float, scope: float = None) -> Actions: ...
def _get_key_data(self, key: str, action: str) -> dict: ...

View File

@ -11,13 +11,7 @@ from .._functions.settings import Settings
from ..errors import WaitTimeoutError, NoRectError
class BaseWaiter(object):
def __init__(self, page_or_ele):
"""
:param page_or_ele: 页面对象或元素对象
"""
self._driver = page_or_ele
class OriginWaiter(object):
def __call__(self, second, scope=None):
"""等待若干秒,如传入两个参数,等待时间为这两个数间的一个随机数
:param second: 秒数
@ -30,6 +24,14 @@ class BaseWaiter(object):
from random import uniform
sleep(uniform(second, scope))
class BaseWaiter(OriginWaiter):
def __init__(self, page_or_ele):
"""
:param page_or_ele: 页面对象或元素对象
"""
self._driver = page_or_ele
def ele_deleted(self, loc_or_ele, timeout=None, raise_err=None):
"""等待元素从DOM中删除
:param loc_or_ele: 要等待的元素可以是已有元素定位符
@ -125,7 +127,7 @@ class BaseWaiter(object):
:return: 成功返回任务对象失败返回False
"""
if not self._driver.browser._dl_mgr._running:
raise RuntimeError('使用下载管理功能前需显式设置下载路径使用set.download_path()方法、配置对象或ini文件均可')
raise RuntimeError('此功能需显式设置下载路径使用set.download_path()方法、配置对象或ini文件均可')
self._driver.browser._dl_mgr.set_flag(self._driver.tab_id, False if cancel_it else True)
if timeout is None:
timeout = self._driver.timeout
@ -240,7 +242,7 @@ class TabWaiter(BaseWaiter):
:return: 是否等待成功
"""
if not self._driver.browser._dl_mgr._running:
raise RuntimeError('使用下载管理功能前需显式设置下载路径使用set.download_path()方法、配置对象或ini文件均可')
raise RuntimeError('此功能需显式设置下载路径使用set.download_path()方法、配置对象或ini文件均可')
if not timeout:
while self._driver.browser._dl_mgr.get_tab_missions(self._driver.tab_id):
sleep(.5)
@ -282,9 +284,9 @@ class PageWaiter(TabWaiter):
timeout = timeout if timeout is not None else self._driver.timeout
end_time = perf_counter() + timeout
while perf_counter() < end_time:
latest_tab = self._driver.latest_tab
if self._driver.tab_id != latest_tab:
return latest_tab
latest_tid = self._driver.tab_ids[0]
if self._driver.tab_id != latest_tid:
return latest_tid
sleep(.01)
if raise_err is True or Settings.raise_when_wait_failed is True:
@ -299,7 +301,7 @@ class PageWaiter(TabWaiter):
:return: 是否等待成功
"""
if not self._driver.browser._dl_mgr._running:
raise RuntimeError('使用下载管理功能前需显式设置下载路径使用set.download_path()方法、配置对象或ini文件均可')
raise RuntimeError('此功能需显式设置下载路径使用set.download_path()方法、配置对象或ini文件均可')
if not timeout:
while self._driver.browser._dl_mgr._missions:
sleep(.5)
@ -321,7 +323,7 @@ class PageWaiter(TabWaiter):
return True
class ElementWaiter(object):
class ElementWaiter(OriginWaiter):
"""等待元素在dom中某种状态如删除、显示、隐藏"""
def __init__(self, owner, ele):
@ -332,18 +334,6 @@ class ElementWaiter(object):
self._owner = owner
self._ele = ele
def __call__(self, second, scope=None):
"""等待若干秒,如传入两个参数,等待时间为这两个数间的一个随机数
:param second: 秒数
:param scope: 随机数范围
:return: None
"""
if scope is None:
sleep(second)
else:
from random import uniform
sleep(uniform(second, scope))
def deleted(self, timeout=None, raise_err=None):
"""等待元素从dom删除
:param timeout: 超时时间为None使用元素所在页面timeout属性
@ -457,7 +447,7 @@ class ElementWaiter(object):
:param raise_err: 等待失败时是否报错为None时根据Settings设置
:return: 是否等待成功
"""
return self._wait_state('has_rect', True, timeout, raise_err, err_text='等待元素拥有大小及位置属性失败(等{}秒)。')
return self._wait_state('has_rect', True, timeout, raise_err, err_text='等待元素拥有大小及位置失败(等{}秒)。')
def _wait_state(self, attr, mode=False, timeout=None, raise_err=None, err_text=None):
"""等待元素某个元素状态到达指定状态

View File

@ -14,7 +14,11 @@ from .._pages.chromium_frame import ChromiumFrame
from .._pages.chromium_page import ChromiumPage
class BaseWaiter(object):
class OriginWaiter(object):
def __call__(self, second: float, scope: float = None) -> None: ...
class BaseWaiter(OriginWaiter):
def __init__(self, page: ChromiumBase):
self._driver: ChromiumBase = ...
@ -73,7 +77,7 @@ class PageWaiter(TabWaiter):
def all_downloads_done(self, timeout: float = None, cancel_if_timeout: bool = True) -> bool: ...
class ElementWaiter(object):
class ElementWaiter(OriginWaiter):
def __init__(self, owner: ChromiumBase, ele: ChromiumElement):
self._ele: ChromiumElement = ...
self._owner: ChromiumBase = ...