246 lines
9.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# -*- coding:utf-8 -*-
"""
@Author : g1879
@Contact : g1879@qq.com
@File : mix_page.py
"""
from typing import Union, List
from urllib import parse
from requests import Response
from requests_html import HTMLSession
from selenium.webdriver.chrome.webdriver import WebDriver
from .drission import Drission
from .driver_element import DriverElement
from .driver_page import DriverPage
from .session_element import SessionElement
from .session_page import SessionPage
class Null(object):
"""避免IDE警告未调用超类初始化函数而引入的无用类"""
def __init__(self):
pass
class MixPage(Null, SessionPage, DriverPage):
"""MixPage封装了页面操作的常用功能可在seleniumd模式和requestss模式间无缝切换。
切换的时候会自动同步cookies兼顾selenium的易用性和requests的高性能。
获取信息功能为两种模式共有操作页面元素功能只有d模式有。调用某种模式独有的功能会自动切换到该模式。
这些功能由DriverPage和SessionPage类实现。
"""
def __init__(self, drission: Drission, mode='d', timeout: float = 10):
"""初始化函数
:param drission: 整合了driver和session的类
:param mode: 默认使用selenium的d模式
"""
super().__init__()
self._drission = drission
self._session = None
self._driver = None
self._url = None
self._response = None
self.timeout = timeout
self._url_available = None
self._mode = mode
if mode == 's':
self._session = self._drission.session
elif mode == 'd':
self._driver = self._drission.driver
else:
raise KeyError("mode must be 'd' or 's'.")
@property
def url(self) -> str:
"""根据模式获取当前活动的url"""
if self._mode == 'd':
return super(SessionPage, self).url
elif self._mode == 's':
return self.session_url
@property
def session_url(self) -> str:
return self._response.url if self._response else None
@property
def mode(self) -> str:
"""返回当前模式
:return: 's''d'
"""
return self._mode
def change_mode(self, mode: str = None, go: bool = True) -> None:
"""切换模式接收字符串s或d除此以外的字符串会切换为d模式
切换时会把当前模式的cookies复制到目标模式
切换后如果go是True调用相应的get函数使访问的页面同步
:param mode: 模式字符串
:param go: 是否跳转到原模式的url
"""
if mode == self._mode:
return
self._mode = 's' if self._mode == 'd' else 'd'
if self._mode == 'd': # s转d
self._url = super(SessionPage, self).url
if self.session_url:
self.cookies_to_driver(self.session_url)
if go:
self.get(self.session_url)
elif self._mode == 's': # d转s
self._url = self.session_url
if self._session is None:
self._session = self._drission.session
if self._driver:
self.cookies_to_session()
if go:
self.get(super(SessionPage, self).url)
@property
def drission(self) -> Drission:
"""返回当前使用的Dirssion对象"""
return self._drission
@property
def driver(self) -> WebDriver:
"""返回driver对象如没有则创建
每次访问时切换到d模式主要用于独有函数及外部调用
:return:selenium的WebDriver对象
"""
if self._driver is None:
self._driver = self._drission.driver
self.change_mode('d')
return self._driver
@property
def session(self) -> HTMLSession:
"""返回session对象如没有则创建
每次访问时切换到s模式主要用于独有函数及外部调用
:return:requests-html的HTMLSession对象
"""
if self._session is None:
self._session = self._drission.session
# self.change_mode('s')
return self._session
@property
def response(self) -> Response:
"""返回response对象切换到s模式"""
self.change_mode('s')
return self._response
@property
def cookies(self) -> Union[dict, list]:
"""返回cookies根据模式获取"""
if self._mode == 's':
return super().cookies
elif self._mode == 'd':
return super(SessionPage, self).cookies
def cookies_to_session(self, copy_user_agent: bool = False) -> None:
"""从driver复制cookies到session
:param copy_user_agent : 是否复制user agent信息
"""
self._drission.cookies_to_session(copy_user_agent)
def cookies_to_driver(self, url=None) -> None:
"""从session复制cookies到driverchrome需要指定域才能接收cookies"""
u = url or self.session_url
self._drission.cookies_to_driver(u)
# ----------------重写SessionPage的函数-----------------------
def post(self, url: str, params: dict = None, data: dict = None, go_anyway: bool = False, **kwargs) \
-> Union[bool, None]:
"""post前先转换模式但不跳转"""
self.change_mode('s', go=False)
return super().post(url, params, data, go_anyway, **kwargs)
# ----------------重写DriverPage的函数-----------------------
def chrome_downloading(self, download_path: str = None) -> list:
"""检查浏览器下载情况,返回正在下载的文件列表
:param download_path: 下载文件夹路径,默认读取配置信息
:return: 正在下载的文件列表
"""
try:
path = download_path or self._drission.driver_options['experimental_options']['prefs'][
'download.default_directory']
if not path:
raise KeyError
except KeyError:
raise KeyError('Download path not found.')
return super().chrome_downloading(path)
# ----------------以下为共用函数-----------------------
def get(self, url: str, params: dict = None, go_anyway=False, **kwargs) -> Union[bool, None]:
"""跳转到一个url跳转前先同步cookies跳转后判断目标url是否可用"""
to_url = f'{url}?{parse.urlencode(params)}' if params else url
if not url or (not go_anyway and self.url == to_url):
return
if self._mode == 'd':
super(SessionPage, self).get(url=to_url, go_anyway=go_anyway)
if self.session_url == self.url:
self._url_available = True if self._response and self._response.ok else False
else:
self._url_available = self.check_page()
return self._url_available
elif self._mode == 's':
super().get(url=to_url, go_anyway=go_anyway, **kwargs)
return self._url_available
def ele(self, loc_or_ele: Union[tuple, str, DriverElement, SessionElement], mode: str = None, timeout: float = None,
show_errmsg: bool = False) -> Union[DriverElement, SessionElement]:
"""查找一个元素,根据模式调用对应的查找函数
:param loc_or_ele: 页面元素地址
:param mode: 以某种方式查找元素,可选'single','all','visible'(d模式独有)
:param timeout: 超时时间
:param show_errmsg: 是否显示错误信息
:return: 页面元素对象s模式下返回Elementd模式下返回WebElement
"""
if self._mode == 's':
return super().ele(loc_or_ele, mode=mode, show_errmsg=show_errmsg)
elif self._mode == 'd':
timeout = timeout or self.timeout
# return super(SessionPage, self).ele(loc_or_ele, mode=mode, timeout=timeout, show_errmsg=show_errmsg)
return DriverPage.ele(self, loc_or_ele, mode=mode, timeout=timeout, show_errmsg=show_errmsg)
def eles(self, loc_or_str: Union[tuple, str], timeout: float = None, show_errmsg: bool = False) \
-> List[DriverElement]:
"""查找符合条件的所有元素"""
if self._mode == 's':
return super().eles(loc_or_str, show_errmsg)
elif self._mode == 'd':
return super(SessionPage, self).eles(loc_or_str, timeout=timeout, show_errmsg=show_errmsg)
@property
def html(self) -> str:
"""获取页面HTML"""
if self._mode == 's':
return super().html
elif self._mode == 'd':
return super(SessionPage, self).html
@property
def title(self) -> str:
"""获取页面title"""
if self._mode == 's':
return super().title
elif self._mode == 'd':
return super(SessionPage, self).title
def close_driver(self) -> None:
"""关闭driver及浏览器切换到s模式"""
self.change_mode('s')
self._driver = None
self.drission.close_driver()
def close_session(self) -> None:
"""关闭session切换到d模式"""
self.change_mode('d')
self._session = None
self.drission.close_session()