This commit is contained in:
bwstudio
2025-10-18 22:26:14 +08:00
parent c31031eb8f
commit 8db81de42a
6 changed files with 495 additions and 510 deletions
+2 -2
View File
@@ -49,12 +49,12 @@ autoLogon/
```json ```json
{ {
"duration": 30,
"browser_path": "C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe", "browser_path": "C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe",
"sites": [ "sites": [
{ {
"name": "网站名称", "name": "网站名称",
"url": "网站URL", "url": "网站URL",
"duration": 30,
"cookies": [ "cookies": [
{ {
"name": "cookie名称", "name": "cookie名称",
@@ -105,7 +105,7 @@ python main.py
## 注意事项 ## 注意事项
- 确保浏览器路径正确配置,默认使用Edge浏览器 - 确保浏览器路径正确配置,默认使用Edge浏览器
- 配置文件中的`duration`参数表示网站打开后保持的时间(秒) - 配置文件中的`duration`参数表示处理每个网站时的延时时间(秒),默认30秒
- 首次运行时,应用会为没有cookies的网站创建空的cookies配置 - 首次运行时,应用会为没有cookies的网站创建空的cookies配置
- 日志文件保存在`log`目录下,按日期命名 - 日志文件保存在`log`目录下,按日期命名
+33 -173
View File
@@ -1,86 +1,14 @@
{ {
"browser_path": "C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe", "duration": 30,
"AutoQuit": true,
"sites": [ "sites": [
{ {
"name": "百度", "name": "百度",
"url": "https://www.baidu.com", "url": "https://www.baidu.com",
"duration": 30,
"cookies": [ "cookies": [
{
"name": "BD_HOME",
"value": "1",
"domain": "www.baidu.com",
"path": "/",
"expires": 0,
"secure": false,
"httpOnly": false
},
{
"name": "BA_HECTOR",
"value": "20252k858l240g858hag848g2kcg811kf6cpe25",
"domain": ".baidu.com",
"path": "/",
"expires": 0,
"secure": false,
"httpOnly": false
},
{
"name": "PAD_BROWSER",
"value": "1",
"domain": "www.baidu.com",
"path": "/",
"expires": 0,
"secure": false,
"httpOnly": false
},
{
"name": "BIDUPSID",
"value": "118F20D8D3C3135F00559E5F3510FBB5",
"domain": ".baidu.com",
"path": "/",
"expires": 0,
"secure": false,
"httpOnly": false
},
{
"name": "BD_UPN",
"value": "12314753",
"domain": "www.baidu.com",
"path": "/",
"expires": 0,
"secure": false,
"httpOnly": false
},
{
"name": "PSTM",
"value": "1760768814",
"domain": ".baidu.com",
"path": "/",
"expires": 0,
"secure": false,
"httpOnly": false
},
{
"name": "ZFY",
"value": "R:BWN9Jj8RMAPiaPxY4bTL7jFZJ:AvWzWoS2wj:BdtPdNI:C",
"domain": ".baidu.com",
"path": "/",
"expires": 0,
"secure": false,
"httpOnly": false
},
{
"name": "H_PS_PSSID",
"value": "60275_64980_65216_65249_65311_65422_65456_65571_65596_65619_65598_65635_65664_65665_65682_65711_65733_65392_65759_65774_65791_65838_65866",
"domain": ".baidu.com",
"path": "/",
"expires": 0,
"secure": false,
"httpOnly": false
},
{ {
"name": "BAIDUID_BFESS", "name": "BAIDUID_BFESS",
"value": "16F9B2349AAE73EDA898BA50E44AE46D:FG=1", "value": "07286BA0A030422D44083F3B4F2DAB27:FG=1",
"domain": ".baidu.com", "domain": ".baidu.com",
"path": "/", "path": "/",
"expires": 0, "expires": 0,
@@ -92,92 +20,19 @@
{ {
"name": "新浪", "name": "新浪",
"url": "https://www.sina.com.cn", "url": "https://www.sina.com.cn",
"duration": 30,
"cookies": [ "cookies": [
{
"name": "BAIDUID_BFESS",
"value": "07286BA0A030422D44083F3B4F2DAB27:FG=1",
"domain": ".baidu.com",
"path": "/",
"expires": 0,
"secure": false,
"httpOnly": false
},
{ {
"name": "Hm_lpvt_90c40f528e0b2106bc03da5aadec190f", "name": "Hm_lpvt_90c40f528e0b2106bc03da5aadec190f",
"value": "1760771854", "value": "1760797352",
"domain": ".www.sina.com.cn",
"path": "/",
"expires": 0,
"secure": false,
"httpOnly": false
},
{
"name": "NowDate",
"value": "Sat Oct 18 2025 15:17:33 GMT+0800 (中国标准时间)",
"domain": "www.sina.com.cn",
"path": "/",
"expires": 0,
"secure": false,
"httpOnly": false
},
{
"name": "name",
"value": "sinaAds",
"domain": "www.sina.com.cn",
"path": "/",
"expires": 0,
"secure": false,
"httpOnly": false
},
{
"name": "post",
"value": "massage",
"domain": "www.sina.com.cn",
"path": "/",
"expires": 0,
"secure": false,
"httpOnly": false
},
{
"name": "Apache",
"value": "120.227.58.197_1760769194.496218",
"domain": ".sina.com.cn",
"path": "/",
"expires": 0,
"secure": false,
"httpOnly": false
},
{
"name": "CLICK_ENHANCER_LAST_ACTIVATION",
"value": "1",
"domain": "www.sina.com.cn",
"path": "/",
"expires": 0,
"secure": false,
"httpOnly": false
},
{
"name": "ULV",
"value": "1760769329201:4:4:4:120.227.58.197_1760769194.496218:1760769193685",
"domain": ".sina.com.cn",
"path": "/",
"expires": 0,
"secure": false,
"httpOnly": false
},
{
"name": "UOR",
"value": "192.168.100.100,www.sina.com.cn,",
"domain": ".sina.com.cn",
"path": "/",
"expires": 0,
"secure": false,
"httpOnly": false
},
{
"name": "SINAGLOBAL",
"value": "120.227.58.197_1760768783.259381",
"domain": ".sina.com.cn",
"path": "/",
"expires": 0,
"secure": false,
"httpOnly": false
},
{
"name": "HMACCOUNT",
"value": "84FEB82BA2589EE5",
"domain": ".www.sina.com.cn", "domain": ".www.sina.com.cn",
"path": "/", "path": "/",
"expires": 0, "expires": 0,
@@ -186,32 +41,37 @@
}, },
{ {
"name": "Hm_lvt_90c40f528e0b2106bc03da5aadec190f", "name": "Hm_lvt_90c40f528e0b2106bc03da5aadec190f",
"value": "1760759893,1760768784,1760769195", "value": "1760794976,1760795033,1760795177,1760795343",
"domain": ".www.sina.com.cn", "domain": ".www.sina.com.cn",
"path": "/", "path": "/",
"expires": 0, "expires": 0,
"secure": false, "secure": false,
"httpOnly": false "httpOnly": false
},
{
"name": "SGUID",
"value": "1760768783361_32567590",
"domain": ".sina.com.cn",
"path": "/",
"expires": 0,
"secure": false,
"httpOnly": false
} }
] ]
}, },
{ {
"name": "示例网站2", "name": "示例网站5",
"url": "http://192.168.100.1/", "url": "http://192.168.100.100:3000/#/login",
"duration": 30, "cookies": [
{
"name": "MoviePilot",
"value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3NjA3OTkxNTAsImlhdCI6MTc2MDc5NzM1MCwic3ViIjoiMSIsInVzZXJuYW1lIjoiYWRtaW4iLCJzdXBlcl91c2VyIjp0cnVlLCJsZXZlbCI6MiwicHVycG9zZSI6InJlc291cmNlIn0.hrce7pqwIUTzuYMSe4QXM80tepOvv5dKkDfLL8OuQo4",
"domain": "192.168.100.100",
"path": "/",
"expires": 0,
"secure": false,
"httpOnly": false
}
]
},
{
"name": "示例网站6",
"url": "http://192.168.100.1",
"cookies": [ "cookies": [
{ {
"name": "sysauth_http", "name": "sysauth_http",
"value": "b05d9c8feea4a5a931e8ef687ac119de", "value": "788d6c33e71fbe7dd24947876759ac71",
"domain": "192.168.100.1", "domain": "192.168.100.1",
"path": "/", "path": "/",
"expires": 0, "expires": 0,
+84 -16
View File
@@ -17,12 +17,18 @@ class ConfigManager:
""" """
self.config_file = config_file self.config_file = config_file
self.default_config = { self.default_config = {
'sites': [] 'duration': 30,
'sites': [
{'name': '百度', 'url': 'https://www.baidu.com'},
{'name': '新浪', 'url': 'https://www.sina.com.cn'}
]
} }
# 在初始化时加载配置并保存到实例属性
self._config = self._load_config_from_file()
def load_config(self) -> Dict: def _load_config_from_file(self) -> Dict:
""" """
加载配置文件 从文件加载配置的内部方法
Returns: Returns:
dict: 配置数据 dict: 配置数据
@@ -32,23 +38,68 @@ class ConfigManager:
with open(self.config_file, 'r', encoding='utf-8') as f: with open(self.config_file, 'r', encoding='utf-8') as f:
config = json.load(f) config = json.load(f)
logger.info(f"成功加载配置文件: {self.config_file}") logger.info(f"成功加载配置文件: {self.config_file}")
# 确保配置中包含duration字段
if 'duration' not in config:
logger.info("配置中没有duration字段,使用默认值30秒")
config['duration'] = self.default_config['duration']
# 检查是否存在网站配置,如果不存在则使用默认示例网站并保存
sites = config.get('sites', [])
if not sites:
logger.warning("配置中没有设置任何网站,使用默认示例网站")
config['sites'] = self.default_config['sites']
# 保存默认网站到配置中
try:
self._save_config_to_file(config)
logger.info("已保存默认网站配置")
except Exception as e:
logger.error(f"保存默认网站配置失败: {e}")
return config return config
else: else:
logger.warning(f"配置文件不存在: {self.config_file}") logger.warning(f"配置文件不存在: {self.config_file}")
logger.info("使用默认配置") logger.info("使用默认配置")
return self.default_config.copy()
# 创建并保存默认配置
default_config = self.default_config.copy()
try:
self._save_config_to_file(default_config)
logger.info("已创建默认配置文件")
except Exception as e:
logger.error(f"保存默认配置文件失败: {e}")
return default_config
except json.JSONDecodeError as e: except json.JSONDecodeError as e:
logger.error(f"配置文件格式错误: {e}") logger.error(f"配置文件格式错误: {e}")
logger.info("使用默认配置") logger.info("使用默认配置")
return self.default_config.copy()
# 使用默认配置并保存
default_config = self.default_config.copy()
try:
self._save_config_to_file(default_config)
logger.info("已覆盖错误配置文件")
except Exception as e:
logger.error(f"保存默认配置文件失败: {e}")
return default_config
except Exception as e: except Exception as e:
logger.error(f"加载配置文件时发生错误: {e}") logger.error(f"加载配置文件时发生错误: {e}")
logger.info("使用默认配置") logger.info("使用默认配置")
return self.default_config.copy() return self.default_config.copy()
def save_config(self, config: Dict) -> bool: def load_config(self) -> Dict:
""" """
保存配置到文件 获取当前配置(从实例属性中返回)
Returns:
dict: 配置数据
"""
return self._config.copy() # 返回配置的副本,避免外部直接修改
def _save_config_to_file(self, config: Dict) -> bool:
"""
保存配置到文件的内部方法
Args: Args:
config (dict): 要保存的配置数据 config (dict): 要保存的配置数据
@@ -70,6 +121,22 @@ class ConfigManager:
logger.error(f"保存配置文件时发生错误: {e}") logger.error(f"保存配置文件时发生错误: {e}")
return False return False
def save_config(self, config: Dict) -> bool:
"""
保存配置并更新实例属性
Args:
config (dict): 要保存的配置数据
Returns:
bool: 保存是否成功
"""
if self._save_config_to_file(config):
# 保存成功后更新实例属性
self._config = config
return True
return False
def get_sites(self) -> List[Dict]: def get_sites(self) -> List[Dict]:
""" """
获取所有网站配置 获取所有网站配置
@@ -77,10 +144,9 @@ class ConfigManager:
Returns: Returns:
list: 网站配置列表 list: 网站配置列表
""" """
config = self.load_config() sites = self._config.get('sites', [])
sites = config.get('sites', [])
logger.info(f"获取到 {len(sites)} 个网站配置") logger.info(f"获取到 {len(sites)} 个网站配置")
return sites return sites.copy() # 返回副本以避免直接修改内部数据
def add_site(self, site_config: Dict) -> bool: def add_site(self, site_config: Dict) -> bool:
""" """
@@ -96,15 +162,15 @@ class ConfigManager:
logger.error("网站配置格式错误,必须包含name和url字段") logger.error("网站配置格式错误,必须包含name和url字段")
return False return False
config = self.load_config()
sites = config.get('sites', [])
# 检查是否已存在同名网站 # 检查是否已存在同名网站
for site in sites: for site in self._config.get('sites', []):
if site.get('name') == site_config.get('name'): if site.get('name') == site_config.get('name'):
logger.warning(f"网站 '{site_config.get('name')}' 已存在") logger.warning(f"网站 '{site_config.get('name')}' 已存在")
return False return False
# 创建配置副本并添加新网站
config = self._config.copy()
sites = config.get('sites', [])
sites.append(site_config) sites.append(site_config)
config['sites'] = sites config['sites'] = sites
return self.save_config(config) return self.save_config(config)
@@ -120,7 +186,8 @@ class ConfigManager:
Returns: Returns:
bool: 更新是否成功 bool: 更新是否成功
""" """
config = self.load_config() # 创建配置副本
config = self._config.copy()
sites = config.get('sites', []) sites = config.get('sites', [])
for i, site in enumerate(sites): for i, site in enumerate(sites):
@@ -145,7 +212,8 @@ class ConfigManager:
Returns: Returns:
bool: 删除是否成功 bool: 删除是否成功
""" """
config = self.load_config() # 创建配置副本
config = self._config.copy()
sites = config.get('sites', []) sites = config.get('sites', [])
new_sites = [site for site in sites if site.get('name') != site_name] new_sites = [site for site in sites if site.get('name') != site_name]
+345 -236
View File
@@ -1,7 +1,7 @@
import os import os
import time import time
from typing import Dict, List, Any from typing import Dict, List, Any, Optional
from DrissionPage import ChromiumPage, ChromiumOptions from DrissionPage import ChromiumPage, ChromiumOptions, Chromium
from logger_config import global_logger as logger from logger_config import global_logger as logger
from config import ConfigManager from config import ConfigManager
@@ -13,287 +13,396 @@ class LoginManager:
def __init__(self): def __init__(self):
""" """
初始化登录管理器 初始化登录管理器,创建浏览器对象
""" """
self.page = None self.browser = None
self.config_manager = ConfigManager() self.config_manager = ConfigManager()
self.sites = [] # 存储包含网站和页面信息的字典列表
def login(self, site_url: str, cookies: List[Dict[str, Any]] = None) -> bool:
"""
登录网站
Args:
site_url (str): 网站URL
cookies (List[Dict[str, Any]], optional): 预先设置的cookies记录列表
Returns:
bool: 登录是否成功
"""
logger.info(f"开始登录网站: {site_url}")
# 初始化浏览器对象
try: try:
# 根据DrissionPage 4.x文档,使用ChromiumOptions配置浏览器
logger.info("正在配置浏览器...")
# 创建浏览器配置对象 # 创建浏览器配置对象
co = ChromiumOptions() co = ChromiumOptions()
# 从配置文件中读取浏览器路径,如果不存在则使用默认路径 # 从配置文件中读取浏览器路径
config = self.config_manager.load_config() config = self.config_manager.load_config()
edge_path = config.get('browser_path', r'C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe') edge_path = config.get('browser_path', r'C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe')
if os.path.exists(edge_path): if os.path.exists(edge_path):
# 根据文档,使用set_browser_path方法设置浏览器路径 # 设置浏览器路径
co.set_browser_path(edge_path) co.set_browser_path(edge_path)
logger.info(f"已配置Edge浏览器路径: {edge_path}") logger.info(f"已配置浏览器路径: {edge_path}")
else: else:
logger.warning(f"Edge浏览器路径不存在: {edge_path}") logger.warning(f"浏览器路径不存在: {edge_path}")
logger.info("将使用默认配置,让DrissionPage自动查找浏览器") logger.info("将使用默认配置,让DrissionPage自动查找浏览器")
# 使用配置创建ChromiumPage实例 # 用配置创建浏览器对象
# 根据文档,在DrissionPage 4.x中,应该使用正确的方式初始化 self.browser = Chromium(addr_or_opts=co)
self.page = ChromiumPage(co) logger.info("浏览器对象初始化成功")
except Exception as e:
logger.error(f"初始化浏览器失败: {e}")
self.browser = None
def save_cookies(self) -> List[Dict[str, Any]]:
"""
保存所有打开网站的cookies到配置文件
Returns:
List[Dict[str, Any]]: 包含所有网站cookies的列表
"""
logger.info("开始保存所有网站的cookies到配置文件")
all_sites_cookies = []
if not self.sites:
logger.warning("没有打开的网站,无法获取cookies")
return all_sites_cookies
try:
# 加载配置文件
config = self.config_manager.load_config()
# 访问网站 # 遍历所有网站获取cookies
logger.info(f"正在加载网站: {site_url}") for index, site_info in enumerate(self.sites):
self.page.get(site_url) try:
page = site_info['page']
# 如果提供了cookies,设置它们 site_name = site_info.get('name', f'网站_{index+1}')
if cookies: site_url = getattr(page, 'url', site_info.get('url', f'网站_{index+1}'))
logger.info(f"设置 {len(cookies)} 条cookies记录") page_title = getattr(page, 'title', f'网站_{index+1}')
logger.info(f"获取网站 {index+1}/{len(self.sites)} 的cookies: {site_name} - {site_url}")
# 检查cookies类型,支持列表格式和字典格式
if isinstance(cookies, dict): # 获取页面的cookies
# 兼容旧的字典格式,转换为列表格式 cookies_list = self._get_page_cookies(page, site_name, site_url)
cookies_list = []
for key, value in cookies.items(): # 将网站信息和cookies添加到结果列表
cookies_list.append({ if cookies_list:
'name': key, all_sites_cookies.append({
'value': value, 'url': site_url,
'domain': '', 'title': page_title,
'path': '/', 'cookies': cookies_list
'expires': 0,
'secure': False,
'httpOnly': False
}) })
cookies = cookies_list logger.info(f"成功获取网站 {site_url}{len(cookies_list)} 条cookies记录")
logger.info(f"已将字典格式cookies转换为列表格式")
# 更新配置文件中的cookies - 使用name属性作为匹配条件
# 尝试多种方式设置cookies for config_site in config.get('sites', []):
try: if config_site.get('name') == site_name:
# 首先尝试使用set.cookies()方法(DrissionPage 4.x推荐方式) config_site['cookies'] = cookies_list
if hasattr(self.page, 'set') and hasattr(self.page.set, 'cookies'): logger.info(f"已更新配置文件中网站 {site_name} 的cookies")
if hasattr(self.page.set.cookies, 'clear'): break
self.page.set.cookies.clear()
self.page.set.cookies(cookies)
logger.info(f"成功使用set.cookies()方法设置cookies")
# 备选方案1:使用set_cookies方法
elif hasattr(self.page, 'set_cookies'):
self.page.set_cookies(cookies)
logger.info("成功使用set_cookies方法设置cookies")
# 备选方案2:使用page.cookies属性
elif hasattr(self.page, 'cookies'):
self.page.cookies = cookies
logger.info("成功使用page.cookies属性设置cookies")
else: else:
logger.error("无法找到设置cookies的有效方法") logger.warning(f"未能获取网站 {site_name} - {site_url}cookies")
except Exception as e: except Exception as e:
logger.error(f"设置cookies失败: {e}") logger.error(f"处理网站时发生错误: {e}")
continue
# 刷新页面以应用cookies
# 使用ConfigManager保存更新后的配置
if self.config_manager.save_config(config):
logger.info("已将更新后的配置保存到配置文件")
else:
logger.error("保存配置文件失败")
except Exception as e:
logger.error(f"保存cookies到配置文件时发生错误: {e}")
logger.info(f"成功获取并保存 {len(all_sites_cookies)} 个网站的cookies")
return all_sites_cookies
def close(self, quit_browser: bool = None) -> None:
"""
关闭浏览器页面,可选择是否同时关闭浏览器对象
Args:
quit_browser (bool): 是否关闭浏览器对象,None表示根据配置决定
"""
# 关闭所有页面
if self.sites:
for page_info in reversed(self.sites):
try: try:
self.page.refresh() page = page_info['page']
if hasattr(page, 'close'):
page.close()
logger.info("一个标签页已关闭")
except Exception as e: except Exception as e:
logger.warning(f"刷新页面时出错: {e}") logger.error(f"关闭浏览器页面时发生错误: {e}")
# 清空sites列表
self.sites = []
else:
logger.warning("没有活动的浏览器页面需要关闭")
# 决定是否关闭浏览器对象
should_quit = quit_browser
# 只有当调用者没有明确指定时,才从配置中读取
if should_quit is None:
try:
config = self.config_manager.load_config()
should_quit = config.get('AutoQuit', False)
except Exception as e:
logger.error(f"读取配置时出错: {e}")
should_quit = False
# 如果需要关闭浏览器对象
if should_quit and self.browser:
try:
self.browser.quit()
logger.info("浏览器对象已关闭")
self.browser = None
except Exception as e:
logger.error(f"关闭浏览器对象时发生错误: {e}")
def _add_page_metadata(self, page: Any, name: str, url: str, cookies: Optional[List[Dict[str, Any]]] = None) -> None:
"""
添加页面元数据到sites列表
Args:
page: 页面对象
name: 网站名称
url: 网站URL
cookies: cookies列表
"""
self.sites.append({
'page': page,
'name': name,
'url': url,
'original_cookies': cookies if cookies else []
})
def _set_cookies_to_page(self, page: Any, cookies: List[Dict[str, Any]] or Dict[str, str], site_name: str) -> bool:
"""
为页面对象设置cookies
Args:
page: 页面对象
cookies: cookies列表或字典
site_name: 网站名称
# 等待页面加载完成 Returns:
time.sleep(2) bool: 设置是否成功
"""
logger.info(f"为网站 '{site_name}' 设置 {len(cookies)} 条cookies记录")
try:
# 检查cookies类型,支持列表格式和字典格式
if isinstance(cookies, dict):
# 兼容旧的字典格式,转换为列表格式
cookies_list = []
for key, value in cookies.items():
cookies_list.append({
'name': key,
'value': value,
'domain': '',
'path': '/',
'expires': 0,
'secure': False,
'httpOnly': False
})
cookies = cookies_list
logger.info(f"已将字典格式cookies转换为列表格式")
# 验证是否登录成功 # 尝试多种方式设置cookies
current_url = self.page.url if hasattr(page, 'set') and hasattr(page.set, 'cookies'):
logger.info(f"当前URL: {current_url}") if hasattr(page.set.cookies, 'clear'):
page.set.cookies.clear()
page.set.cookies(cookies)
logger.info(f"成功使用set.cookies()方法设置cookies")
elif hasattr(page, 'set_cookies'):
page.set_cookies(cookies)
logger.info("成功使用set_cookies方法设置cookies")
elif hasattr(page, 'cookies'):
page.cookies = cookies
logger.info("成功使用page.cookies属性设置cookies")
else:
logger.error("无法找到设置cookies的有效方法")
return False
# 如果能成功加载页面,暂时认为登录成功 # 刷新页面以应用cookies
page.refresh()
logger.info(f"网站 '{site_name}' 页面已刷新以应用cookies")
return True return True
except Exception as e: except Exception as e:
error_msg = str(e) logger.error(f"为网站 '{site_name}' 设置cookies失败: {e}")
logger.error(f"登录网站时发生错误: {error_msg}")
# 针对浏览器路径错误提供详细指导
if "无法找到浏览器可执行文件路径" in error_msg:
logger.warning("===== 浏览器配置错误指导 =====")
logger.warning("DrissionPage无法找到系统中的浏览器")
logger.warning("请尝试以下解决方法:")
logger.warning("1. 确保Microsoft Edge浏览器已正确安装")
logger.warning("2. Edge浏览器默认安装路径: C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe")
logger.warning("3. 检查Edge浏览器是否在系统PATH环境变量中")
logger.warning("4. 或者尝试安装Chrome浏览器")
logger.warning("5. 确保用户有足够的权限运行浏览器")
logger.warning("==========================")
# 安全关闭浏览器
if self.page:
try:
self.page.close()
except:
pass
self.page = None
return False return False
def maintain_login(self, duration: int = 120) -> None: def _get_page_cookies(self, page: Any, site_name: str, page_url: Optional[str] = None) -> List[Dict[str, Any]]:
""" """
保持登录状态 获取指定页面的cookies
Args: Args:
duration (int): 保持时间(秒) page: 页面对象
site_name: 网站名称
page_url: 页面URL(可选)
Returns:
List[Dict[str, Any]]: cookies列表
""" """
if not self.page: cookies_list = []
logger.warning("没有活动的浏览器页面,无法保持登录状态") cookies = None
return
logger.info(f"保持登录状态 {duration} 秒...") # 构建日志标识
log_id = f"网站 '{site_name}'" if not page_url else f"页面 {site_name} - {page_url}"
# 尝试多种方式获取cookies
try: try:
# 定期刷新页面以保持会话活跃 if hasattr(page, 'get_cookies'):
interval = min(30, duration) # 最多每30秒刷新一次 cookies = page.get_cookies()
elapsed = 0 elif hasattr(page, 'cookies'):
if callable(page.cookies):
while elapsed < duration: cookies = page.cookies()
time.sleep(interval) else:
elapsed += interval cookies = page.cookies
except Exception as api_e:
logger.warning(f"获取{log_id}的cookies时出错: {api_e}")
# 处理获取到的cookies
if cookies is not None:
if isinstance(cookies, list):
for cookie in cookies:
if 'name' in cookie and 'value' in cookie:
standard_cookie = {
'name': cookie.get('name'),
'value': cookie.get('value'),
'domain': cookie.get('domain', ''),
'path': cookie.get('path', '/'),
'expires': cookie.get('expires', 0),
'secure': cookie.get('secure', False),
'httpOnly': cookie.get('httpOnly', False)
}
cookies_list.append(standard_cookie)
elif isinstance(cookies, dict):
for key, value in cookies.items():
cookies_list.append({
'name': key,
'value': value,
'domain': '',
'path': '/',
'expires': 0,
'secure': False,
'httpOnly': False
})
# 如果还是没有获取到cookies,尝试使用JavaScript
if not cookies_list:
try:
if hasattr(page, 'run_js'):
js_cookies = page.run_js('return document.cookie')
elif hasattr(page, 'execute_script'):
js_cookies = page.execute_script('return document.cookie')
else:
js_cookies = None
# 刷新页面 if js_cookies:
try: cookie_pairs = js_cookies.split(';')
self.page.refresh() for pair in cookie_pairs:
logger.info(f"页面已刷新,已保持登录 {elapsed}") pair = pair.strip()
except Exception as e: if '=' in pair:
logger.error(f"刷新页面时发生错误: {e}") try:
break key, value = pair.split('=', 1)
cookies_list.append({
except KeyboardInterrupt: 'name': key.strip(),
logger.info("保持登录状态被用户中断") 'value': value.strip(),
except Exception as e: 'domain': '',
logger.error(f"保持登录状态时发生错误: {e}") 'path': '/',
'expires': 0,
'secure': False,
'httpOnly': False
})
except Exception as parse_e:
logger.warning(f"解析cookie对失败: {pair}, 错误: {parse_e}")
except Exception as js_e:
logger.warning(f"使用JavaScript获取{log_id}的cookies时出错: {js_e}")
return cookies_list
def get_cookies(self) -> List[Dict[str, Any]]: def get_duration(self) -> int:
""" """
获取当前页面的cookies 获取配置中的延时时间
Returns: Returns:
List[Dict[str, Any]]: cookies记录列表 int: 延时秒数
""" """
if not self.page: try:
logger.warning("没有活动的浏览器页面,无法获取cookies") return self.config_manager.load_config().get('duration', 30)
return [] except Exception as e:
logger.error(f"获取duration配置时发生错误: {e}")
return 30 # 返回默认值
def run(self) -> None:
"""
从配置文件中读取网站信息,为每个网站新建标签页并加载cookies
"""
logger.info("开始运行自动登录流程")
# 检查浏览器对象是否初始化
if not self.browser:
logger.error("浏览器对象未初始化,无法运行自动登录流程")
return
try: try:
cookies_list = [] # 从配置文件中读取网站信息
config = self.config_manager.load_config()
sites = config.get('sites', [])
# 根据DrissionPage文档,尝试多种方式获取cookies logger.info(f"获取到 {len(sites)} 个网站配置")
cookies = None
# 首先尝试使用标准API获取cookies if not sites:
try: logger.warning("配置文件中没有网站信息")
# 尝试使用get_cookies方法 return
if hasattr(self.page, 'get_cookies'):
logger.info("尝试使用page.get_cookies()方法获取cookies")
cookies = self.page.get_cookies()
# 尝试使用page.cookies属性
elif hasattr(self.page, 'cookies'):
logger.info("尝试使用page.cookies属性获取cookies")
# 检查是否是可调用对象
if callable(self.page.cookies):
cookies = self.page.cookies()
else:
cookies = self.page.cookies
else:
logger.error("无法找到获取cookies的有效方法")
return []
except Exception as api_e:
logger.warning(f"使用标准API获取cookies失败: {api_e},尝试其他方法")
# 处理获取到的cookies logger.info(f"找到 {len(sites)} 个网站需要登录")
if cookies is not None:
if isinstance(cookies, list):
# 如果已经是列表格式,确保每条记录包含必要字段
for cookie in cookies:
# 确保cookie记录包含必要的字段
if 'name' in cookie and 'value' in cookie:
# 标准化cookie记录格式
standard_cookie = {
'name': cookie.get('name'),
'value': cookie.get('value'),
'domain': cookie.get('domain', ''),
'path': cookie.get('path', '/'),
'expires': cookie.get('expires', 0),
'secure': cookie.get('secure', False),
'httpOnly': cookie.get('httpOnly', False)
}
cookies_list.append(standard_cookie)
elif isinstance(cookies, dict):
# 如果是字典格式,转换为列表格式
for key, value in cookies.items():
cookies_list.append({
'name': key,
'value': value,
'domain': '',
'path': '/',
'expires': 0,
'secure': False,
'httpOnly': False
})
# 如果还是没有获取到cookies,尝试使用execute_script方法 # 存储所有创建的标签页
if not cookies_list: tabs = []
# 为每个网站创建标签页并设置cookies
for site in sites:
try: try:
logger.info("尝试使用JavaScript获取cookies") site_name = site.get('name', '未命名网站')
if hasattr(self.page, 'run_js'): site_url = site.get('url', '')
js_cookies = self.page.run_js('return document.cookie') site_cookies = site.get('cookies', [])
elif hasattr(self.page, 'execute_script'):
js_cookies = self.page.execute_script('return document.cookie')
else:
logger.warning("无法找到执行JavaScript的方法")
js_cookies = None
if js_cookies: if not site_url:
# 解析JavaScript返回的cookie字符串 logger.warning(f"网站 '{site_name}' 缺少URL,跳过")
cookie_pairs = js_cookies.split(';') continue
for pair in cookie_pairs:
pair = pair.strip() logger.info(f"开始处理网站: {site_name} ({site_url})")
if '=' in pair:
try: # 创建新标签页
key, value = pair.split('=', 1) try:
cookies_list.append({ new_tab = self.browser.new_tab(site_url)
'name': key.strip(), logger.info(f"为网站 '{site_name}' 创建了新标签页")
'value': value.strip(), tabs.append(new_tab)
'domain': '', # 添加网站元数据
'path': '/', self._add_page_metadata(new_tab, site_name, site_url, site_cookies)
'expires': 0, except Exception as e:
'secure': False, logger.error(f"为网站 '{site_name}' 创建标签页失败: {e}")
'httpOnly': False continue
})
except Exception as parse_e: # 设置cookies
logger.warning(f"解析cookie对失败: {pair}, 错误: {parse_e}") if site_cookies:
except Exception as js_e: self._set_cookies_to_page(new_tab, site_cookies, site_name)
logger.warning(f"使用JavaScript获取cookies失败: {js_e}")
# 等待页面加载完成
logger.info("页面加载中...")
time.sleep(2) # 仅保留短暂延时确保页面加载完成
logger.info(f"网站 '{site_name}' 处理完成")
except Exception as e:
logger.error(f"处理网站时发生错误: {e}")
continue
logger.info(f"成功获取 {len(cookies_list)} 条cookies记录") logger.info(f"所有网站处理完成,共创建了 {len(tabs)} 个标签页")
return cookies_list
# 所有标签页已通过_add_page_metadata添加到sites列表中
if tabs:
logger.info("已创建并记录所有标签页")
except Exception as e: except Exception as e:
logger.error(f"获取cookies时发生错误: {e}") logger.error(f"运行自动登录流程时发生错误: {e}")
return []
def close(self) -> None:
"""
关闭浏览器页面
"""
if self.page:
try:
self.page.browser.quit()
# self.page.close()
# self.page = None
logger.info("浏览器页面已关闭")
except Exception as e:
logger.error(f"关闭浏览器页面时发生错误: {e}")
else:
logger.warning("没有活动的浏览器页面需要关闭")
+24 -76
View File
@@ -1,5 +1,4 @@
import time import time
import json
from typing import Dict, List, Any from typing import Dict, List, Any
from config import ConfigManager from config import ConfigManager
from login_manager import LoginManager from login_manager import LoginManager
@@ -17,7 +16,6 @@ class AutoLoginApp:
""" """
self.config_manager = ConfigManager() self.config_manager = ConfigManager()
self.login_manager = LoginManager() self.login_manager = LoginManager()
def run(self): def run(self):
""" """
@@ -25,83 +23,36 @@ class AutoLoginApp:
""" """
logger.info("===== 自动登录应用启动 =====") logger.info("===== 自动登录应用启动 =====")
# 加载配置 # 直接调用LoginManager的run函数处理所有网站
config = self.config_manager.load_config() self.login_manager.run()
sites = config.get('sites', [])
if not sites: # 从配置管理器直接获取duration参数
logger.warning("配置中没有设置任何网站,使用默认示例网站") duration = self.login_manager.get_duration()
# 添加默认示例网站
sites = [
{'name': '百度', 'url': 'https://www.baidu.com'},
{'name': '新浪', 'url': 'https://www.sina.com.cn'}
]
# 处理每个网站 logger.info(f"延时 {duration} 秒后获取所有页面cookies")
for site in sites: time.sleep(duration)
site_name = site.get('name', '未知网站')
site_url = site.get('url', '')
if not site_url:
logger.warning(f"网站 '{site_name}' 的URL为空,跳过")
continue
logger.info(f"处理网站: {site_name} ({site_url})")
# 获取cookies(从配置中)
cookies = []
if 'cookies' in site:
config_cookies = site['cookies']
# 确保cookies是列表格式
if isinstance(config_cookies, dict):
# 直接将字典转换为列表格式
cookies = []
for key, value in config_cookies.items():
cookies.append({
'name': key,
'value': value,
'domain': '',
'path': '/',
'expires': 0,
'secure': False,
'httpOnly': False
})
else:
cookies = config_cookies
logger.info(f"使用配置文件中的 {len(cookies)} 条cookies记录")
# 登录网站
login_success = self.login_manager.login(site_url, cookies)
if login_success:
logger.info(f"登录网站 '{site_name}' 成功")
# 保持登录状态一段时间,从配置中获取duration值,默认30秒
duration = site.get('duration', 30)
self.login_manager.maintain_login(duration=duration) # 保持{duration}秒
# 保持登录状态后获取并更新到配置中的cookies
new_cookies = self.login_manager.get_cookies()
if new_cookies:
# 更新当前site对象的cookies
site['cookies'] = new_cookies
logger.info(f"已更新网站 '{site_name}' 的cookies到配置中,共 {len(new_cookies)} 条记录")
else:
logger.error(f"登录网站 '{site_name}' 失败")
# 关闭当前浏览器页面,准备下一个网站
self.login_manager.close()
time.sleep(2) # 间隔2秒再处理下一个网站
# 保存更新后的配置到config.json # 调用save_cookies函数获取并保存所有网站的cookies到config.json
try: try:
self.config_manager.save_config(config) all_cookies = self.login_manager.save_cookies()
logger.info("已将更新后的cookies保存到config.json文件") logger.info(f"成功获取并保存 {len(all_cookies)} 个网站的cookies到config.json文件")
except Exception as e: except Exception as e:
logger.error(f"保存配置文件时发生错误: {e}") logger.error(f"获取或保存cookies时发生错误: {e}")
logger.info("===== 所有网站处理完成 =====") logger.info("===== 所有网站处理完成 =====")
# 从配置管理器直接获取AutoQuit配置并关闭浏览器
auto_quit = self.config_manager.load_config().get('AutoQuit', False)
if auto_quit:
try:
self.login_manager.close(True) # 直接传入True表示关闭浏览器
logger.info("已关闭浏览器")
except Exception as e:
logger.error(f"关闭浏览器时发生错误: {e}")
else:
logger.info("浏览器保持打开状态")
# 不再需要返回auto_quit值,因为它已经在方法内部使用
if __name__ == "__main__": if __name__ == "__main__":
app = AutoLoginApp() app = AutoLoginApp()
@@ -110,7 +61,4 @@ if __name__ == "__main__":
except KeyboardInterrupt: except KeyboardInterrupt:
logger.info("程序被用户中断") logger.info("程序被用户中断")
except Exception as e: except Exception as e:
logger.error(f"程序运行时发生错误: {e}") logger.error(f"程序运行时发生错误: {e}")
finally:
# 确保关闭浏览器
app.login_manager.close()
+7 -7
View File
@@ -8,7 +8,7 @@
- 从配置文件读取网站地址和对应的cookies信息 - 从配置文件读取网站地址和对应的cookies信息
- 按顺序登录每个网站 - 按顺序登录每个网站
- 登录成功后保持登录状态指定时间(可配置) - 登录成功后依次打开多个网页(支持全局duration配置)
- 保存更新后的cookies信息到配置文件 - 保存更新后的cookies信息到配置文件
- 关闭网站浏览器实例 - 关闭网站浏览器实例
- 完整的日志记录功能 - 完整的日志记录功能
@@ -60,7 +60,7 @@ autoLogon/
- `__init__()`: 初始化登录管理器 - `__init__()`: 初始化登录管理器
- `login(site_url, cookies=None)`: 登录网站,支持设置cookies - `login(site_url, cookies=None)`: 登录网站,支持设置cookies
- `maintain_login(duration=120)`: 保持登录状态指定时间 - `maintain_login(duration=120)`: 保持登录状态指定时间
- `get_cookies()`: 获取当前页面的cookies - `save_cookies()`: 获取所有页面的cookies
- `close()`: 关闭浏览器页面 - `close()`: 关闭浏览器页面
### 5.3 日志配置器 (logger_config.py) ### 5.3 日志配置器 (logger_config.py)
@@ -96,13 +96,13 @@ autoLogon/
```json ```json
{ {
"duration": 30,
"browser_path": "C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe", "browser_path": "C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe",
"sites": [ "sites": [
{ {
"name": "网站名称", "name": "网站名称",
"url": "网站URL", "url": "网站URL",
"duration": 30, "cookies": [
"cookies": [
{ {
"name": "cookie名称", "name": "cookie名称",
"value": "cookie值", "value": "cookie值",
@@ -119,11 +119,11 @@ autoLogon/
``` ```
**配置项说明** **配置项说明**
- `duration`: 处理每个网站时的延时时间(秒),默认30秒
- `browser_path`: 浏览器可执行文件路径(可选) - `browser_path`: 浏览器可执行文件路径(可选)
- `sites`: 网站配置列表 - `sites`: 网站配置列表
- `name`: 网站名称 - `name`: 网站名称
- `url`: 网站URL - `url`: 网站URL
- `duration`: 保持登录状态的时间(秒),默认30秒
- `cookies`: cookies列表,每个cookie包含完整信息(name, value, domain等) - `cookies`: cookies列表,每个cookie包含完整信息(name, value, domain等)
``` ```