完善 cookie 监控系统:
1. 修复 browser_login.py 中的 cookie 处理错误 2. 重构 cookie_cloud.py 实现浏览器实例级 cookie 共享 3. 创建 cookie_extractor.py GUI 应用程序,支持打开、保存和恢复功能 4. 优化 cookie 导入方式,支持批量导入和回退机制 5. 修复 localStorage 注入问题,确保特殊字符正确处理
This commit is contained in:
+233
-65
@@ -71,29 +71,34 @@ class BrowserLogin:
|
||||
parsed = urlparse(url)
|
||||
return parsed.netloc
|
||||
|
||||
def _get_cookies_dict(self) -> Dict[str, List[Dict]]:
|
||||
def get_all_cookies(self) -> List[Dict]:
|
||||
"""
|
||||
获取当前浏览器的所有 cookie,按域名分组
|
||||
获取当前浏览器实例的所有 cookie
|
||||
|
||||
Returns:
|
||||
按域名分组的 cookie 字典
|
||||
所有 cookie 的列表
|
||||
"""
|
||||
cookies = self.tab.cookies.as_dict
|
||||
# 先获取 cookies 对象,然后调用 as_dict 方法
|
||||
cookies_obj = self.tab.cookies
|
||||
cookies = cookies_obj() if callable(cookies_obj) else cookies_obj
|
||||
cookies = cookies.as_dict() if hasattr(cookies, 'as_dict') else cookies
|
||||
|
||||
# 按域名分组
|
||||
cookie_dict = {}
|
||||
# 确保返回完整的 cookie 列表,包含所有属性
|
||||
formatted_cookies = []
|
||||
for cookie in cookies:
|
||||
domain = cookie.get('domain', '')
|
||||
# 统一域名格式(去掉开头的点)
|
||||
if domain.startswith('.'):
|
||||
domain = domain[1:]
|
||||
|
||||
if domain not in cookie_dict:
|
||||
cookie_dict[domain] = []
|
||||
|
||||
cookie_dict[domain].append(cookie)
|
||||
formatted_cookie = {
|
||||
'name': cookie.get('name', ''),
|
||||
'value': cookie.get('value', ''),
|
||||
'domain': cookie.get('domain', ''),
|
||||
'path': cookie.get('path', '/'),
|
||||
'expires': cookie.get('expirationDate', None),
|
||||
'secure': cookie.get('secure', False),
|
||||
'httpOnly': cookie.get('httpOnly', False),
|
||||
'sameSite': cookie.get('sameSite', 'Lax')
|
||||
}
|
||||
formatted_cookies.append(formatted_cookie)
|
||||
|
||||
return cookie_dict
|
||||
return formatted_cookies
|
||||
|
||||
def check_login(self, url: str, check_selector: str, success_text: str) -> bool:
|
||||
"""
|
||||
@@ -146,8 +151,11 @@ class BrowserLogin:
|
||||
fail_count = 0
|
||||
|
||||
logger.info(f"开始导入 {len(cookies)} 个 cookie")
|
||||
for cookie in cookies:
|
||||
try:
|
||||
|
||||
try:
|
||||
# 准备所有 cookie 数据
|
||||
formatted_cookies = []
|
||||
for cookie in cookies:
|
||||
cookie_dict = {
|
||||
'name': cookie.get('name', ''),
|
||||
'value': cookie.get('value', ''),
|
||||
@@ -160,11 +168,23 @@ class BrowserLogin:
|
||||
cookie_dict['httpOnly'] = True
|
||||
if cookie.get('sameSite'):
|
||||
cookie_dict['sameSite'] = cookie.get('sameSite')
|
||||
|
||||
self.tab.set.cookies(cookie_dict)
|
||||
success_count += 1
|
||||
except Exception as e:
|
||||
fail_count += 1
|
||||
formatted_cookies.append(cookie_dict)
|
||||
|
||||
# 根据 DrissionPage 文档,set.cookies() 方法可以直接接收列表格式的多个 cookie
|
||||
logger.info("使用一次性注入方式导入 cookie")
|
||||
self.tab.set.cookies(formatted_cookies)
|
||||
success_count = len(formatted_cookies)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"导入 cookie 失败: {e}")
|
||||
# 回退到逐个注入
|
||||
logger.info("回退到逐个注入方式导入 cookie")
|
||||
for cookie_dict in formatted_cookies:
|
||||
try:
|
||||
self.tab.set.cookies(cookie_dict)
|
||||
success_count += 1
|
||||
except Exception as e:
|
||||
fail_count += 1
|
||||
|
||||
logger.info(f"Cookie 导入完成: 成功 {success_count}, 失败 {fail_count}")
|
||||
return fail_count == 0
|
||||
@@ -309,9 +329,19 @@ class BrowserLogin:
|
||||
time.sleep(3)
|
||||
|
||||
# 获取 cookies
|
||||
cookies = self.tab.cookies.as_dict
|
||||
cookies_obj = self.browser.cookies
|
||||
cookies = cookies_obj() if callable(cookies_obj) else cookies_obj
|
||||
cookies = cookies.as_dict() if hasattr(cookies, 'as_dict') else cookies
|
||||
|
||||
return cookies
|
||||
# 确保返回的是列表
|
||||
if isinstance(cookies, list):
|
||||
return cookies
|
||||
elif isinstance(cookies, dict):
|
||||
# 如果是字典,转换为列表
|
||||
return [cookies]
|
||||
else:
|
||||
# 如果是其他类型,返回空列表
|
||||
return []
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"刷新页面并保存 cookie 失败: {e}")
|
||||
@@ -323,7 +353,7 @@ class BrowserLogin:
|
||||
|
||||
|
||||
class CookieManager:
|
||||
"""Cookie 管理器"""
|
||||
"""Cookie 管理器 - 实现浏览器实例级别的 Cookie 共享"""
|
||||
|
||||
def __init__(self, cookie_file: str):
|
||||
"""
|
||||
@@ -335,14 +365,25 @@ class CookieManager:
|
||||
self.cookie_file = cookie_file
|
||||
self.cookies = self._load_cookies()
|
||||
|
||||
def _load_cookies(self) -> Dict[str, Dict[str, List[Dict]]]:
|
||||
def _load_cookies(self) -> Dict[str, List[Dict]]:
|
||||
"""加载 cookie 文件"""
|
||||
import os
|
||||
|
||||
if os.path.exists(self.cookie_file):
|
||||
try:
|
||||
with open(self.cookie_file, 'r', encoding='utf-8') as f:
|
||||
return json.load(f)
|
||||
data = json.load(f)
|
||||
# 兼容旧格式
|
||||
if isinstance(data, dict) and any(isinstance(v, dict) for v in data.values()):
|
||||
# 转换旧格式为新格式
|
||||
new_data = {}
|
||||
for user, websites in data.items():
|
||||
all_cookies = []
|
||||
for site_cookies in websites.values():
|
||||
all_cookies.extend(site_cookies)
|
||||
new_data[user] = all_cookies
|
||||
return new_data
|
||||
return data
|
||||
except Exception as e:
|
||||
logger.error(f"加载 cookie 文件失败: {e}")
|
||||
|
||||
@@ -356,76 +397,203 @@ class CookieManager:
|
||||
except Exception as e:
|
||||
logger.error(f"保存 cookie 文件失败: {e}")
|
||||
|
||||
def get_cookies(self, user_name: str, website_name: str) -> List[Dict]:
|
||||
def get_all_cookies(self, user_name: str) -> List[Dict]:
|
||||
"""
|
||||
获取指定用户和网站的 cookie
|
||||
获取指定用户的所有 cookie
|
||||
|
||||
Args:
|
||||
user_name: 用户名
|
||||
website_name: 网站名
|
||||
|
||||
Returns:
|
||||
cookie 列表
|
||||
"""
|
||||
if user_name not in self.cookies:
|
||||
return []
|
||||
|
||||
if website_name not in self.cookies[user_name]:
|
||||
return []
|
||||
|
||||
return self.cookies[user_name][website_name]
|
||||
return self.cookies.get(user_name, [])
|
||||
|
||||
def save_cookies(self, user_name: str, website_name: str, cookies: List[Dict]):
|
||||
def save_all_cookies(self, user_name: str, cookies: List[Dict]):
|
||||
"""
|
||||
保存指定用户和网站的 cookie
|
||||
保存指定用户的所有 cookie
|
||||
|
||||
Args:
|
||||
user_name: 用户名
|
||||
website_name: 网站名
|
||||
cookies: cookie 列表
|
||||
"""
|
||||
if user_name not in self.cookies:
|
||||
self.cookies[user_name] = {}
|
||||
|
||||
self.cookies[user_name][website_name] = cookies
|
||||
self.cookies[user_name] = cookies
|
||||
self._save_cookies()
|
||||
|
||||
def get_cookies_for_domain(self, domain: str) -> List[Dict]:
|
||||
def save_cookies(self, user_name: str, website_name: str, cookies: List[Dict]):
|
||||
"""
|
||||
获取指定域名的 cookie(从所有用户的 cookie 中查找)
|
||||
保存指定用户和网站的 cookie(兼容旧接口)
|
||||
|
||||
Args:
|
||||
user_name: 用户名
|
||||
website_name: 网站名(未使用,保持兼容)
|
||||
cookies: cookie 列表
|
||||
"""
|
||||
# 保持向后兼容,将网站的cookie添加到用户的所有cookie中
|
||||
if user_name not in self.cookies:
|
||||
self.cookies[user_name] = []
|
||||
|
||||
# 合并新cookie到现有cookie中
|
||||
existing_cookies = self.cookies[user_name]
|
||||
|
||||
# 创建现有cookie的索引,用于快速查找
|
||||
cookie_index = {}
|
||||
for i, cookie in enumerate(existing_cookies):
|
||||
if isinstance(cookie, dict):
|
||||
key = (cookie.get('name'), cookie.get('domain'), cookie.get('path'))
|
||||
cookie_index[key] = i
|
||||
|
||||
# 添加或更新cookie
|
||||
if isinstance(cookies, list):
|
||||
valid_cookies = []
|
||||
for cookie in cookies:
|
||||
if isinstance(cookie, dict):
|
||||
valid_cookies.append(cookie)
|
||||
|
||||
for cookie in valid_cookies:
|
||||
key = (cookie.get('name'), cookie.get('domain'), cookie.get('path'))
|
||||
if key in cookie_index:
|
||||
# 更新现有cookie
|
||||
existing_cookies[cookie_index[key]] = cookie
|
||||
else:
|
||||
# 添加新cookie
|
||||
existing_cookies.append(cookie)
|
||||
|
||||
if valid_cookies:
|
||||
logger.info(f" 已保存 {len(valid_cookies)} 个新 cookie")
|
||||
|
||||
self._save_cookies()
|
||||
|
||||
def add_cookie(self, user_name: str, cookie: Dict):
|
||||
"""
|
||||
为指定用户添加单个 cookie
|
||||
|
||||
Args:
|
||||
user_name: 用户名
|
||||
cookie: cookie 字典
|
||||
"""
|
||||
if user_name not in self.cookies:
|
||||
self.cookies[user_name] = []
|
||||
|
||||
# 检查是否已存在相同的 cookie
|
||||
existing_index = -1
|
||||
for i, existing_cookie in enumerate(self.cookies[user_name]):
|
||||
if (existing_cookie.get('name') == cookie.get('name') and
|
||||
existing_cookie.get('domain') == cookie.get('domain') and
|
||||
existing_cookie.get('path') == cookie.get('path')):
|
||||
existing_index = i
|
||||
break
|
||||
|
||||
if existing_index >= 0:
|
||||
# 更新现有 cookie
|
||||
self.cookies[user_name][existing_index] = cookie
|
||||
else:
|
||||
# 添加新 cookie
|
||||
self.cookies[user_name].append(cookie)
|
||||
|
||||
self._save_cookies()
|
||||
|
||||
def remove_cookie(self, user_name: str, cookie_name: str, domain: str):
|
||||
"""
|
||||
从指定用户中移除指定的 cookie
|
||||
|
||||
Args:
|
||||
user_name: 用户名
|
||||
cookie_name: cookie 名称
|
||||
domain: cookie 域名
|
||||
"""
|
||||
if user_name in self.cookies:
|
||||
self.cookies[user_name] = [
|
||||
cookie for cookie in self.cookies[user_name]
|
||||
if not (cookie.get('name') == cookie_name and cookie.get('domain') == domain)
|
||||
]
|
||||
self._save_cookies()
|
||||
|
||||
def clear_cookies(self, user_name: str):
|
||||
"""
|
||||
清空指定用户的所有 cookie
|
||||
|
||||
Args:
|
||||
user_name: 用户名
|
||||
"""
|
||||
if user_name in self.cookies:
|
||||
self.cookies[user_name] = []
|
||||
self._save_cookies()
|
||||
|
||||
def get_cookies_for_domain(self, user_name: str, domain: str) -> List[Dict]:
|
||||
"""
|
||||
获取指定用户的指定域名的 cookie
|
||||
|
||||
Args:
|
||||
user_name: 用户名
|
||||
domain: 域名
|
||||
|
||||
Returns:
|
||||
cookie 列表
|
||||
"""
|
||||
all_cookies = []
|
||||
user_cookies = self.get_all_cookies(user_name)
|
||||
filtered_cookies = []
|
||||
|
||||
for user_name, websites in self.cookies.items():
|
||||
for website_name, cookies in websites.items():
|
||||
for cookie in cookies:
|
||||
cookie_domain = cookie.get('domain', '')
|
||||
# 统一域名格式
|
||||
if cookie_domain.startswith('.'):
|
||||
cookie_domain = cookie_domain[1:]
|
||||
|
||||
if domain in cookie_domain or cookie_domain in domain:
|
||||
all_cookies.append(cookie)
|
||||
for cookie in user_cookies:
|
||||
cookie_domain = cookie.get('domain', '')
|
||||
# 统一域名格式
|
||||
if cookie_domain.startswith('.'):
|
||||
cookie_domain = cookie_domain[1:]
|
||||
|
||||
if domain in cookie_domain or cookie_domain in domain:
|
||||
filtered_cookies.append(cookie)
|
||||
|
||||
return all_cookies
|
||||
return filtered_cookies
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# 初始化日志系统
|
||||
from applogger import setup_logging
|
||||
setup_logging()
|
||||
|
||||
# 测试代码
|
||||
manager = CookieManager("cookies.json")
|
||||
|
||||
# 保存测试 cookie
|
||||
# 测试1: 保存所有 cookie
|
||||
test_cookies = [
|
||||
{"name": "test", "value": "123", "domain": "example.com", "path": "/"}
|
||||
{"name": "test", "value": "123", "domain": "example.com", "path": "/"},
|
||||
{"name": "session", "value": "abc123", "domain": "example.com", "path": "/", "httpOnly": True},
|
||||
{"name": "user", "value": "testuser", "domain": "example.org", "path": "/"}
|
||||
]
|
||||
manager.save_cookies("测试用户", "测试网站", test_cookies)
|
||||
manager.save_all_cookies("测试用户", test_cookies)
|
||||
logger.info("测试1: 保存所有 cookie 完成")
|
||||
|
||||
# 获取 cookie
|
||||
cookies = manager.get_cookies("测试用户", "测试网站")
|
||||
logger.info(f"获取到的 cookie: {cookies}")
|
||||
# 测试2: 获取所有 cookie
|
||||
all_cookies = manager.get_all_cookies("测试用户")
|
||||
logger.info(f"测试2: 获取到 {len(all_cookies)} 个 cookie")
|
||||
|
||||
# 测试3: 添加单个 cookie
|
||||
new_cookie = {"name": "token", "value": "xyz789", "domain": "example.com", "path": "/", "secure": True}
|
||||
manager.add_cookie("测试用户", new_cookie)
|
||||
logger.info("测试3: 添加单个 cookie 完成")
|
||||
|
||||
# 测试4: 获取更新后的所有 cookie
|
||||
updated_cookies = manager.get_all_cookies("测试用户")
|
||||
logger.info(f"测试4: 获取到 {len(updated_cookies)} 个 cookie")
|
||||
|
||||
# 测试5: 获取指定域名的 cookie
|
||||
example_cookies = manager.get_cookies_for_domain("测试用户", "example.com")
|
||||
logger.info(f"测试5: 获取到 {len(example_cookies)} 个 example.com 的 cookie")
|
||||
|
||||
# 测试6: 移除指定 cookie
|
||||
manager.remove_cookie("测试用户", "test", "example.com")
|
||||
logger.info("测试6: 移除指定 cookie 完成")
|
||||
|
||||
# 测试7: 检查移除后的 cookie
|
||||
final_cookies = manager.get_all_cookies("测试用户")
|
||||
logger.info(f"测试7: 移除后剩余 {len(final_cookies)} 个 cookie")
|
||||
|
||||
# 测试8: 清空所有 cookie
|
||||
manager.clear_cookies("测试用户")
|
||||
logger.info("测试8: 清空所有 cookie 完成")
|
||||
|
||||
# 测试9: 检查清空后的 cookie
|
||||
empty_cookies = manager.get_all_cookies("测试用户")
|
||||
logger.info(f"测试9: 清空后剩余 {len(empty_cookies)} 个 cookie")
|
||||
|
||||
logger.info("所有测试完成!")
|
||||
Reference in New Issue
Block a user