408 lines
16 KiB
Python
408 lines
16 KiB
Python
import os
|
|
import time
|
|
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
|
|
|
|
|
|
class LoginManager:
|
|
"""
|
|
登录管理器,负责处理网站登录和会话维护
|
|
"""
|
|
|
|
def __init__(self):
|
|
"""
|
|
初始化登录管理器,创建浏览器对象
|
|
"""
|
|
self.browser = None
|
|
self.config_manager = ConfigManager()
|
|
self.sites = [] # 存储包含网站和页面信息的字典列表
|
|
|
|
# 初始化浏览器对象
|
|
try:
|
|
# 创建浏览器配置对象
|
|
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):
|
|
# 设置浏览器路径
|
|
co.set_browser_path(edge_path)
|
|
logger.info(f"已配置浏览器路径: {edge_path}")
|
|
else:
|
|
logger.warning(f"浏览器路径不存在: {edge_path}")
|
|
logger.info("将使用默认配置,让DrissionPage自动查找浏览器")
|
|
|
|
# 用该配置创建浏览器对象
|
|
self.browser = Chromium(addr_or_opts=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
|
|
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
|
|
})
|
|
logger.info(f"成功获取网站 {site_url} 的 {len(cookies_list)} 条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.warning(f"未能获取网站 {site_name} - {site_url} 的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:
|
|
page = page_info['page']
|
|
if hasattr(page, 'close'):
|
|
page.close()
|
|
logger.info("一个标签页已关闭")
|
|
except Exception as 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:
|
|
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:
|
|
logger.error(f"为网站 '{site_name}' 设置cookies失败: {e}")
|
|
return False
|
|
|
|
def _get_page_cookies(self, page: Any, site_name: str, page_url: Optional[str] = None) -> List[Dict[str, Any]]:
|
|
"""
|
|
获取指定页面的cookies
|
|
|
|
Args:
|
|
page: 页面对象
|
|
site_name: 网站名称
|
|
page_url: 页面URL(可选)
|
|
|
|
Returns:
|
|
List[Dict[str, Any]]: cookies列表
|
|
"""
|
|
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:
|
|
# 从配置文件中读取网站信息
|
|
config = self.config_manager.load_config()
|
|
sites = config.get('sites', [])
|
|
|
|
logger.info(f"获取到 {len(sites)} 个网站配置")
|
|
|
|
if not sites:
|
|
logger.warning("配置文件中没有网站信息")
|
|
return
|
|
|
|
logger.info(f"找到 {len(sites)} 个网站需要登录")
|
|
|
|
# 存储所有创建的标签页
|
|
tabs = []
|
|
|
|
# 为每个网站创建标签页并设置cookies
|
|
for site in sites:
|
|
try:
|
|
site_name = site.get('name', '未命名网站')
|
|
site_url = site.get('url', '')
|
|
site_cookies = site.get('cookies', [])
|
|
|
|
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("已创建并记录所有标签页")
|
|
|
|
except Exception as e:
|
|
logger.error(f"运行自动登录流程时发生错误: {e}") |