改进自动分配端口功能

This commit is contained in:
g1879 2023-01-24 14:30:36 +08:00
parent 77e35e64c6
commit d1a534204b
18 changed files with 195 additions and 121 deletions

View File

@ -1,4 +1,8 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
"""
@Author : g1879
@Contact : g1879@qq.com
"""
from functools import partial from functools import partial
from json import dumps, loads from json import dumps, loads
from queue import Queue, Empty from queue import Queue, Empty

View File

@ -1,4 +1,8 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
"""
@Author : g1879
@Contact : g1879@qq.com
"""
from queue import Queue from queue import Queue
from threading import Thread, Event from threading import Thread, Event
from typing import Union, Callable from typing import Union, Callable

View File

@ -1,14 +1,18 @@
# -*- coding:utf-8 -*- # -*- coding:utf-8 -*-
"""
@Author : g1879
@Contact : g1879@qq.com
"""
from pathlib import Path from pathlib import Path
from shutil import rmtree from tempfile import gettempdir, TemporaryDirectory
from DrissionPage.functions.tools import port_is_using from DrissionPage.functions.tools import port_is_using, clean_folder
from .options_manage import OptionsManager from .options_manage import OptionsManager
class ChromiumOptions(object): class ChromiumOptions(object):
def __init__(self, read_file=True, ini_path=None): def __init__(self, read_file=True, ini_path=None):
"""初始化,默认从文件读取设置 \n """
:param read_file: 是否从默认ini文件中读取配置信息 :param read_file: 是否从默认ini文件中读取配置信息
:param ini_path: ini文件路径为None则读取默认ini文件 :param ini_path: ini文件路径为None则读取默认ini文件
""" """
@ -17,7 +21,7 @@ class ChromiumOptions(object):
self._prefs_to_del = [] self._prefs_to_del = []
if read_file: if read_file:
self.ini_path = ini_path or str(Path(__file__).parent / 'configs.ini') self.ini_path = str(ini_path) if ini_path else str(Path(__file__).parent / 'configs.ini')
om = OptionsManager(self.ini_path) om = OptionsManager(self.ini_path)
options = om.chrome_options options = om.chrome_options
@ -45,6 +49,12 @@ class ChromiumOptions(object):
self._timeouts = {'implicit': timeouts['implicit'], self._timeouts = {'implicit': timeouts['implicit'],
'pageLoad': timeouts['page_load'], 'pageLoad': timeouts['page_load'],
'script': timeouts['script']} 'script': timeouts['script']}
self._auto_port = options.get('auto_port', False)
if self._auto_port:
port, path = PortFinder().get_port()
self._debugger_address = f'127.0.0.1:{port}'
self.set_paths(user_data_path=path)
return return
self.ini_path = None self.ini_path = None
@ -57,6 +67,7 @@ class ChromiumOptions(object):
self._debugger_address = '127.0.0.1:9222' self._debugger_address = '127.0.0.1:9222'
self._page_load_strategy = 'normal' self._page_load_strategy = 'normal'
self._proxy = None self._proxy = None
self._auto_port = False
@property @property
def download_path(self): def download_path(self):
@ -100,6 +111,7 @@ class ChromiumOptions(object):
@debugger_address.setter @debugger_address.setter
def debugger_address(self, address): def debugger_address(self, address):
"""设置浏览器地址格式ip:port"""
self._debugger_address = address self._debugger_address = address
@property @property
@ -118,7 +130,7 @@ class ChromiumOptions(object):
return self._prefs return self._prefs
def set_argument(self, arg, value=None): def set_argument(self, arg, value=None):
"""设置浏览器配置的argument属性 \n """设置浏览器配置的argument属性
:param arg: 属性名 :param arg: 属性名
:param value: 属性值有值的属性传入值没有的传入None如传入False删除该项 :param value: 属性值有值的属性传入值没有的传入None如传入False删除该项
:return: 当前对象 :return: 当前对象
@ -130,7 +142,7 @@ class ChromiumOptions(object):
return self return self
def remove_argument(self, value): def remove_argument(self, value):
"""移除一个argument项 \n """移除一个argument项
:param value: 设置项名有值的设置项传入设置名称即可 :param value: 设置项名有值的设置项传入设置名称即可
:return: 当前对象 :return: 当前对象
""" """
@ -146,7 +158,7 @@ class ChromiumOptions(object):
return self return self
def add_extension(self, path): def add_extension(self, path):
"""添加插件 \n """添加插件
:param path: 插件路径可指向文件夹 :param path: 插件路径可指向文件夹
:return: 当前对象 :return: 当前对象
""" """
@ -157,7 +169,7 @@ class ChromiumOptions(object):
return self return self
def remove_extensions(self): def remove_extensions(self):
"""移除所有插件 \n """移除所有插件
:return: 当前对象 :return: 当前对象
""" """
self._extensions = [] self._extensions = []
@ -173,7 +185,7 @@ class ChromiumOptions(object):
return self return self
def remove_pref(self, arg): def remove_pref(self, arg):
"""删除用户首选项设置,不能删除已设置到文件中的项 \n """删除用户首选项设置,不能删除已设置到文件中的项
:param arg: 设置项名称 :param arg: 设置项名称
:return: 当前对象 :return: 当前对象
""" """
@ -181,7 +193,7 @@ class ChromiumOptions(object):
return self return self
def remove_pref_from_file(self, arg): def remove_pref_from_file(self, arg):
"""删除用户配置文件中已设置的项 \n """删除用户配置文件中已设置的项
:param arg: 设置项名称 :param arg: 设置项名称
:return: 当前对象 :return: 当前对象
""" """
@ -189,7 +201,7 @@ class ChromiumOptions(object):
return self return self
def set_timeouts(self, implicit=None, pageLoad=None, script=None): def set_timeouts(self, implicit=None, pageLoad=None, script=None):
"""设置超时时间,单位为秒 \n """设置超时时间,单位为秒
:param implicit: 默认超时时间 :param implicit: 默认超时时间
:param pageLoad: 页面加载超时时间 :param pageLoad: 页面加载超时时间
:param script: 脚本运行超时时间 :param script: 脚本运行超时时间
@ -205,7 +217,7 @@ class ChromiumOptions(object):
return self return self
def set_user(self, user='Default'): def set_user(self, user='Default'):
"""设置使用哪个用户配置文件夹 \n """设置使用哪个用户配置文件夹
:param user: 用户文件夹名称 :param user: 用户文件夹名称
:return: 当前对象 :return: 当前对象
""" """
@ -214,7 +226,7 @@ class ChromiumOptions(object):
return self return self
def set_headless(self, on_off=True): def set_headless(self, on_off=True):
"""设置是否隐藏浏览器界面 \n """设置是否隐藏浏览器界面
:param on_off: 开或关 :param on_off: 开或关
:return: 当前对象 :return: 当前对象
""" """
@ -222,7 +234,7 @@ class ChromiumOptions(object):
return self.set_argument('--headless', on_off) return self.set_argument('--headless', on_off)
def set_no_imgs(self, on_off=True): def set_no_imgs(self, on_off=True):
"""设置是否加载图片 \n """设置是否加载图片
:param on_off: 开或关 :param on_off: 开或关
:return: 当前对象 :return: 当前对象
""" """
@ -230,7 +242,7 @@ class ChromiumOptions(object):
return self.set_argument('--blink-settings=imagesEnabled=false', on_off) return self.set_argument('--blink-settings=imagesEnabled=false', on_off)
def set_no_js(self, on_off=True): def set_no_js(self, on_off=True):
"""设置是否禁用js \n """设置是否禁用js
:param on_off: 开或关 :param on_off: 开或关
:return: 当前对象 :return: 当前对象
""" """
@ -238,7 +250,7 @@ class ChromiumOptions(object):
return self.set_argument('--disable-javascript', on_off) return self.set_argument('--disable-javascript', on_off)
def set_mute(self, on_off=True): def set_mute(self, on_off=True):
"""设置是否静音 \n """设置是否静音
:param on_off: 开或关 :param on_off: 开或关
:return: 当前对象 :return: 当前对象
""" """
@ -246,14 +258,14 @@ class ChromiumOptions(object):
return self.set_argument('--mute-audio', on_off) return self.set_argument('--mute-audio', on_off)
def set_user_agent(self, user_agent): def set_user_agent(self, user_agent):
"""设置user agent \n """设置user agent
:param user_agent: user agent文本 :param user_agent: user agent文本
:return: 当前对象 :return: 当前对象
""" """
return self.set_argument('--user-agent', user_agent) return self.set_argument('--user-agent', user_agent)
def set_proxy(self, proxy): def set_proxy(self, proxy):
"""设置代理 \n """设置代理
:param proxy: 代理url和端口 :param proxy: 代理url和端口
:return: 当前对象 :return: 当前对象
""" """
@ -261,7 +273,7 @@ class ChromiumOptions(object):
return self.set_argument('--proxy-server', proxy) return self.set_argument('--proxy-server', proxy)
def set_page_load_strategy(self, value): def set_page_load_strategy(self, value):
"""设置page_load_strategy可接收 'normal', 'eager', 'none' \n """设置page_load_strategy可接收 'normal', 'eager', 'none'
selenium4以上版本才支持此功能 selenium4以上版本才支持此功能
normal默认情况下使用, 等待所有资源下载完成 normal默认情况下使用, 等待所有资源下载完成
eagerDOM访问已准备就绪, 但其他资源 (如图像) 可能仍在加载中 eagerDOM访问已准备就绪, 但其他资源 (如图像) 可能仍在加载中
@ -276,7 +288,7 @@ class ChromiumOptions(object):
def set_paths(self, browser_path=None, local_port=None, debugger_address=None, download_path=None, def set_paths(self, browser_path=None, local_port=None, debugger_address=None, download_path=None,
user_data_path=None, cache_path=None): user_data_path=None, cache_path=None):
"""快捷的路径设置函数 \n """快捷的路径设置函数
:param browser_path: 浏览器可执行文件路径 :param browser_path: 浏览器可执行文件路径
:param local_port: 本地端口号 :param local_port: 本地端口号
:param debugger_address: 调试浏览器地址127.0.0.1:9222 :param debugger_address: 调试浏览器地址127.0.0.1:9222
@ -307,18 +319,22 @@ class ChromiumOptions(object):
return self return self
def auto_port(self, data_path=None): def auto_port(self, on_off=True):
"""自动获取可用端口 \n """自动获取可用端口
:param data_path: 用户文件夹保存路径为None则保存在当前路径 :param on_off: 是否开启自动获取端口号
:return: 当前对象 :return: 当前对象
""" """
data_path = data_path or '' if on_off:
port, path = PortFinder().get_port(data_path) port, path = PortFinder().get_port()
self.set_paths(local_port=port, user_data_path=path) self.set_paths(local_port=port, user_data_path=path)
self._auto_port = True
else:
self._auto_port = False
self._debugger_address = '127.0.0.1:9222'
return self return self
def save(self, path=None): def save(self, path=None):
"""保存设置到文件 \n """保存设置到文件
:param path: ini文件的路径 None 保存到当前读取的配置文件传入 'default' 保存到默认ini文件 :param path: ini文件的路径 None 保存到当前读取的配置文件传入 'default' 保存到默认ini文件
:return: 保存文件的绝对路径 :return: 保存文件的绝对路径
""" """
@ -342,7 +358,8 @@ class ChromiumOptions(object):
om = OptionsManager(self.ini_path or str(Path(__file__).parent / 'configs.ini')) om = OptionsManager(self.ini_path or str(Path(__file__).parent / 'configs.ini'))
# 设置chrome_options # 设置chrome_options
attrs = ('debugger_address', 'binary_location', 'arguments', 'extensions', 'user', 'page_load_strategy') attrs = ('debugger_address', 'binary_location', 'arguments', 'extensions', 'user', 'page_load_strategy',
'auto_port')
for i in attrs: for i in attrs:
om.set_item('chrome_options', i, self.__getattribute__(f'_{i}')) om.set_item('chrome_options', i, self.__getattribute__(f'_{i}'))
# 设置代理 # 设置代理
@ -372,26 +389,22 @@ class ChromiumOptions(object):
class PortFinder(object): class PortFinder(object):
used_port = [] used_port = []
@staticmethod def __init__(self):
def get_port(path): self.tmp_dir = Path(gettempdir()) / 'DrissionPageTempFolder'
"""查找一个可用端口 \n self.tmp_dir.mkdir(parents=True, exist_ok=True)
:param path: 用户文件夹保存路径 if not PortFinder.used_port:
clean_folder(self.tmp_dir)
def get_port(self):
"""查找一个可用端口
:return: 可以使用的端口和用户文件夹路径组成的元组 :return: 可以使用的端口和用户文件夹路径组成的元组
""" """
path = Path(path)
for i in range(9600, 9800): for i in range(9600, 9800):
if i in PortFinder.used_port or port_is_using('127.0.0.1', i): if i in PortFinder.used_port or port_is_using('127.0.0.1', i):
continue continue
path = path / f'userData{i}' path = TemporaryDirectory(dir=self.tmp_dir)
if not path.exists(): PortFinder.used_port.append(i)
PortFinder.used_port.append(i) return i, path.name
return i, str(path)
try:
rmtree(path)
return i, str(path)
except PermissionError:
continue
raise OSError('未找到可用端口。') raise OSError('未找到可用端口。')

View File

@ -1,10 +1,14 @@
# -*- coding:utf-8 -*- # -*- coding:utf-8 -*-
"""
@Author : g1879
@Contact : g1879@qq.com
"""
from pathlib import Path from pathlib import Path
from typing import Union, Tuple, Any from typing import Union, Tuple, Any
class ChromiumOptions(object): class ChromiumOptions(object):
def __init__(self, read_file: bool = True, ini_path: str = None): def __init__(self, read_file: bool = True, ini_path: Union[str, Path] = None):
self.ini_path: str = ... self.ini_path: str = ...
self._driver_path: str = ... self._driver_path: str = ...
self._user_data_path: str = ... self._user_data_path: str = ...
@ -19,6 +23,7 @@ class ChromiumOptions(object):
self._extensions: list = ... self._extensions: list = ...
self._prefs: dict = ... self._prefs: dict = ...
self._prefs_to_del: list = ... self._prefs_to_del: list = ...
self._auto_port: bool = ...
@property @property
def download_path(self) -> str: ... def download_path(self) -> str: ...
@ -93,7 +98,7 @@ class ChromiumOptions(object):
debugger_address: str = None, download_path: Union[str, Path] = None, debugger_address: str = None, download_path: Union[str, Path] = None,
user_data_path: Union[str, Path] = None, cache_path: Union[str, Path] = None) -> ChromiumOptions: ... user_data_path: Union[str, Path] = None, cache_path: Union[str, Path] = None) -> ChromiumOptions: ...
def auto_port(self, data_path: Union[str, Path]=None) -> ChromiumOptions: ... def auto_port(self, on_off: bool = True) -> ChromiumOptions: ...
def save(self, path: Union[str, Path] = None) -> str: ... def save(self, path: Union[str, Path] = None) -> str: ...
@ -104,4 +109,4 @@ class PortFinder(object):
used_port: list = ... used_port: list = ...
@staticmethod @staticmethod
def get_port(path: Union[str, Path]) -> Tuple[int, str]: ... def get_port() -> Tuple[int, str]: ...

View File

@ -10,6 +10,7 @@ extensions = []
experimental_options = {'prefs': {'profile.default_content_settings.popups': 0, 'profile.default_content_setting_values': {'notifications': 2}, 'plugins.plugins_list': [{'enabled': False, 'name': 'Chrome PDF Viewer'}]}, 'useAutomationExtension': False, 'excludeSwitches': ['enable-automation']} experimental_options = {'prefs': {'profile.default_content_settings.popups': 0, 'profile.default_content_setting_values': {'notifications': 2}, 'plugins.plugins_list': [{'enabled': False, 'name': 'Chrome PDF Viewer'}]}, 'useAutomationExtension': False, 'excludeSwitches': ['enable-automation']}
page_load_strategy = normal page_load_strategy = normal
user = Default user = Default
auto_port = True
[session_options] [session_options]
headers = {'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/603.3.8 (KHTML, like Gecko) Version/10.1.2 Safari/603.3.8', 'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 'connection': 'keep-alive', 'accept-charset': 'GB2312,utf-8;q=0.7,*;q=0.7'} headers = {'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/603.3.8 (KHTML, like Gecko) Version/10.1.2 Safari/603.3.8', 'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 'connection': 'keep-alive', 'accept-charset': 'GB2312,utf-8;q=0.7,*;q=0.7'}

View File

@ -1,4 +1,8 @@
# -*- coding:utf-8 -*- # -*- coding:utf-8 -*-
"""
@Author : g1879
@Contact : g1879@qq.com
"""
from pathlib import Path from pathlib import Path
from selenium.webdriver.chrome.options import Options from selenium.webdriver.chrome.options import Options
@ -12,7 +16,7 @@ class DriverOptions(Options):
""" """
def __init__(self, read_file=True, ini_path=None): def __init__(self, read_file=True, ini_path=None):
"""初始化,默认从文件读取设置 \n """初始化,默认从文件读取设置
:param read_file: 是否从默认ini文件中读取配置信息 :param read_file: 是否从默认ini文件中读取配置信息
:param ini_path: ini文件路径为None则读取默认ini文件 :param ini_path: ini文件路径为None则读取默认ini文件
""" """
@ -20,7 +24,7 @@ class DriverOptions(Options):
self._user_data_path = None self._user_data_path = None
if read_file: if read_file:
self.ini_path = ini_path or str(Path(__file__).parent / 'configs.ini') self.ini_path = str(ini_path) if ini_path else str(Path(__file__).parent / 'configs.ini')
om = OptionsManager(self.ini_path) om = OptionsManager(self.ini_path)
options_dict = om.chrome_options options_dict = om.chrome_options
@ -74,7 +78,7 @@ class DriverOptions(Options):
# -------------重写父类方法,实现链式操作------------- # -------------重写父类方法,实现链式操作-------------
def add_argument(self, argument): def add_argument(self, argument):
"""添加一个配置项 \n """添加一个配置项
:param argument: 配置项内容 :param argument: 配置项内容
:return: 当前对象 :return: 当前对象
""" """
@ -82,7 +86,7 @@ class DriverOptions(Options):
return self return self
def set_capability(self, name, value): def set_capability(self, name, value):
"""设置一个capability \n """设置一个capability
:param name: capability名称 :param name: capability名称
:param value: capability值 :param value: capability值
:return: 当前对象 :return: 当前对象
@ -91,7 +95,7 @@ class DriverOptions(Options):
return self return self
def add_extension(self, extension): def add_extension(self, extension):
"""添加插件 \n """添加插件
:param extension: crx文件路径 :param extension: crx文件路径
:return: 当前对象 :return: 当前对象
""" """
@ -99,7 +103,7 @@ class DriverOptions(Options):
return self return self
def add_encoded_extension(self, extension): def add_encoded_extension(self, extension):
"""将带有扩展数据的 Base64 编码字符串添加到将用于将其提取到 ChromeDriver 的列表中 \n """将带有扩展数据的 Base64 编码字符串添加到将用于将其提取到 ChromeDriver 的列表中
:param extension: 带有扩展数据的 Base64 编码字符串 :param extension: 带有扩展数据的 Base64 编码字符串
:return: 当前对象 :return: 当前对象
""" """
@ -107,7 +111,7 @@ class DriverOptions(Options):
return self return self
def add_experimental_option(self, name, value): def add_experimental_option(self, name, value):
"""添加一个实验选项到浏览器 \n """添加一个实验选项到浏览器
:param name: 选项名称 :param name: 选项名称
:param value: 选项值 :param value: 选项值
:return: 当前对象 :return: 当前对象
@ -118,7 +122,7 @@ class DriverOptions(Options):
# -------------重写父类方法结束------------- # -------------重写父类方法结束-------------
def save(self, path=None): def save(self, path=None):
"""保存设置到文件 \n """保存设置到文件
:param path: ini文件的路径 None 保存到当前读取的配置文件传入 'default' 保存到默认ini文件 :param path: ini文件的路径 None 保存到当前读取的配置文件传入 'default' 保存到默认ini文件
:return: 保存文件的绝对路径 :return: 保存文件的绝对路径
""" """
@ -161,7 +165,7 @@ class DriverOptions(Options):
return self.save('default') return self.save('default')
def remove_argument(self, value): def remove_argument(self, value):
"""移除一个argument项 \n """移除一个argument项
:param value: 设置项名有值的设置项传入设置名称即可 :param value: 设置项名有值的设置项传入设置名称即可
:return: 当前对象 :return: 当前对象
""" """
@ -177,7 +181,7 @@ class DriverOptions(Options):
return self return self
def remove_experimental_option(self, key): def remove_experimental_option(self, key):
"""移除一个实验设置传入key值删除 \n """移除一个实验设置传入key值删除
:param key: 实验设置的名称 :param key: 实验设置的名称
:return: 当前对象 :return: 当前对象
""" """
@ -187,7 +191,7 @@ class DriverOptions(Options):
return self return self
def remove_all_extensions(self): def remove_all_extensions(self):
"""移除所有插件 \n """移除所有插件
:return: 当前对象 :return: 当前对象
""" """
# 因插件是以整个文件储存,难以移除其中一个,故如须设置则全部移除再重设 # 因插件是以整个文件储存,难以移除其中一个,故如须设置则全部移除再重设
@ -195,7 +199,7 @@ class DriverOptions(Options):
return self return self
def set_argument(self, arg, value): def set_argument(self, arg, value):
"""设置浏览器配置的argument属性 \n """设置浏览器配置的argument属性
:param arg: 属性名 :param arg: 属性名
:param value: 属性值有值的属性传入值没有的传入bool :param value: 属性值有值的属性传入值没有的传入bool
:return: 当前对象 :return: 当前对象
@ -209,7 +213,7 @@ class DriverOptions(Options):
return self return self
def set_timeouts(self, implicit=None, pageLoad=None, script=None): def set_timeouts(self, implicit=None, pageLoad=None, script=None):
"""设置超时时间设置单位为秒selenium4以上版本有效 \n """设置超时时间设置单位为秒selenium4以上版本有效
:param implicit: 查找元素超时时间 :param implicit: 查找元素超时时间
:param pageLoad: 页面加载超时时间 :param pageLoad: 页面加载超时时间
:param script: 脚本运行超时时间 :param script: 脚本运行超时时间
@ -225,7 +229,7 @@ class DriverOptions(Options):
return self return self
def set_headless(self, on_off=True): def set_headless(self, on_off=True):
"""设置是否隐藏浏览器界面 \n """设置是否隐藏浏览器界面
:param on_off: 开或关 :param on_off: 开或关
:return: 当前对象 :return: 当前对象
""" """
@ -233,7 +237,7 @@ class DriverOptions(Options):
return self.set_argument('--headless', on_off) return self.set_argument('--headless', on_off)
def set_no_imgs(self, on_off=True): def set_no_imgs(self, on_off=True):
"""设置是否加载图片 \n """设置是否加载图片
:param on_off: 开或关 :param on_off: 开或关
:return: 当前对象 :return: 当前对象
""" """
@ -241,7 +245,7 @@ class DriverOptions(Options):
return self.set_argument('--blink-settings=imagesEnabled=false', on_off) return self.set_argument('--blink-settings=imagesEnabled=false', on_off)
def set_no_js(self, on_off=True): def set_no_js(self, on_off=True):
"""设置是否禁用js \n """设置是否禁用js
:param on_off: 开或关 :param on_off: 开或关
:return: 当前对象 :return: 当前对象
""" """
@ -249,7 +253,7 @@ class DriverOptions(Options):
return self.set_argument('--disable-javascript', on_off) return self.set_argument('--disable-javascript', on_off)
def set_mute(self, on_off=True): def set_mute(self, on_off=True):
"""设置是否静音 \n """设置是否静音
:param on_off: 开或关 :param on_off: 开或关
:return: 当前对象 :return: 当前对象
""" """
@ -257,21 +261,21 @@ class DriverOptions(Options):
return self.set_argument('--mute-audio', on_off) return self.set_argument('--mute-audio', on_off)
def set_user_agent(self, user_agent): def set_user_agent(self, user_agent):
"""设置user agent \n """设置user agent
:param user_agent: user agent文本 :param user_agent: user agent文本
:return: 当前对象 :return: 当前对象
""" """
return self.set_argument('--user-agent', user_agent) return self.set_argument('--user-agent', user_agent)
def set_proxy(self, proxy): def set_proxy(self, proxy):
"""设置代理 \n """设置代理
:param proxy: 代理url和端口 :param proxy: 代理url和端口
:return: 当前对象 :return: 当前对象
""" """
return self.set_argument('--proxy-server', proxy) return self.set_argument('--proxy-server', proxy)
def set_page_load_strategy(self, value): def set_page_load_strategy(self, value):
"""设置page_load_strategy可接收 'normal', 'eager', 'none' \n """设置page_load_strategy可接收 'normal', 'eager', 'none'
selenium4以上版本才支持此功能 selenium4以上版本才支持此功能
normal默认情况下使用, 等待所有资源下载完成 normal默认情况下使用, 等待所有资源下载完成
eagerDOM访问已准备就绪, 但其他资源 (如图像) 可能仍在加载中 eagerDOM访问已准备就绪, 但其他资源 (如图像) 可能仍在加载中
@ -286,7 +290,7 @@ class DriverOptions(Options):
def set_paths(self, driver_path=None, chrome_path=None, browser_path=None, local_port=None, def set_paths(self, driver_path=None, chrome_path=None, browser_path=None, local_port=None,
debugger_address=None, download_path=None, user_data_path=None, cache_path=None): debugger_address=None, download_path=None, user_data_path=None, cache_path=None):
"""快捷的路径设置函数 \n """快捷的路径设置函数
:param driver_path: chromedriver.exe路径 :param driver_path: chromedriver.exe路径
:param chrome_path: chrome.exe路径 :param chrome_path: chrome.exe路径
:param browser_path: 浏览器可执行文件路径 :param browser_path: 浏览器可执行文件路径
@ -330,7 +334,7 @@ class DriverOptions(Options):
def chrome_options_to_dict(options): def chrome_options_to_dict(options):
"""把chrome配置对象转换为字典 \n """把chrome配置对象转换为字典
:param options: chrome配置对象字典或DriverOptions对象 :param options: chrome配置对象字典或DriverOptions对象
:return: 配置字典 :return: 配置字典
""" """

View File

@ -1,4 +1,8 @@
# -*- coding:utf-8 -*- # -*- coding:utf-8 -*-
"""
@Author : g1879
@Contact : g1879@qq.com
"""
from pathlib import Path from pathlib import Path
from typing import Union, List from typing import Union, List
@ -7,7 +11,7 @@ from selenium.webdriver.chrome.options import Options
class DriverOptions(Options): class DriverOptions(Options):
def __init__(self, read_file: bool = True, ini_path: str = None): def __init__(self, read_file: bool = True, ini_path: Union[str, Path] = None):
self.ini_path: str = ... self.ini_path: str = ...
self._driver_path: str = ... self._driver_path: str = ...
self._user_data_path: str = ... self._user_data_path: str = ...

View File

@ -1,5 +1,8 @@
# -*- coding:utf-8 -*- # -*- coding:utf-8 -*-
"""
@Author : g1879
@Contact : g1879@qq.com
"""
from configparser import RawConfigParser, NoSectionError, NoOptionError from configparser import RawConfigParser, NoSectionError, NoOptionError
from pathlib import Path from pathlib import Path
from pprint import pprint from pprint import pprint
@ -9,10 +12,10 @@ class OptionsManager(object):
"""管理配置文件内容的类""" """管理配置文件内容的类"""
def __init__(self, path=None): def __init__(self, path=None):
"""初始化,读取配置文件,如没有设置临时文件夹,则设置并新建 \n """初始化,读取配置文件,如没有设置临时文件夹,则设置并新建
:param path: ini文件的路径默认读取模块文件夹下的 :param path: ini文件的路径默认读取模块文件夹下的
""" """
self.ini_path = str(Path(__file__).parent / 'configs.ini') if path == 'default' or path is None else path self.ini_path = str(Path(__file__).parent / 'configs.ini') if path == 'default' or path is None else str(path)
if not Path(self.ini_path).exists(): if not Path(self.ini_path).exists():
raise FileNotFoundError('ini文件不存在。') raise FileNotFoundError('ini文件不存在。')
self._conf = RawConfigParser() self._conf = RawConfigParser()
@ -26,7 +29,7 @@ class OptionsManager(object):
return self.get_option(item) return self.get_option(item)
def get_value(self, section, item): def get_value(self, section, item):
"""获取配置的值 \n """获取配置的值
:param section: 段名 :param section: 段名
:param item: 项名 :param item: 项名
:return: 项值 :return: 项值
@ -39,7 +42,7 @@ class OptionsManager(object):
return None return None
def get_option(self, section): def get_option(self, section):
"""把section内容以字典方式返回 \n """把section内容以字典方式返回
:param section: 段名 :param section: 段名
:return: 段内容生成的字典 :return: 段内容生成的字典
""" """
@ -55,7 +58,7 @@ class OptionsManager(object):
return option return option
def set_item(self, section, item, value): def set_item(self, section, item, value):
"""设置配置值 \n """设置配置值
:param section: 段名 :param section: 段名
:param item: 项名 :param item: 项名
:param value: 项值 :param value: 项值
@ -66,7 +69,7 @@ class OptionsManager(object):
return self return self
def remove_item(self, section, item): def remove_item(self, section, item):
"""删除配置值 \n """删除配置值
:param section: 段名 :param section: 段名
:param item: 项名 :param item: 项名
:return: None :return: None
@ -75,7 +78,7 @@ class OptionsManager(object):
return self return self
def save(self, path=None): def save(self, path=None):
"""保存配置文件 \n """保存配置文件
:param path: ini文件的路径传入 'default' 保存到默认ini文件 :param path: ini文件的路径传入 'default' 保存到默认ini文件
:return: 保存路径 :return: 保存路径
""" """

View File

@ -1,5 +1,8 @@
# -*- coding:utf-8 -*- # -*- coding:utf-8 -*-
"""
@Author : g1879
@Contact : g1879@qq.com
"""
from configparser import RawConfigParser from configparser import RawConfigParser
from typing import Any from typing import Any

View File

@ -1,5 +1,8 @@
# -*- coding:utf-8 -*- # -*- coding:utf-8 -*-
"""
@Author : g1879
@Contact : g1879@qq.com
"""
from pathlib import Path from pathlib import Path
from DrissionPage.functions.web import cookies_to_tuple from DrissionPage.functions.web import cookies_to_tuple
@ -33,7 +36,7 @@ class SessionOptions(object):
self._del_set = set() # 记录要从ini文件删除的参数 self._del_set = set() # 记录要从ini文件删除的参数
if read_file: if read_file:
self.ini_path = ini_path or str(Path(__file__).parent / 'configs.ini') self.ini_path = str(ini_path) if ini_path else str(Path(__file__).parent / 'configs.ini')
om = OptionsManager(self.ini_path) om = OptionsManager(self.ini_path)
options_dict = om.session_options options_dict = om.session_options

View File

@ -1,4 +1,8 @@
# -*- coding:utf-8 -*- # -*- coding:utf-8 -*-
"""
@Author : g1879
@Contact : g1879@qq.com
"""
from pathlib import Path from pathlib import Path
from typing import Any, Union, Tuple from typing import Any, Union, Tuple
@ -8,7 +12,7 @@ from requests.cookies import RequestsCookieJar
class SessionOptions(object): class SessionOptions(object):
def __init__(self, read_file: bool = True, ini_path: str = None): def __init__(self, read_file: bool = True, ini_path: Union[str, Path] = None):
self.ini_path: str = ... self.ini_path: str = ...
self._download_path: str = ... self._download_path: str = ...
self._headers: dict = ... self._headers: dict = ...
@ -108,4 +112,5 @@ class SessionOptions(object):
def as_dict(self) -> dict: ... def as_dict(self) -> dict: ...
def session_options_to_dict(options: Union[dict, SessionOptions, None]) -> Union[dict, None]: ... def session_options_to_dict(options: Union[dict, SessionOptions, None]) -> Union[dict, None]: ...

View File

@ -52,7 +52,7 @@ def set_paths(driver_path=None,
om = OptionsManager(ini_path) om = OptionsManager(ini_path)
def format_path(path: str) -> str: def format_path(path: str) -> str:
return path or '' return str(path) if path else ''
if driver_path is not None: if driver_path is not None:
om.set_item('paths', 'chromedriver_path', format_path(driver_path)) om.set_item('paths', 'chromedriver_path', format_path(driver_path))
@ -64,10 +64,10 @@ def set_paths(driver_path=None,
om.set_item('chrome_options', 'binary_location', format_path(browser_path)) om.set_item('chrome_options', 'binary_location', format_path(browser_path))
if local_port is not None: if local_port is not None:
om.set_item('chrome_options', 'debugger_address', format_path(f'127.0.0.1:{local_port}')) om.set_item('chrome_options', 'debugger_address', f'127.0.0.1:{local_port}')
if debugger_address is not None: if debugger_address is not None:
om.set_item('chrome_options', 'debugger_address', format_path(debugger_address)) om.set_item('chrome_options', 'debugger_address', debugger_address)
if download_path is not None: if download_path is not None:
om.set_item('paths', 'download_path', format_path(download_path)) om.set_item('paths', 'download_path', format_path(download_path))
@ -84,6 +84,19 @@ def set_paths(driver_path=None,
check_driver_version(format_path(driver_path), format_path(browser_path)) check_driver_version(format_path(driver_path), format_path(browser_path))
def use_auto_port(on_off=True, ini_path=None):
"""设置启动浏览器时使用自动分配的端口和临时文件夹
:param on_off: 是否开启自动端口
:param ini_path: 要修改的ini文件路径
:return: None
"""
if not isinstance(on_off, bool):
raise TypeError('on_off参数只能输入bool值。')
om = OptionsManager(ini_path)
om.set_item('chrome_options', 'auto_port', on_off)
om.save()
def set_argument(arg, value=None, ini_path=None): def set_argument(arg, value=None, ini_path=None):
"""设置浏览器配置argument属性 """设置浏览器配置argument属性
:param arg: 属性名 :param arg: 属性名
@ -91,9 +104,9 @@ def set_argument(arg, value=None, ini_path=None):
:param ini_path: 要修改的ini文件路径 :param ini_path: 要修改的ini文件路径
:return: None :return: None
""" """
do = ChromiumOptions(ini_path=ini_path) co = ChromiumOptions(ini_path=ini_path)
do.set_argument(arg, value) co.set_argument(arg, value)
do.save() co.save()
def set_headless(on_off=True, ini_path=None): def set_headless(on_off=True, ini_path=None):

View File

@ -3,46 +3,50 @@
@Author : g1879 @Author : g1879
@Contact : g1879@qq.com @Contact : g1879@qq.com
""" """
from pathlib import Path
from typing import Union from typing import Union
def show_settings(ini_path: str = None) -> None: ... def show_settings(ini_path: Union[str, Path] = None) -> None: ...
def set_paths(driver_path: str = None, def set_paths(driver_path: Union[str, Path] = None,
chrome_path: str = None, chrome_path: Union[str, Path] = None,
browser_path: str = None, browser_path: Union[str, Path] = None,
local_port: Union[int, str] = None, local_port: Union[int, str] = None,
debugger_address: str = None, debugger_address: str = None,
download_path: str = None, download_path: Union[str, Path] = None,
user_data_path: str = None, user_data_path: Union[str, Path] = None,
cache_path: str = None, cache_path: Union[str, Path] = None,
ini_path: str = None, ini_path: Union[str, Path] = None,
check_version: bool = False) -> None: ... check_version: bool = False) -> None: ...
def set_argument(arg: str, value: Union[bool, str] = None, ini_path: str = None) -> None: ... def use_auto_port(on_off: bool = True, ini_path: Union[str, Path] = None) -> None: ...
def set_headless(on_off: bool = True, ini_path: str = None) -> None: ... def set_argument(arg: str, value: Union[bool, str] = None, ini_path: Union[str, Path] = None) -> None: ...
def set_no_imgs(on_off: bool = True, ini_path: str = None) -> None: ... def set_headless(on_off: bool = True, ini_path: Union[str, Path] = None) -> None: ...
def set_no_js(on_off: bool = True, ini_path: str = None) -> None: ... def set_no_imgs(on_off: bool = True, ini_path: Union[str, Path] = None) -> None: ...
def set_mute(on_off: bool = True, ini_path: str = None) -> None: ... def set_no_js(on_off: bool = True, ini_path: Union[str, Path] = None) -> None: ...
def set_user_agent(user_agent: str, ini_path: str = None) -> None: ... def set_mute(on_off: bool = True, ini_path: Union[str, Path] = None) -> None: ...
def set_proxy(proxy: str, ini_path: str = None) -> None: ... def set_user_agent(user_agent: str, ini_path: Union[str, Path] = None) -> None: ...
def check_driver_version(driver_path: str = None, chrome_path: str = None) -> bool: ... def set_proxy(proxy: str, ini_path: Union[str, Path] = None) -> None: ...
def check_driver_version(driver_path: Union[str, Path] = None, chrome_path: str = None) -> bool: ...
# -------------------------自动识别chrome版本号并下载对应driver------------------------ # -------------------------自动识别chrome版本号并下载对应driver------------------------

View File

@ -1,4 +1,8 @@
# -*- coding:utf-8 -*- # -*- coding:utf-8 -*-
"""
@Author : g1879
@Contact : g1879@qq.com
"""
from json import load, dump from json import load, dump
from pathlib import Path from pathlib import Path
from platform import system from platform import system
@ -12,7 +16,7 @@ from .tools import port_is_using, get_exe_from_port
def connect_browser(option): def connect_browser(option):
"""连接或启动浏览器 \n """连接或启动浏览器
:param option: DriverOptions对象 :param option: DriverOptions对象
:return: chrome 路径和进程对象组成的元组 :return: chrome 路径和进程对象组成的元组
""" """
@ -116,7 +120,7 @@ def set_prefs(opt):
def _run_browser(port, path: str, args) -> Popen: def _run_browser(port, path: str, args) -> Popen:
"""创建chrome进程 \n """创建chrome进程
:param port: 端口号 :param port: 端口号
:param path: 浏览器地址 :param path: 浏览器地址
:param args: 启动参数 :param args: 启动参数

View File

@ -1,4 +1,8 @@
# -*- coding:utf-8 -*- # -*- coding:utf-8 -*-
"""
@Author : g1879
@Contact : g1879@qq.com
"""
from typing import Union from typing import Union
from DrissionPage.configs.chromium_options import ChromiumOptions from DrissionPage.configs.chromium_options import ChromiumOptions

View File

@ -7,7 +7,7 @@ from re import split
def get_loc(loc, translate_css=False): def get_loc(loc, translate_css=False):
"""接收selenium定位元组或本库定位语法转换为标准定位元组可翻译css selector为xpath \n """接收selenium定位元组或本库定位语法转换为标准定位元组可翻译css selector为xpath
:param loc: selenium定位元组或本库定位语法 :param loc: selenium定位元组或本库定位语法
:param translate_css: 是否翻译css selector为xpath :param translate_css: 是否翻译css selector为xpath
:return: DrissionPage定位元组 :return: DrissionPage定位元组
@ -34,9 +34,9 @@ def get_loc(loc, translate_css=False):
def str_to_loc(loc): def str_to_loc(loc):
"""处理元素查找语句 \n """处理元素查找语句
查找方式属性tag name及属性文本xpathcss selectoridclass \n 查找方式属性tag name及属性文本xpathcss selectoridclass
@表示属性.表示class#表示id=表示精确匹配,:表示模糊匹配,无控制字符串时默认搜索该字符串 \n @表示属性.表示class#表示id=表示精确匹配,:表示模糊匹配,无控制字符串时默认搜索该字符串
""" """
loc_by = 'xpath' loc_by = 'xpath'
@ -108,7 +108,7 @@ def str_to_loc(loc):
def _make_single_xpath_str(tag: str, text: str) -> str: def _make_single_xpath_str(tag: str, text: str) -> str:
"""生成xpath语句 \n """生成xpath语句
:param tag: 标签名 :param tag: 标签名
:param text: 待处理的字符串 :param text: 待处理的字符串
:return: xpath字符串 :return: xpath字符串
@ -145,7 +145,7 @@ def _make_single_xpath_str(tag: str, text: str) -> str:
def _make_multi_xpath_str(tag: str, text: str) -> str: def _make_multi_xpath_str(tag: str, text: str) -> str:
"""生成多属性查找的xpath语句 \n """生成多属性查找的xpath语句
:param tag: 标签名 :param tag: 标签名
:param text: 待处理的字符串 :param text: 待处理的字符串
:return: xpath字符串 :return: xpath字符串
@ -185,7 +185,7 @@ def _make_multi_xpath_str(tag: str, text: str) -> str:
def _make_search_str(search_str: str) -> str: def _make_search_str(search_str: str) -> str:
""""转义,不知何故不能直接用 \ 来转义 \n """"转义,不知何故不能直接用 \ 来转义
:param search_str: 查询字符串 :param search_str: 查询字符串
:return: "转义后的字符串 :return: "转义后的字符串
""" """
@ -202,7 +202,7 @@ def _make_search_str(search_str: str) -> str:
def translate_loc(loc): def translate_loc(loc):
"""把By类型的loc元组转换为css selector或xpath类型的 \n """把By类型的loc元组转换为css selector或xpath类型的
:param loc: By类型的loc元组 :param loc: By类型的loc元组
:return: css selector或xpath类型的loc元组 :return: css selector或xpath类型的loc元组
""" """

View File

@ -10,7 +10,7 @@ from zipfile import ZipFile
def get_exe_from_port(port): def get_exe_from_port(port):
"""获取端口号第一条进程的可执行文件路径 \n """获取端口号第一条进程的可执行文件路径
:param port: 端口号 :param port: 端口号
:return: 可执行文件的绝对路径 :return: 可执行文件的绝对路径
""" """
@ -25,7 +25,7 @@ def get_exe_from_port(port):
def get_pid_from_port(port): def get_pid_from_port(port):
"""获取端口号第一条进程的pid \n """获取端口号第一条进程的pid
:param port: 端口号 :param port: 端口号
:return: 进程id :return: 进程id
""" """
@ -50,7 +50,7 @@ def get_pid_from_port(port):
def get_usable_path(path): def get_usable_path(path):
"""检查文件或文件夹是否有重名,并返回可以使用的路径 \n """检查文件或文件夹是否有重名,并返回可以使用的路径
:param path: 文件或文件夹路径 :param path: 文件或文件夹路径
:return: 可用的路径Path对象 :return: 可用的路径Path对象
""" """
@ -78,7 +78,7 @@ def get_usable_path(path):
def make_valid_name(full_name): def make_valid_name(full_name):
"""获取有效的文件名 \n """获取有效的文件名
:param full_name: 文件名 :param full_name: 文件名
:return: 可用的文件名 :return: 可用的文件名
""" """
@ -104,7 +104,7 @@ def make_valid_name(full_name):
def get_long(txt): def get_long(txt):
"""返回字符串中字符个数一个汉字是2个字符 \n """返回字符串中字符个数一个汉字是2个字符
:param txt: 字符串 :param txt: 字符串
:return: 字符个数 :return: 字符个数
""" """
@ -113,7 +113,7 @@ def get_long(txt):
def port_is_using(ip, port): def port_is_using(ip, port):
"""检查端口是否被占用 \n """检查端口是否被占用
:param ip: 浏览器地址 :param ip: 浏览器地址
:param port: 浏览器端口 :param port: 浏览器端口
:return: bool :return: bool
@ -126,7 +126,7 @@ def port_is_using(ip, port):
def clean_folder(folder_path, ignore=None): def clean_folder(folder_path, ignore=None):
"""清空一个文件夹除了ignore里的文件和文件夹 \n """清空一个文件夹除了ignore里的文件和文件夹
:param folder_path: 要清空的文件夹路径 :param folder_path: 要清空的文件夹路径
:param ignore: 忽略列表 :param ignore: 忽略列表
:return: None :return: None

View File

@ -25,7 +25,7 @@ def get_long(txt) -> int: ...
def port_is_using(ip: str, port: Union[str, int]) -> bool: ... def port_is_using(ip: str, port: Union[str, int]) -> bool: ...
def clean_folder(folder_path: str, ignore: list = None) -> None: ... def clean_folder(folder_path: Union[str, Path], ignore: list = None) -> None: ...
def unzip(zip_path: str, to_path: str) -> Union[list, None]: ... def unzip(zip_path: str, to_path: str) -> Union[list, None]: ...