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
{
"duration": 30,
"browser_path": "C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe",
"sites": [
{
"name": "网站名称",
"url": "网站URL",
"duration": 30,
"cookies": [
{
"name": "cookie名称",
@@ -105,7 +105,7 @@ python main.py
## 注意事项
- 确保浏览器路径正确配置,默认使用Edge浏览器
- 配置文件中的`duration`参数表示网站打开后保持的时间(秒)
- 配置文件中的`duration`参数表示处理每个网站时的延时时间(秒),默认30秒
- 首次运行时,应用会为没有cookies的网站创建空的cookies配置
- 日志文件保存在`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": [
{
"name": "百度",
"url": "https://www.baidu.com",
"duration": 30,
"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",
"value": "16F9B2349AAE73EDA898BA50E44AE46D:FG=1",
"value": "07286BA0A030422D44083F3B4F2DAB27:FG=1",
"domain": ".baidu.com",
"path": "/",
"expires": 0,
@@ -92,92 +20,19 @@
{
"name": "新浪",
"url": "https://www.sina.com.cn",
"duration": 30,
"cookies": [
{
"name": "BAIDUID_BFESS",
"value": "07286BA0A030422D44083F3B4F2DAB27:FG=1",
"domain": ".baidu.com",
"path": "/",
"expires": 0,
"secure": false,
"httpOnly": false
},
{
"name": "Hm_lpvt_90c40f528e0b2106bc03da5aadec190f",
"value": "1760771854",
"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",
"value": "1760797352",
"domain": ".www.sina.com.cn",
"path": "/",
"expires": 0,
@@ -186,32 +41,37 @@
},
{
"name": "Hm_lvt_90c40f528e0b2106bc03da5aadec190f",
"value": "1760759893,1760768784,1760769195",
"value": "1760794976,1760795033,1760795177,1760795343",
"domain": ".www.sina.com.cn",
"path": "/",
"expires": 0,
"secure": false,
"httpOnly": false
},
{
"name": "SGUID",
"value": "1760768783361_32567590",
"domain": ".sina.com.cn",
"path": "/",
"expires": 0,
"secure": false,
"httpOnly": false
}
]
},
{
"name": "示例网站2",
"url": "http://192.168.100.1/",
"duration": 30,
{
"name": "示例网站5",
"url": "http://192.168.100.100:3000/#/login",
"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": [
{
"name": "sysauth_http",
"value": "b05d9c8feea4a5a931e8ef687ac119de",
"value": "788d6c33e71fbe7dd24947876759ac71",
"domain": "192.168.100.1",
"path": "/",
"expires": 0,
+84 -16
View File
@@ -17,12 +17,18 @@ class ConfigManager:
"""
self.config_file = config_file
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:
dict: 配置数据
@@ -32,23 +38,68 @@ class ConfigManager:
with open(self.config_file, 'r', encoding='utf-8') as f:
config = json.load(f)
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
else:
logger.warning(f"配置文件不存在: {self.config_file}")
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:
logger.error(f"配置文件格式错误: {e}")
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:
logger.error(f"加载配置文件时发生错误: {e}")
logger.info("使用默认配置")
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:
config (dict): 要保存的配置数据
@@ -70,6 +121,22 @@ class ConfigManager:
logger.error(f"保存配置文件时发生错误: {e}")
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]:
"""
获取所有网站配置
@@ -77,10 +144,9 @@ class ConfigManager:
Returns:
list: 网站配置列表
"""
config = self.load_config()
sites = config.get('sites', [])
sites = self._config.get('sites', [])
logger.info(f"获取到 {len(sites)} 个网站配置")
return sites
return sites.copy() # 返回副本以避免直接修改内部数据
def add_site(self, site_config: Dict) -> bool:
"""
@@ -96,15 +162,15 @@ class ConfigManager:
logger.error("网站配置格式错误,必须包含name和url字段")
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'):
logger.warning(f"网站 '{site_config.get('name')}' 已存在")
return False
# 创建配置副本并添加新网站
config = self._config.copy()
sites = config.get('sites', [])
sites.append(site_config)
config['sites'] = sites
return self.save_config(config)
@@ -120,7 +186,8 @@ class ConfigManager:
Returns:
bool: 更新是否成功
"""
config = self.load_config()
# 创建配置副本
config = self._config.copy()
sites = config.get('sites', [])
for i, site in enumerate(sites):
@@ -145,7 +212,8 @@ class ConfigManager:
Returns:
bool: 删除是否成功
"""
config = self.load_config()
# 创建配置副本
config = self._config.copy()
sites = config.get('sites', [])
new_sites = [site for site in sites if site.get('name') != site_name]
+348 -239
View File
@@ -1,7 +1,7 @@
import os
import time
from typing import Dict, List, Any
from DrissionPage import ChromiumPage, ChromiumOptions
from typing import Dict, List, Any, Optional
from DrissionPage import ChromiumPage, ChromiumOptions, Chromium
from logger_config import global_logger as logger
from config import ConfigManager
@@ -13,287 +13,396 @@ class LoginManager:
def __init__(self):
"""
初始化登录管理器
初始化登录管理器,创建浏览器对象
"""
self.page = None
self.browser = None
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:
# 根据DrissionPage 4.x文档,使用ChromiumOptions配置浏览器
logger.info("正在配置浏览器...")
# 创建浏览器配置对象
co = ChromiumOptions()
# 从配置文件中读取浏览器路径,如果不存在则使用默认路径
# 从配置文件中读取浏览器路径
config = self.config_manager.load_config()
edge_path = config.get('browser_path', r'C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe')
if os.path.exists(edge_path):
# 根据文档,使用set_browser_path方法设置浏览器路径
# 设置浏览器路径
co.set_browser_path(edge_path)
logger.info(f"已配置Edge浏览器路径: {edge_path}")
logger.info(f"已配置浏览器路径: {edge_path}")
else:
logger.warning(f"Edge浏览器路径不存在: {edge_path}")
logger.warning(f"浏览器路径不存在: {edge_path}")
logger.info("将使用默认配置,让DrissionPage自动查找浏览器")
# 使用配置创建ChromiumPage实例
# 根据文档,在DrissionPage 4.x中,应该使用正确的方式初始化
self.page = ChromiumPage(co)
# 用配置创建浏览器对象
self.browser = Chromium(addr_or_opts=co)
logger.info("浏览器对象初始化成功")
except Exception as e:
logger.error(f"初始化浏览器失败: {e}")
self.browser = None
# 访问网站
logger.info(f"正在加载网站: {site_url}")
self.page.get(site_url)
# 如果提供了cookies,设置它们
if cookies:
logger.info(f"设置 {len(cookies)} 条cookies记录")
# 检查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
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
for index, site_info in enumerate(self.sites):
try:
page = site_info['page']
site_name = site_info.get('name', f'网站_{index+1}')
site_url = getattr(page, 'url', site_info.get('url', f'网站_{index+1}'))
page_title = getattr(page, 'title', f'网站_{index+1}')
logger.info(f"获取网站 {index+1}/{len(self.sites)} 的cookies: {site_name} - {site_url}")
# 获取页面的cookies
cookies_list = self._get_page_cookies(page, site_name, site_url)
# 将网站信息和cookies添加到结果列表
if cookies_list:
all_sites_cookies.append({
'url': site_url,
'title': page_title,
'cookies': cookies_list
})
cookies = cookies_list
logger.info(f"已将字典格式cookies转换为列表格式")
logger.info(f"成功获取网站 {site_url}{len(cookies_list)} 条cookies记录")
# 尝试多种方式设置cookies
try:
# 首先尝试使用set.cookies()方法(DrissionPage 4.x推荐方式)
if hasattr(self.page, 'set') and hasattr(self.page.set, 'cookies'):
if hasattr(self.page.set.cookies, 'clear'):
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")
# 更新配置文件中的cookies - 使用name属性作为匹配条件
for config_site in config.get('sites', []):
if config_site.get('name') == site_name:
config_site['cookies'] = cookies_list
logger.info(f"已更新配置文件中网站 {site_name} 的cookies")
break
else:
logger.error("无法找到设置cookies的有效方法")
except Exception as e:
logger.error(f"设置cookies失败: {e}")
logger.warning(f"未能获取网站 {site_name} - {site_url}cookies")
# 刷新页面以应用cookies
except Exception as e:
logger.error(f"处理网站时发生错误: {e}")
continue
# 使用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:
self.page.refresh()
page = page_info['page']
if hasattr(page, 'close'):
page.close()
logger.info("一个标签页已关闭")
except Exception as e:
logger.warning(f"刷新页面时出错: {e}")
logger.error(f"关闭浏览器页面时发生错误: {e}")
# 清空sites列表
self.sites = []
else:
logger.warning("没有活动的浏览器页面需要关闭")
# 等待页面加载完成
time.sleep(2)
# 决定是否关闭浏览器对象
should_quit = quit_browser
# 验证是否登录成功
current_url = self.page.url
logger.info(f"当前URL: {current_url}")
# 只有当调用者没有明确指定时,才从配置中读取
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:
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
if hasattr(page, 'set') and hasattr(page.set, 'cookies'):
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
except Exception as e:
error_msg = str(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
logger.error(f"为网站 '{site_name}' 设置cookies失败: {e}")
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:
duration (int): 保持时间(秒)
"""
if not self.page:
logger.warning("没有活动的浏览器页面,无法保持登录状态")
return
logger.info(f"保持登录状态 {duration} 秒...")
try:
# 定期刷新页面以保持会话活跃
interval = min(30, duration) # 最多每30秒刷新一次
elapsed = 0
while elapsed < duration:
time.sleep(interval)
elapsed += interval
# 刷新页面
try:
self.page.refresh()
logger.info(f"页面已刷新,已保持登录 {elapsed}")
except Exception as e:
logger.error(f"刷新页面时发生错误: {e}")
break
except KeyboardInterrupt:
logger.info("保持登录状态被用户中断")
except Exception as e:
logger.error(f"保持登录状态时发生错误: {e}")
def get_cookies(self) -> List[Dict[str, Any]]:
"""
获取当前页面的cookies
page: 页面对象
site_name: 网站名称
page_url: 页面URL(可选)
Returns:
List[Dict[str, Any]]: cookies记录列表
List[Dict[str, Any]]: cookies列表
"""
if not self.page:
logger.warning("没有活动的浏览器页面,无法获取cookies")
return []
cookies_list = []
cookies = None
# 构建日志标识
log_id = f"网站 '{site_name}'" if not page_url else f"页面 {site_name} - {page_url}"
# 尝试多种方式获取cookies
try:
if hasattr(page, 'get_cookies'):
cookies = page.get_cookies()
elif hasattr(page, 'cookies'):
if callable(page.cookies):
cookies = page.cookies()
else:
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:
cookie_pairs = js_cookies.split(';')
for pair in cookie_pairs:
pair = pair.strip()
if '=' in pair:
try:
key, value = pair.split('=', 1)
cookies_list.append({
'name': key.strip(),
'value': value.strip(),
'domain': '',
'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_duration(self) -> int:
"""
获取配置中的延时时间
Returns:
int: 延时秒数
"""
try:
return self.config_manager.load_config().get('duration', 30)
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:
cookies_list = []
# 从配置文件中读取网站信息
config = self.config_manager.load_config()
sites = config.get('sites', [])
# 根据DrissionPage文档,尝试多种方式获取cookies
cookies = None
logger.info(f"获取到 {len(sites)} 个网站配置")
# 首先尝试使用标准API获取cookies
try:
# 尝试使用get_cookies方法
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},尝试其他方法")
if not sites:
logger.warning("配置文件中没有网站信息")
return
# 处理获取到的cookies
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
})
logger.info(f"找到 {len(sites)} 个网站需要登录")
# 如果还是没有获取到cookies,尝试使用execute_script方法
if not cookies_list:
# 存储所有创建的标签页
tabs = []
# 为每个网站创建标签页并设置cookies
for site in sites:
try:
logger.info("尝试使用JavaScript获取cookies")
if hasattr(self.page, 'run_js'):
js_cookies = self.page.run_js('return document.cookie')
elif hasattr(self.page, 'execute_script'):
js_cookies = self.page.execute_script('return document.cookie')
else:
logger.warning("无法找到执行JavaScript的方法")
js_cookies = None
site_name = site.get('name', '未命名网站')
site_url = site.get('url', '')
site_cookies = site.get('cookies', [])
if js_cookies:
# 解析JavaScript返回的cookie字符串
cookie_pairs = js_cookies.split(';')
for pair in cookie_pairs:
pair = pair.strip()
if '=' in pair:
try:
key, value = pair.split('=', 1)
cookies_list.append({
'name': key.strip(),
'value': value.strip(),
'domain': '',
'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获取cookies失败: {js_e}")
if not site_url:
logger.warning(f"网站 '{site_name}' 缺少URL,跳过")
continue
logger.info(f"开始处理网站: {site_name} ({site_url})")
# 创建新标签页
try:
new_tab = self.browser.new_tab(site_url)
logger.info(f"为网站 '{site_name}' 创建了新标签页")
tabs.append(new_tab)
# 添加网站元数据
self._add_page_metadata(new_tab, site_name, site_url, site_cookies)
except Exception as e:
logger.error(f"为网站 '{site_name}' 创建标签页失败: {e}")
continue
# 设置cookies
if site_cookies:
self._set_cookies_to_page(new_tab, site_cookies, site_name)
# 等待页面加载完成
logger.info("页面加载中...")
time.sleep(2) # 仅保留短暂延时确保页面加载完成
logger.info(f"网站 '{site_name}' 处理完成")
except Exception as e:
logger.error(f"处理网站时发生错误: {e}")
continue
logger.info(f"所有网站处理完成,共创建了 {len(tabs)} 个标签页")
# 所有标签页已通过_add_page_metadata添加到sites列表中
if tabs:
logger.info("已创建并记录所有标签页")
logger.info(f"成功获取 {len(cookies_list)} 条cookies记录")
return cookies_list
except Exception as e:
logger.error(f"获取cookies时发生错误: {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("没有活动的浏览器页面需要关闭")
logger.error(f"运行自动登录流程时发生错误: {e}")
+23 -75
View File
@@ -1,5 +1,4 @@
import time
import json
from typing import Dict, List, Any
from config import ConfigManager
from login_manager import LoginManager
@@ -18,91 +17,43 @@ class AutoLoginApp:
self.config_manager = ConfigManager()
self.login_manager = LoginManager()
def run(self):
"""
运行自动登录应用
"""
logger.info("===== 自动登录应用启动 =====")
# 加载配置
config = self.config_manager.load_config()
sites = config.get('sites', [])
# 直接调用LoginManager的run函数处理所有网站
self.login_manager.run()
if not sites:
logger.warning("配置中没有设置任何网站,使用默认示例网站")
# 添加默认示例网站
sites = [
{'name': '百度', 'url': 'https://www.baidu.com'},
{'name': '新浪', 'url': 'https://www.sina.com.cn'}
]
# 从配置管理器直接获取duration参数
duration = self.login_manager.get_duration()
# 处理每个网站
for site in sites:
site_name = site.get('name', '未知网站')
site_url = site.get('url', '')
logger.info(f"延时 {duration} 秒后获取所有页面cookies")
time.sleep(duration)
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:
self.config_manager.save_config(config)
logger.info("已将更新后的cookies保存到config.json文件")
all_cookies = self.login_manager.save_cookies()
logger.info(f"成功获取并保存 {len(all_cookies)} 个网站的cookies到config.json文件")
except Exception as e:
logger.error(f"保存配置文件时发生错误: {e}")
logger.error(f"获取或保存cookies时发生错误: {e}")
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__":
app = AutoLoginApp()
try:
@@ -111,6 +62,3 @@ if __name__ == "__main__":
logger.info("程序被用户中断")
except Exception as e:
logger.error(f"程序运行时发生错误: {e}")
finally:
# 确保关闭浏览器
app.login_manager.close()
+7 -7
View File
@@ -8,7 +8,7 @@
- 从配置文件读取网站地址和对应的cookies信息
- 按顺序登录每个网站
- 登录成功后保持登录状态指定时间(可配置)
- 登录成功后依次打开多个网页(支持全局duration配置)
- 保存更新后的cookies信息到配置文件
- 关闭网站浏览器实例
- 完整的日志记录功能
@@ -60,7 +60,7 @@ autoLogon/
- `__init__()`: 初始化登录管理器
- `login(site_url, cookies=None)`: 登录网站,支持设置cookies
- `maintain_login(duration=120)`: 保持登录状态指定时间
- `get_cookies()`: 获取当前页面的cookies
- `save_cookies()`: 获取所有页面的cookies
- `close()`: 关闭浏览器页面
### 5.3 日志配置器 (logger_config.py)
@@ -96,13 +96,13 @@ autoLogon/
```json
{
"duration": 30,
"browser_path": "C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe",
"sites": [
{
"name": "网站名称",
"url": "网站URL",
"duration": 30,
"cookies": [
"name": "网站名称",
"url": "网站URL",
"cookies": [
{
"name": "cookie名称",
"value": "cookie值",
@@ -119,11 +119,11 @@ autoLogon/
```
**配置项说明**
- `duration`: 处理每个网站时的延时时间(秒),默认30秒
- `browser_path`: 浏览器可执行文件路径(可选)
- `sites`: 网站配置列表
- `name`: 网站名称
- `url`: 网站URL
- `duration`: 保持登录状态的时间(秒),默认30秒
- `cookies`: cookies列表,每个cookie包含完整信息(name, value, domain等)
```