diff --git a/DrissionPage/common.py b/DrissionPage/common.py index d477d46..4a5de9b 100644 --- a/DrissionPage/common.py +++ b/DrissionPage/common.py @@ -131,16 +131,27 @@ def get_loc_from_str(loc: str) -> tuple: def _make_xpath_str(tag: str, arg: str, val: str, mode: str = 'fuzzy') -> str: - """生成xpath语句""" + """生成xpath语句 \n + :param tag: 标签名 + :param arg: 属性名 + :param val: 属性值 + :param mode: 'exact' 或 'fuzzy',对应精确或模糊查找 + :return: xpath字符串 + """ tag_name = '' if tag == '*' else f'name()="{tag}" and ' if mode == 'exact': return f'//*[{tag_name}{arg}={_make_search_str(val)}]' - else: + elif mode == 'fuzzy': return f"//*[{tag_name}contains({arg},{_make_search_str(val)})]" + else: + raise ValueError("Argument mode can only be 'exact' or 'fuzzy'.") def _make_search_str(search_str: str) -> str: - """将"转义,不知何故不能直接用\"""" + """将"转义,不知何故不能直接用\ \n + :param search_str: 查询字符串 + :return: 把"转义后的字符串 + """ parts = search_str.split('"') parts_num = len(parts) search_str = 'concat(' @@ -151,8 +162,11 @@ def _make_search_str(search_str: str) -> str: return search_str -def translate_loc_to_xpath(loc) -> tuple: - """把By类型转为xpath或css selector""" +def translate_loc_to_xpath(loc: tuple) -> tuple: + """把By类型的loc元组转换为css selector或xpath类型的 \n + :param loc: By类型的loc元组 + :return: css selector或xpath类型的loc元组 + """ loc_by = 'xpath' loc_str = None if loc[0] == 'xpath': diff --git a/DrissionPage/config.py b/DrissionPage/config.py index 887930f..f36212f 100644 --- a/DrissionPage/config.py +++ b/DrissionPage/config.py @@ -17,7 +17,7 @@ class OptionsManager(object): """管理配置文件内容的类""" def __init__(self, path: str = None): - """初始化,读取配置文件,如没有设置临时文件夹,则设置并新建 + """初始化,读取配置文件,如没有设置临时文件夹,则设置并新建 \n :param path: ini文件的路径,默认读取模块文件夹下的 """ self.path = path or Path(__file__).parent / 'configs.ini' @@ -30,7 +30,11 @@ class OptionsManager(object): self.save() def get_value(self, section: str, item: str) -> Any: - """获取配置的值""" + """获取配置的值 \n + :param section: 段名 + :param item: 项名 + :return: 项值 + """ try: return eval(self._conf.get(section, item)) except SyntaxError: @@ -39,7 +43,10 @@ class OptionsManager(object): return None def get_option(self, section: str) -> dict: - """把section内容以字典方式返回""" + """把section内容以字典方式返回 \n + :param section: 段名 + :return: 段内容生成的字典 + """ items = self._conf.items(section) option = dict() for j in items: @@ -50,14 +57,19 @@ class OptionsManager(object): return option def set_item(self, section: str, item: str, value: Any): - """设置配置值""" + """设置配置值 \n + :param section: 段名 + :param item: 项名 + :param value: 项值 + :return: 当前对象 + """ self._conf.set(section, item, str(value)) return self def save(self, path: str = None): - """保存配置文件 + """保存配置文件 \n :param path: ini文件的路径,默认保存到模块文件夹下的 - :return: None + :return: 当前对象 """ path = path or Path(__file__).parent / 'configs.ini' self._conf.write(open(path, 'w', encoding='utf-8')) @@ -66,7 +78,9 @@ class OptionsManager(object): class DriverOptions(Options): def __init__(self, read_file=True): - """初始化,默认从文件读取设置""" + """初始化,默认从文件读取设置 \n + :param read_file: 是否从默认ini文件中读取配置信息 + """ super().__init__() self._driver_path = None if read_file: @@ -89,9 +103,9 @@ class DriverOptions(Options): return self.binary_location def save(self, path: str = None): - """保存设置到文件 + """保存设置到文件 \n :param path: ini文件的路径,默认保存到模块文件夹下的 - :return: None + :return: 当前对象 """ om = OptionsManager() options = _chrome_options_to_dict(self) @@ -104,9 +118,9 @@ class DriverOptions(Options): return self def remove_argument(self, value: str): - """移除一个设置 + """移除一个argument项 \n :param value: 设置项名,有值的设置项传入设置名称即可 - :return: None + :return: 当前对象 """ del_list = [] for argument in self._arguments: @@ -117,22 +131,27 @@ class DriverOptions(Options): return self def remove_experimental_option(self, key: str): - """移除一个实验设置,传入key值删除""" + """移除一个实验设置,传入key值删除 \n + :param key: 实验设置的名称 + :return: 当前对象 + """ if key in self._experimental_options: self._experimental_options.pop(key) return self def remove_all_extensions(self): - """移除所有插件 - 因插件是以整个文件储存,难以移除其中一个,故如须设置则全部移除再重设""" + """移除所有插件 \n + 因插件是以整个文件储存,难以移除其中一个,故如须设置则全部移除再重设 + :return: 当前对象 + """ self._extensions = [] return self def set_argument(self, arg: str, value: Union[bool, str]): - """设置浏览器配置argument属性 + """设置浏览器配置的argument属性 \n :param arg: 属性名 :param value: 属性值,有值的属性传入值,没有的传入bool - :return: None + :return: 当前对象 """ self.remove_argument(arg) if value: @@ -141,27 +160,45 @@ class DriverOptions(Options): return self def set_headless(self, on_off: bool = True): - """设置headless""" + """设置是否隐藏浏览器界面 \n + :param on_off: 开或关 + :return: 当前对象 + """ return self.set_argument('--headless', on_off) def set_no_imgs(self, on_off: bool = True): - """设置是否加载图片""" + """设置是否加载图片 \n + :param on_off: 开或关 + :return: 当前对象 + """ return self.set_argument('--blink-settings=imagesEnabled=false', on_off) def set_no_js(self, on_off: bool = True): - """设置禁用js""" + """设置是否禁用js \n + :param on_off: 开或关 + :return: 当前对象 + """ return self.set_argument('--disable-javascript', on_off) def set_mute(self, on_off: bool = True): - """设置静音""" + """设置是否静音 \n + :param on_off: 开或关 + :return: 当前对象 + """ return self.set_argument('--mute-audio', on_off) def set_user_agent(self, user_agent: str): - """设置user agent""" + """设置user agent \n + :param user_agent: user agent文本 + :return: 当前对象 + """ return self.set_argument('user-agent', user_agent) def set_proxy(self, proxy: str): - """设置代理""" + """设置代理 \n + :param proxy: 代理url和端口 + :return: 当前对象 + """ return self.set_argument('--proxy-server', proxy) def set_paths(self, @@ -171,14 +208,14 @@ class DriverOptions(Options): download_path: str = None, user_data_path: str = None, cache_path: str = None): - """简易设置路径函数 + """快捷的路径设置函数 \n :param driver_path: chromedriver.exe路径 :param chrome_path: chrome.exe路径 :param debugger_address: 调试浏览器地址,例:127.0.0.1:9222 :param download_path: 下载文件路径 :param user_data_path: 用户数据路径 :param cache_path: 缓存路径 - :return: None + :return: 当前对象 """ def format_path(path: str) -> str: @@ -200,7 +237,10 @@ class DriverOptions(Options): def _dict_to_chrome_options(options: dict) -> Options: - """从传入的字典获取浏览器设置,返回ChromeOptions对象""" + """从传入的字典获取浏览器设置,返回ChromeOptions对象 \n + :param options: 配置信息字典 + :return: 保存浏览器配置的ChromeOptions对象 + """ chrome_options = webdriver.ChromeOptions() if 'debugger_address' in options and options['debugger_address']: # 控制已打开的浏览器 @@ -238,6 +278,10 @@ def _dict_to_chrome_options(options: dict) -> Options: def _chrome_options_to_dict(options: Union[dict, DriverOptions, None]) -> Union[dict, None]: + """把chrome配置对象转换为字典 \n + :param options: chrome配置对象,字典或DriverOptions对象 + :return: 配置字典 + """ if options is None or isinstance(options, dict): return options diff --git a/DrissionPage/drission.py b/DrissionPage/drission.py index 779d017..b4631f5 100644 --- a/DrissionPage/drission.py +++ b/DrissionPage/drission.py @@ -26,7 +26,7 @@ class Drission(object): session_options: dict = None, ini_path: str = None, proxy: dict = None): - """初始化配置信息,但不生成session和driver实例 + """初始化配置信息,但不生成session和driver实例 \n :param driver_options: chrome设置,Options类或设置字典 :param session_options: session设置 :param ini_path: ini文件路径' @@ -52,7 +52,7 @@ class Drission(object): @property def session(self) -> HTMLSession: - """获取HTMLSession对象""" + """返回HTMLSession对象,如为None则按配置信息创建""" if self._session is None: self._session = HTMLSession() attrs = ['headers', 'cookies', 'auth', 'proxies', 'hooks', 'params', 'verify', @@ -67,7 +67,7 @@ class Drission(object): @property def driver(self) -> WebDriver: - """获取WebDriver对象,按传入配置信息初始化""" + """返回WebDriver对象,如为None则按配置信息创建""" if self._driver is None: if isinstance(self._driver_options, dict): options = _dict_to_chrome_options(self._driver_options) @@ -92,22 +92,33 @@ class Drission(object): @property def driver_options(self) -> dict: + """返回driver配置信息""" return self._driver_options @property def session_options(self) -> dict: + """返回session配置信息""" return self._session_options @session_options.setter - def session_options(self, value: dict): + def session_options(self, value: dict) -> None: + """设置session配置 + :param value: session配置字典 + :return: None + """ self._session_options = value @property def proxy(self) -> Union[None, dict]: + """返回代理信息""" return self._proxy @proxy.setter - def proxy(self, proxies: dict = None): + def proxy(self, proxies: dict = None) -> None: + """设置代理信息 \n + :param proxies: 代理信息字典 + :return: None + """ self._proxy = proxies if self._session: self._session.proxies = proxies @@ -121,9 +132,15 @@ class Drission(object): for cookie in cookies: self._ensure_add_cookie(cookie) - def cookies_to_session(self, copy_user_agent: bool = False, driver: WebDriver = None, session: Session = None) \ - -> None: - """把driver的cookies复制到session""" + def cookies_to_session(self, copy_user_agent: bool = False, + driver: WebDriver = None, + session: Session = None) -> None: + """把driver对象的cookies复制到session对象 \n + :param copy_user_agent: 是否复制ua信息 + :param driver: 来源driver对象 + :param session: 目标session对象 + :return: None + """ driver = driver or self.driver session = session or self.session if copy_user_agent: @@ -131,8 +148,15 @@ class Drission(object): for cookie in driver.get_cookies(): session.cookies.set(cookie['name'], cookie['value'], domain=cookie['domain']) - def cookies_to_driver(self, url: str, driver: WebDriver = None, session: Session = None) -> None: - """把session的cookies复制到driver""" + def cookies_to_driver(self, url: str, + driver: WebDriver = None, + session: Session = None) -> None: + """把session对象的cookies复制到driver对象 \n + :param url: 作用域 + :param driver: 目标driver对象 + :param session: 来源session对象 + :return: None + """ driver = driver or self.driver session = session or self.session domain = urlparse(url).netloc @@ -146,7 +170,12 @@ class Drission(object): self._ensure_add_cookie(cookie_data, driver=driver) def _ensure_add_cookie(self, cookie, override_domain=None, driver=None) -> None: - """添加cookie到driver""" + """添加cookie到driver \n + :param cookie: 要添加的cookie + :param override_domain: 覆盖作用域 + :param driver: 操作的driver对象 + :return: None + """ driver = driver or self.driver if override_domain: cookie['domain'] = override_domain @@ -171,8 +200,12 @@ class Drission(object): raise WebDriverException(f"Couldn't add the following cookie to the webdriver\n{cookie}\n") def _is_cookie_in_driver(self, cookie, driver=None) -> bool: - """检查cookie是否已经在driver里 - 只检查name、value、domain,检查domain时比较宽""" + """检查cookie是否已经在driver里 \n + 只检查name、value、domain,检查domain时比较宽 \n + :param cookie: 要检查的cookie + :param driver: 被检查的driver + :return: 返回布尔值 + """ driver = driver or self.driver for driver_cookie in driver.get_cookies(): if (cookie['name'] == driver_cookie['name'] and @@ -183,7 +216,11 @@ class Drission(object): return False def user_agent_to_session(self, driver: WebDriver = None, session: Session = None) -> None: - """把driver的user-agent复制到session""" + """把driver的user-agent复制到session \n + :param driver: 来源driver对象 + :param session: 目标session对象 + :return: None + """ driver = driver or self.driver session = session or self.session selenium_user_agent = driver.execute_script("return navigator.userAgent;") diff --git a/DrissionPage/driver_element.py b/DrissionPage/driver_element.py index dd4692e..6fe7d50 100644 --- a/DrissionPage/driver_element.py +++ b/DrissionPage/driver_element.py @@ -181,7 +181,7 @@ class DriverElement(DrissionElement): """返回当前元素下级符合条件的子元素,默认返回第一个 \n 示例: \n - 用loc元组查找: \n - ele.ele((By.CLASS_NAME, 'ele_class')) - 返回所有class为ele_class的子元素 \n + ele.ele((By.CLASS_NAME, 'ele_class')) - 返回第一个class为ele_class的子元素 \n - 用查询字符串查找: \n 查找方式:属性、tag name和属性、文本、xpath、css selector \n 其中,@表示属性,=表示精确匹配,:表示模糊匹配,无控制字符串时默认搜索该字符串 \n @@ -251,7 +251,7 @@ class DriverElement(DrissionElement): :param show_errmsg: 出现异常时是否打印信息 :return: DriverElement对象组成的列表 """ - if not isinstance(loc_or_str, tuple) and not isinstance(loc_or_str, str): + if not isinstance(loc_or_str, (tuple, str)): raise TypeError('Type of loc_or_str can only be tuple or str.') return self.ele(loc_or_str, mode='all', show_errmsg=show_errmsg, timeout=timeout) @@ -405,7 +405,7 @@ class DriverElement(DrissionElement): :return: 是否拖拽成功 """ # x, y:目标坐标点 - if isinstance(ele_or_loc, DriverElement) or isinstance(ele_or_loc, WebElement): + if isinstance(ele_or_loc, (DriverElement, WebElement)): target_x = ele_or_loc.location['x'] + ele_or_loc.size['width'] // 2 target_y = ele_or_loc.location['y'] + ele_or_loc.size['height'] // 2 elif isinstance(ele_or_loc, tuple): diff --git a/DrissionPage/easy_set.py b/DrissionPage/easy_set.py index 1dd25cb..c24a385 100644 --- a/DrissionPage/easy_set.py +++ b/DrissionPage/easy_set.py @@ -19,7 +19,7 @@ def set_paths(driver_path: str = None, user_data_path: str = None, cache_path: str = None, check_version: bool = True) -> None: - """简易设置路径函数 + """快捷的路径设置函数 \n :param driver_path: chromedriver.exe路径 :param chrome_path: chrome.exe路径 :param debugger_address: 调试浏览器地址,例:127.0.0.1:9222 @@ -57,10 +57,10 @@ def set_paths(driver_path: str = None, def set_argument(arg: str, value: Union[bool, str]) -> None: - """设置浏览器配置argument属性 + """设置浏览器配置argument属性 \n :param arg: 属性名 :param value: 属性值,有值的属性传入值,没有的传入bool - :return: + :return: None """ do = DriverOptions() do.remove_argument(arg) @@ -71,37 +71,59 @@ def set_argument(arg: str, value: Union[bool, str]) -> None: def set_headless(on_off: bool = True) -> None: - """设置headless""" + """设置是否隐藏浏览器界面 \n + :param on_off: 开或关 + :return: None + """ set_argument('--headless', on_off) def set_no_imgs(on_off: bool = True) -> None: - """设置是否加载图片""" + """设置是否禁止加载图片 \n + :param on_off: 开或关 + :return: None + """ set_argument('--blink-settings=imagesEnabled=false', on_off) def set_no_js(on_off: bool = True) -> None: - """设置禁用js""" + """设置是否禁用js \n + :param on_off: 开或关 + :return: None + """ set_argument('--disable-javascript', on_off) def set_mute(on_off: bool = True) -> None: - """设置静音""" + """设置是否静音 \n + :param on_off: 开或关 + :return: None + """ set_argument('--mute-audio', on_off) def set_user_agent(user_agent: str) -> None: - """设置user agent""" + """设置user agent \n + :param user_agent: user agent文本 + :return: None + """ set_argument('user-agent', user_agent) def set_proxy(proxy: str) -> None: - """设置代理""" + """设置代理 \n + :param proxy: 代理网址和端口 + :return: None + """ set_argument('--proxy-server', proxy) def check_driver_version(driver_path: str = None, chrome_path: str = None) -> bool: - """检查传入的chrome和chromedriver是否匹配""" + """检查传入的chrome和chromedriver是否匹配 \n + :param driver_path: chromedriver.exe路径 + :param chrome_path: chrome.exe路径 + :return: 是否匹配 + """ print('正在检测可用性...') om = OptionsManager() driver_path = driver_path or om.get_value('paths', 'chromedriver_path') or 'chromedriver' diff --git a/DrissionPage/mix_page.py b/DrissionPage/mix_page.py index e11bd7c..d728f89 100644 --- a/DrissionPage/mix_page.py +++ b/DrissionPage/mix_page.py @@ -236,7 +236,7 @@ class MixPage(Null, SessionPage, DriverPage): - 接收到元素对象时: \n 返回元素对象对象 \n - 用loc元组查找: \n - ele.ele((By.CLASS_NAME, 'ele_class')) - 返回所有class为ele_class的子元素 \n + ele.ele((By.CLASS_NAME, 'ele_class')) - 返回第一个class为ele_class的子元素 \n - 用查询字符串查找: \n 查找方式:属性、tag name和属性、文本、xpath、css selector \n 其中,@表示属性,=表示精确匹配,:表示模糊匹配,无控制字符串时默认搜索该字符串 \n diff --git a/DrissionPage/session_element.py b/DrissionPage/session_element.py index 76a9c05..e6fc25f 100644 --- a/DrissionPage/session_element.py +++ b/DrissionPage/session_element.py @@ -117,7 +117,7 @@ class SessionElement(DrissionElement): """返回当前元素下级符合条件的子元素,默认返回第一个 \n 示例: \n - 用loc元组查找: \n - ele.ele((By.CLASS_NAME, 'ele_class')) - 返回所有class为ele_class的子元素 \n + ele.ele((By.CLASS_NAME, 'ele_class')) - 返回第一个class为ele_class的子元素 \n - 用查询字符串查找: \n 查找方式:属性、tag name和属性、文本、xpath、css selector \n 其中,@表示属性,=表示精确匹配,:表示模糊匹配,无控制字符串时默认搜索该字符串 \n diff --git a/DrissionPage/session_page.py b/DrissionPage/session_page.py index f801741..5a28bb0 100644 --- a/DrissionPage/session_page.py +++ b/DrissionPage/session_page.py @@ -133,7 +133,7 @@ class SessionPage(object): :param show_errmsg: 出现异常时是否打印信息 :return: SessionElement对象组成的列表 """ - if not isinstance(loc_or_str, tuple) and not isinstance(loc_or_str, str): + if not isinstance(loc_or_str, (tuple, str)): raise TypeError('Type of loc_or_str can only be tuple or str.') return self.ele(loc_or_str, mode='all', show_errmsg=True)