home1018
This commit is contained in:
@@ -0,0 +1,123 @@
|
||||
# autoLogon - 自动登录应用
|
||||
|
||||
## 项目简介
|
||||
|
||||
autoLogon是一个自动化网站登录工具,可以帮助用户自动登录多个网站并维护登录状态。该应用通过配置文件管理网站信息,支持使用cookies维持登录会话,并能自动保存和更新cookies。
|
||||
|
||||
## 主要功能
|
||||
|
||||
- 自动登录配置的多个网站
|
||||
- 支持cookies管理,自动保存和更新会话信息
|
||||
- 可自定义每个网站的保持登录时间
|
||||
- 支持自定义浏览器路径
|
||||
- 完整的日志记录系统
|
||||
- 配置文件热更新
|
||||
|
||||
## 项目结构
|
||||
|
||||
```
|
||||
autoLogon/
|
||||
├── main.py # 应用主入口
|
||||
├── login_manager.py # 登录管理器
|
||||
├── config.py # 配置管理器
|
||||
├── logger_config.py # 日志配置
|
||||
├── config.json # 配置文件
|
||||
├── requirements.txt # 依赖列表
|
||||
└── log/ # 日志文件夹
|
||||
```
|
||||
|
||||
## 核心文件说明
|
||||
|
||||
### main.py
|
||||
应用的主入口文件,包含`AutoLoginApp`类,负责初始化各个管理器并协调它们的工作流程,包括加载配置、处理网站列表、执行登录操作等。
|
||||
|
||||
### login_manager.py
|
||||
登录管理器,封装了与浏览器相关的操作,负责网站登录、cookies设置与获取、保持登录状态等功能。使用DrissionPage库实现浏览器自动化。
|
||||
|
||||
### config.py
|
||||
配置管理器,负责加载、保存和管理应用配置,包括网站列表、浏览器路径等信息。
|
||||
|
||||
### logger_config.py
|
||||
日志配置模块,设置全局日志记录器,支持文件和控制台双重输出。
|
||||
|
||||
### config.json
|
||||
应用配置文件,使用JSON格式存储,包含以下主要配置项:
|
||||
- `sites`:网站配置列表
|
||||
- `browser_path`:浏览器可执行文件路径
|
||||
|
||||
## 配置文件格式
|
||||
|
||||
```json
|
||||
{
|
||||
"browser_path": "C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe",
|
||||
"sites": [
|
||||
{
|
||||
"name": "网站名称",
|
||||
"url": "网站URL",
|
||||
"duration": 30,
|
||||
"cookies": [
|
||||
{
|
||||
"name": "cookie名称",
|
||||
"value": "cookie值",
|
||||
"domain": "域名",
|
||||
"path": "/",
|
||||
"expires": 0,
|
||||
"secure": false,
|
||||
"httpOnly": false
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## 安装依赖
|
||||
|
||||
使用以下命令安装项目依赖:
|
||||
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
## 使用方法
|
||||
|
||||
1. 确保已安装所有依赖
|
||||
2. 配置`config.json`文件,添加需要自动登录的网站信息
|
||||
3. 运行主程序:
|
||||
|
||||
```bash
|
||||
python main.py
|
||||
```
|
||||
|
||||
## 工作流程
|
||||
|
||||
1. 加载配置文件
|
||||
2. 遍历处理每个网站:
|
||||
- 从配置中获取cookies(如果有)
|
||||
- 启动浏览器并访问网站
|
||||
- 设置cookies(如果提供)
|
||||
- 保持登录状态指定时间
|
||||
- 获取并更新最新cookies到配置
|
||||
- 关闭当前浏览器页面
|
||||
3. 保存更新后的配置
|
||||
4. 关闭浏览器
|
||||
|
||||
## 注意事项
|
||||
|
||||
- 确保浏览器路径正确配置,默认使用Edge浏览器
|
||||
- 配置文件中的`duration`参数表示网站打开后保持的时间(秒)
|
||||
- 首次运行时,应用会为没有cookies的网站创建空的cookies配置
|
||||
- 日志文件保存在`log`目录下,按日期命名
|
||||
|
||||
## 故障排除
|
||||
|
||||
- 如遇浏览器启动失败,请检查`browser_path`配置是否正确
|
||||
- 如遇登录失败,检查网站URL是否正确,以及cookies格式是否符合要求
|
||||
- 查看日志文件了解详细的错误信息
|
||||
|
||||
## 扩展建议
|
||||
|
||||
- 增加对更多浏览器的支持
|
||||
- 添加自动识别登录状态的功能
|
||||
- 实现定时任务功能,定期执行自动登录
|
||||
- 添加图形用户界面进行配置管理
|
||||
+267
@@ -0,0 +1,267 @@
|
||||
{
|
||||
"browser_path": "C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe",
|
||||
"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",
|
||||
"domain": ".baidu.com",
|
||||
"path": "/",
|
||||
"expires": 0,
|
||||
"secure": false,
|
||||
"httpOnly": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "新浪",
|
||||
"url": "https://www.sina.com.cn",
|
||||
"duration": 30,
|
||||
"cookies": [
|
||||
{
|
||||
"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",
|
||||
"domain": ".www.sina.com.cn",
|
||||
"path": "/",
|
||||
"expires": 0,
|
||||
"secure": false,
|
||||
"httpOnly": false
|
||||
},
|
||||
{
|
||||
"name": "Hm_lvt_90c40f528e0b2106bc03da5aadec190f",
|
||||
"value": "1760759893,1760768784,1760769195",
|
||||
"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": "示例网站1",
|
||||
"url": "https://lmkbi.95155.com/bi-system/#/login",
|
||||
"duration": 30,
|
||||
"cookies": [
|
||||
{
|
||||
"name": "userType",
|
||||
"value": "2",
|
||||
"domain": "lmkbi.95155.com",
|
||||
"path": "/",
|
||||
"expires": 0,
|
||||
"secure": false,
|
||||
"httpOnly": false
|
||||
},
|
||||
{
|
||||
"name": "user",
|
||||
"value": "13548569666",
|
||||
"domain": "lmkbi.95155.com",
|
||||
"path": "/",
|
||||
"expires": 0,
|
||||
"secure": false,
|
||||
"httpOnly": false
|
||||
},
|
||||
{
|
||||
"name": "edentoken",
|
||||
"value": "13548569666",
|
||||
"domain": "lmkbi.95155.com",
|
||||
"path": "/",
|
||||
"expires": 0,
|
||||
"secure": false,
|
||||
"httpOnly": false
|
||||
},
|
||||
{
|
||||
"name": "LMKBIUSS",
|
||||
"value": "eyJhY2NvdW50SWQiOjIzMzk0OTQ3NTAwMjA0OSwicGhvbmUiOiIxMzU0ODU2OTY2NiIsInRpbWVzdGFtcCI6MTc2MDY4Mzg5MzIwOCwidG9rZW4iOiJlMmJhMTE1Yi05YmZiLTQ5MDYtYTA0OS1iMzlmMjkwYTFmODUifQ==",
|
||||
"domain": "lmkbi.95155.com",
|
||||
"path": "/",
|
||||
"expires": 0,
|
||||
"secure": false,
|
||||
"httpOnly": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "示例网站2",
|
||||
"url": "http://192.168.100.1/",
|
||||
"duration": 30,
|
||||
"cookies": [
|
||||
{
|
||||
"name": "sysauth_http",
|
||||
"value": "b05d9c8feea4a5a931e8ef687ac119de",
|
||||
"domain": "192.168.100.1",
|
||||
"path": "/",
|
||||
"expires": 0,
|
||||
"secure": false,
|
||||
"httpOnly": false
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
import json
|
||||
import os
|
||||
from typing import Dict, List, Optional
|
||||
from logger_config import global_logger as logger
|
||||
|
||||
class ConfigManager:
|
||||
"""
|
||||
配置管理器,负责加载、保存和管理应用配置
|
||||
"""
|
||||
|
||||
def __init__(self, config_file: str = 'config.json'):
|
||||
"""
|
||||
初始化配置管理器
|
||||
|
||||
Args:
|
||||
config_file (str): 配置文件路径
|
||||
"""
|
||||
self.config_file = config_file
|
||||
self.default_config = {
|
||||
'sites': []
|
||||
}
|
||||
|
||||
def load_config(self) -> Dict:
|
||||
"""
|
||||
加载配置文件
|
||||
|
||||
Returns:
|
||||
dict: 配置数据
|
||||
"""
|
||||
try:
|
||||
if os.path.exists(self.config_file):
|
||||
with open(self.config_file, 'r', encoding='utf-8') as f:
|
||||
config = json.load(f)
|
||||
logger.info(f"成功加载配置文件: {self.config_file}")
|
||||
return config
|
||||
else:
|
||||
logger.warning(f"配置文件不存在: {self.config_file}")
|
||||
logger.info("使用默认配置")
|
||||
return self.default_config.copy()
|
||||
except json.JSONDecodeError as e:
|
||||
logger.error(f"配置文件格式错误: {e}")
|
||||
logger.info("使用默认配置")
|
||||
return self.default_config.copy()
|
||||
except Exception as e:
|
||||
logger.error(f"加载配置文件时发生错误: {e}")
|
||||
logger.info("使用默认配置")
|
||||
return self.default_config.copy()
|
||||
|
||||
def save_config(self, config: Dict) -> bool:
|
||||
"""
|
||||
保存配置到文件
|
||||
|
||||
Args:
|
||||
config (dict): 要保存的配置数据
|
||||
|
||||
Returns:
|
||||
bool: 保存是否成功
|
||||
"""
|
||||
try:
|
||||
# 确保配置中包含必要的键
|
||||
if 'sites' not in config:
|
||||
config['sites'] = []
|
||||
|
||||
with open(self.config_file, 'w', encoding='utf-8') as f:
|
||||
json.dump(config, f, ensure_ascii=False, indent=2)
|
||||
|
||||
logger.info(f"成功保存配置到文件: {self.config_file}")
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"保存配置文件时发生错误: {e}")
|
||||
return False
|
||||
|
||||
def get_sites(self) -> List[Dict]:
|
||||
"""
|
||||
获取所有网站配置
|
||||
|
||||
Returns:
|
||||
list: 网站配置列表
|
||||
"""
|
||||
config = self.load_config()
|
||||
sites = config.get('sites', [])
|
||||
logger.info(f"获取到 {len(sites)} 个网站配置")
|
||||
return sites
|
||||
|
||||
def add_site(self, site_config: Dict) -> bool:
|
||||
"""
|
||||
添加网站配置
|
||||
|
||||
Args:
|
||||
site_config (dict): 网站配置
|
||||
|
||||
Returns:
|
||||
bool: 添加是否成功
|
||||
"""
|
||||
if not isinstance(site_config, dict) or 'name' not in site_config or 'url' not in site_config:
|
||||
logger.error("网站配置格式错误,必须包含name和url字段")
|
||||
return False
|
||||
|
||||
config = self.load_config()
|
||||
sites = config.get('sites', [])
|
||||
|
||||
# 检查是否已存在同名网站
|
||||
for site in sites:
|
||||
if site.get('name') == site_config.get('name'):
|
||||
logger.warning(f"网站 '{site_config.get('name')}' 已存在")
|
||||
return False
|
||||
|
||||
sites.append(site_config)
|
||||
config['sites'] = sites
|
||||
return self.save_config(config)
|
||||
|
||||
def update_site(self, site_name: str, updated_config: Dict) -> bool:
|
||||
"""
|
||||
更新网站配置
|
||||
|
||||
Args:
|
||||
site_name (str): 网站名称
|
||||
updated_config (dict): 更新后的配置
|
||||
|
||||
Returns:
|
||||
bool: 更新是否成功
|
||||
"""
|
||||
config = self.load_config()
|
||||
sites = config.get('sites', [])
|
||||
|
||||
for i, site in enumerate(sites):
|
||||
if site.get('name') == site_name:
|
||||
# 保留原有的name和url,只更新其他字段
|
||||
updated_site = site.copy()
|
||||
updated_site.update(updated_config)
|
||||
sites[i] = updated_site
|
||||
config['sites'] = sites
|
||||
return self.save_config(config)
|
||||
|
||||
logger.warning(f"找不到名称为 '{site_name}' 的网站配置")
|
||||
return False
|
||||
|
||||
def delete_site(self, site_name: str) -> bool:
|
||||
"""
|
||||
删除网站配置
|
||||
|
||||
Args:
|
||||
site_name (str): 网站名称
|
||||
|
||||
Returns:
|
||||
bool: 删除是否成功
|
||||
"""
|
||||
config = self.load_config()
|
||||
sites = config.get('sites', [])
|
||||
|
||||
new_sites = [site for site in sites if site.get('name') != site_name]
|
||||
|
||||
if len(new_sites) == len(sites):
|
||||
logger.warning(f"找不到名称为 '{site_name}' 的网站配置")
|
||||
return False
|
||||
|
||||
config['sites'] = new_sites
|
||||
return self.save_config(config)
|
||||
@@ -0,0 +1,61 @@
|
||||
import logging
|
||||
import os
|
||||
from datetime import datetime
|
||||
|
||||
class LoggerConfig:
|
||||
"""
|
||||
日志配置类,用于初始化和配置logging
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def setup_logger(log_level=logging.INFO):
|
||||
"""
|
||||
设置日志记录器
|
||||
|
||||
Args:
|
||||
log_level: 日志级别,默认为INFO
|
||||
|
||||
Returns:
|
||||
logger: 配置好的logger对象
|
||||
"""
|
||||
# 创建log目录
|
||||
log_dir = 'log'
|
||||
if not os.path.exists(log_dir):
|
||||
os.makedirs(log_dir)
|
||||
|
||||
# 生成日志文件名,使用当前日期
|
||||
current_date = datetime.now().strftime('%Y-%m-%d')
|
||||
log_file = os.path.join(log_dir, f'autologon_{current_date}.log')
|
||||
|
||||
# 创建logger
|
||||
logger = logging.getLogger('autoLogon')
|
||||
logger.setLevel(log_level)
|
||||
|
||||
# 清除已有的handler
|
||||
if logger.handlers:
|
||||
for handler in logger.handlers:
|
||||
logger.removeHandler(handler)
|
||||
|
||||
# 创建文件handler
|
||||
file_handler = logging.FileHandler(log_file, encoding='utf-8')
|
||||
file_handler.setLevel(log_level)
|
||||
|
||||
# 创建控制台handler
|
||||
console_handler = logging.StreamHandler()
|
||||
console_handler.setLevel(log_level)
|
||||
|
||||
# 定义日志格式
|
||||
formatter = logging.Formatter(
|
||||
'%(asctime)s - %(name)s - %(levelname)s - %(filename)s:%(lineno)d - %(message)s'
|
||||
)
|
||||
file_handler.setFormatter(formatter)
|
||||
console_handler.setFormatter(formatter)
|
||||
|
||||
# 添加handler到logger
|
||||
logger.addHandler(file_handler)
|
||||
logger.addHandler(console_handler)
|
||||
|
||||
return logger
|
||||
|
||||
# 创建全局logger实例
|
||||
global_logger = LoggerConfig.setup_logger()
|
||||
@@ -0,0 +1,299 @@
|
||||
import os
|
||||
import time
|
||||
from typing import Dict, List, Any
|
||||
from DrissionPage import ChromiumPage, ChromiumOptions
|
||||
from logger_config import global_logger as logger
|
||||
from config import ConfigManager
|
||||
|
||||
|
||||
class LoginManager:
|
||||
"""
|
||||
登录管理器,负责处理网站登录和会话维护
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
"""
|
||||
初始化登录管理器
|
||||
"""
|
||||
self.page = None
|
||||
self.config_manager = ConfigManager()
|
||||
|
||||
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}")
|
||||
else:
|
||||
logger.warning(f"Edge浏览器路径不存在: {edge_path}")
|
||||
logger.info("将使用默认配置,让DrissionPage自动查找浏览器")
|
||||
|
||||
# 使用配置创建ChromiumPage实例
|
||||
# 根据文档,在DrissionPage 4.x中,应该使用正确的方式初始化
|
||||
self.page = ChromiumPage(co)
|
||||
|
||||
# 访问网站
|
||||
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
|
||||
})
|
||||
cookies = cookies_list
|
||||
logger.info(f"已将字典格式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")
|
||||
else:
|
||||
logger.error("无法找到设置cookies的有效方法")
|
||||
except Exception as e:
|
||||
logger.error(f"设置cookies失败: {e}")
|
||||
|
||||
# 刷新页面以应用cookies
|
||||
try:
|
||||
self.page.refresh()
|
||||
except Exception as e:
|
||||
logger.warning(f"刷新页面时出错: {e}")
|
||||
|
||||
# 等待页面加载完成
|
||||
time.sleep(2)
|
||||
|
||||
# 验证是否登录成功
|
||||
current_url = self.page.url
|
||||
logger.info(f"当前URL: {current_url}")
|
||||
|
||||
# 如果能成功加载页面,暂时认为登录成功
|
||||
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
|
||||
|
||||
return False
|
||||
|
||||
def maintain_login(self, duration: int = 120) -> None:
|
||||
"""
|
||||
保持登录状态
|
||||
|
||||
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
|
||||
|
||||
Returns:
|
||||
List[Dict[str, Any]]: cookies记录列表
|
||||
"""
|
||||
if not self.page:
|
||||
logger.warning("没有活动的浏览器页面,无法获取cookies")
|
||||
return []
|
||||
|
||||
try:
|
||||
cookies_list = []
|
||||
|
||||
# 根据DrissionPage文档,尝试多种方式获取cookies
|
||||
cookies = None
|
||||
|
||||
# 首先尝试使用标准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},尝试其他方法")
|
||||
|
||||
# 处理获取到的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
|
||||
})
|
||||
|
||||
# 如果还是没有获取到cookies,尝试使用execute_script方法
|
||||
if not cookies_list:
|
||||
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
|
||||
|
||||
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}")
|
||||
|
||||
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("没有活动的浏览器页面需要关闭")
|
||||
@@ -0,0 +1,116 @@
|
||||
import time
|
||||
import json
|
||||
from typing import Dict, List, Any
|
||||
from config import ConfigManager
|
||||
from login_manager import LoginManager
|
||||
|
||||
from logger_config import global_logger as logger
|
||||
|
||||
class AutoLoginApp:
|
||||
"""
|
||||
自动登录应用主类
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
"""
|
||||
初始化自动登录应用
|
||||
"""
|
||||
self.config_manager = ConfigManager()
|
||||
self.login_manager = LoginManager()
|
||||
|
||||
|
||||
def run(self):
|
||||
"""
|
||||
运行自动登录应用
|
||||
"""
|
||||
logger.info("===== 自动登录应用启动 =====")
|
||||
|
||||
# 加载配置
|
||||
config = self.config_manager.load_config()
|
||||
sites = config.get('sites', [])
|
||||
|
||||
if not sites:
|
||||
logger.warning("配置中没有设置任何网站,使用默认示例网站")
|
||||
# 添加默认示例网站
|
||||
sites = [
|
||||
{'name': '百度', 'url': 'https://www.baidu.com'},
|
||||
{'name': '新浪', 'url': 'https://www.sina.com.cn'}
|
||||
]
|
||||
|
||||
# 处理每个网站
|
||||
for site in sites:
|
||||
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
|
||||
try:
|
||||
self.config_manager.save_config(config)
|
||||
logger.info("已将更新后的cookies保存到config.json文件中")
|
||||
except Exception as e:
|
||||
logger.error(f"保存配置文件时发生错误: {e}")
|
||||
|
||||
logger.info("===== 所有网站处理完成 =====")
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = AutoLoginApp()
|
||||
try:
|
||||
app.run()
|
||||
except KeyboardInterrupt:
|
||||
logger.info("程序被用户中断")
|
||||
except Exception as e:
|
||||
logger.error(f"程序运行时发生错误: {e}")
|
||||
finally:
|
||||
# 确保关闭浏览器
|
||||
app.login_manager.close()
|
||||
@@ -0,0 +1 @@
|
||||
drissionpage
|
||||
@@ -0,0 +1,132 @@
|
||||
# 自动登录网站并管理Cookies项目开发文档
|
||||
|
||||
## 1. 项目概述
|
||||
|
||||
本项目使用Python语言和DrissionPage库开发一个自动化脚本,用于按顺序登录多个网站,保持登录状态一段时间后保存更新的cookies,并关闭网站。
|
||||
|
||||
## 2. 功能需求
|
||||
|
||||
- 从配置文件读取网站地址和对应的cookies信息
|
||||
- 按顺序登录每个网站
|
||||
- 登录成功后保持登录状态2分钟
|
||||
- 保存更新后的cookies信息
|
||||
- 关闭网站
|
||||
|
||||
## 3. 技术栈
|
||||
|
||||
- Python 3.8+
|
||||
- DrissionPage库
|
||||
- JSON/YAML用于配置文件格式
|
||||
|
||||
## 4. 项目结构
|
||||
|
||||
```
|
||||
autoLogon/
|
||||
├── config.py # 配置文件处理模块
|
||||
├── login_manager.py # 登录管理模块
|
||||
├── cookie_handler.py # Cookies处理模块
|
||||
├── main.py # 主程序入口
|
||||
├── config.json # 配置文件(存储网站和cookies信息)
|
||||
├── requirements.txt # 依赖包列表
|
||||
└── README.md # 项目说明文档
|
||||
```
|
||||
|
||||
## 5. 模块设计
|
||||
|
||||
### 5.1 配置文件处理模块 (config.py)
|
||||
|
||||
**功能**:负责读取和写入配置文件中的网站地址和cookies信息
|
||||
|
||||
**主要函数**:
|
||||
- `load_config(file_path)`: 加载配置文件
|
||||
- `save_config(config_data, file_path)`: 保存配置文件
|
||||
|
||||
### 5.2 Cookies处理模块 (cookie_handler.py)
|
||||
|
||||
**功能**:负责cookies的管理,包括解析、转换和保存
|
||||
|
||||
**主要函数**:
|
||||
- `parse_cookies(cookie_str)`: 解析cookies字符串为字典格式
|
||||
- `cookies_to_str(cookies_dict)`: 将cookies字典转换为字符串
|
||||
- `save_cookies(site_url, cookies, config_manager)`: 保存网站的cookies到配置文件
|
||||
|
||||
### 5.3 登录管理模块 (login_manager.py)
|
||||
|
||||
**功能**:负责网站的登录和维护登录状态
|
||||
|
||||
**主要函数**:
|
||||
- `login_to_site(browser, site_url, cookies)`: 使用提供的cookies登录网站
|
||||
- `verify_login(browser)`: 验证是否登录成功
|
||||
- `maintain_session(browser, duration=120)`: 保持登录状态指定时间
|
||||
|
||||
### 5.4 主程序模块 (main.py)
|
||||
|
||||
**功能**:程序入口,协调各模块工作
|
||||
|
||||
**主要流程**:
|
||||
1. 初始化配置管理器
|
||||
2. 加载配置文件
|
||||
3. 初始化浏览器
|
||||
4. 遍历每个网站配置
|
||||
5. 调用登录管理模块登录网站
|
||||
6. 保持登录状态
|
||||
7. 更新并保存cookies
|
||||
8. 关闭浏览器
|
||||
|
||||
## 6. 配置文件格式 (JSON)
|
||||
|
||||
```json
|
||||
{
|
||||
"sites": [
|
||||
{
|
||||
"name": "网站A",
|
||||
"url": "https://example.com",
|
||||
"cookies": {
|
||||
"cookie1": "value1",
|
||||
"cookie2": "value2"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "网站B",
|
||||
"url": "https://example2.com",
|
||||
"cookies": {
|
||||
"cookie3": "value3",
|
||||
"cookie4": "value4"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## 7. 依赖安装
|
||||
|
||||
```bash
|
||||
pip install drissionpage
|
||||
```
|
||||
|
||||
## 8. 实现步骤
|
||||
|
||||
1. 创建项目目录结构
|
||||
2. 编写配置文件处理模块
|
||||
3. 编写Cookies处理模块
|
||||
4. 编写登录管理模块
|
||||
5. 编写主程序模块
|
||||
6. 创建配置文件示例
|
||||
7. 测试各个模块
|
||||
8. 整合测试
|
||||
|
||||
## 9. 注意事项
|
||||
|
||||
- 确保使用正确的cookies格式
|
||||
- 处理可能的登录失败情况
|
||||
- 考虑网站的反爬机制
|
||||
- 妥善保存配置文件,避免敏感信息泄露
|
||||
- 处理网络异常和超时情况
|
||||
|
||||
## 10. 扩展功能建议
|
||||
|
||||
- 添加日志记录功能
|
||||
- 实现登录失败重试机制
|
||||
- 添加多线程或异步支持以提高效率
|
||||
- 实现定时任务功能
|
||||
- 添加GUI界面便于操作
|
||||
Reference in New Issue
Block a user