diff --git a/__pycache__/applogger.cpython-310.pyc b/__pycache__/applogger.cpython-310.pyc new file mode 100644 index 0000000..7d194fe Binary files /dev/null and b/__pycache__/applogger.cpython-310.pyc differ diff --git a/__pycache__/browser_login.cpython-310.pyc b/__pycache__/browser_login.cpython-310.pyc new file mode 100644 index 0000000..88714ce Binary files /dev/null and b/__pycache__/browser_login.cpython-310.pyc differ diff --git a/__pycache__/cookie_cloud.cpython-310.pyc b/__pycache__/cookie_cloud.cpython-310.pyc new file mode 100644 index 0000000..e7d57d9 Binary files /dev/null and b/__pycache__/cookie_cloud.cpython-310.pyc differ diff --git a/__pycache__/notifier.cpython-310.pyc b/__pycache__/notifier.cpython-310.pyc new file mode 100644 index 0000000..1c37e79 Binary files /dev/null and b/__pycache__/notifier.cpython-310.pyc differ diff --git a/cookiecloud_client/README.md b/cookiecloud_client/README.md new file mode 100644 index 0000000..896ad85 --- /dev/null +++ b/cookiecloud_client/README.md @@ -0,0 +1,248 @@ +# CookieCloud Client + +一个独立的、可复用的CookieCloud服务器客户端Python模块。 + +## 特性 + +- ✅ **完全独立** - 无外部依赖,仅使用Python标准库 +- ✅ **类型安全** - 完整的类型提示支持 +- ✅ **异常处理** - 完善的异常体系 +- ✅ **易于使用** - 简洁的API设计 +- ✅ **可扩展** - 支持自定义配置 +- ✅ **文档完善** - 详细的使用文档和示例 + +## 安装 + +将 `cookiecloud_client` 目录复制到您的项目中即可使用。 + +## 快速开始 + +### 基本使用 + +```python +from cookiecloud_client import CookieCloudClient, CookieConfig + +# 创建配置 +config = CookieConfig( + server="https://cookiecloud.example.com", + username="your_username", + password="your_password" +) + +# 创建客户端 +client = CookieCloudClient(config) + +# 下载所有cookies +result = client.download() + +if result.success: + print(f"下载成功!") + print(f"总域名数: {result.total_domains}") + print(f"总Cookie数: {result.total_cookies}") + print(f"下载耗时: {result.download_time:.2f}秒") + + # 获取指定域名的cookie + cookie_str = result.get_cookie_string("example.com") + print(f"example.com的cookie: {cookie_str}") +else: + print(f"下载失败: {result.error_message}") +``` + +### 下载指定域名的Cookie + +```python +# 下载单个域名 +cookie_str = client.download_for_domain("baidu.com") +if cookie_str: + print(f"baidu.com的cookie: {cookie_str}") + +# 批量下载多个域名 +domains = ["baidu.com", "google.com", "github.com"] +cookies = client.download_for_domains(domains) +for domain, cookie in cookies.items(): + print(f"{domain}: {cookie}") +``` + +### 测试连接 + +```python +success, message = client.test_connection() +if success: + print("连接成功!") +else: + print(f"连接失败: {message}") +``` + +## 高级用法 + +### 自定义配置 + +```python +config = CookieConfig( + server="https://cookiecloud.example.com", + username="your_username", + password="your_password", + timeout=60, # 超时时间(秒) + verify_ssl=False, # 是否验证SSL证书 + ignore_cookies=[ # 忽略的cookie名称 + "CookieAutoDeleteBrowsingDataCleanup", + "CookieAutoDeleteCleaningDiscarded" + ] +) +``` + +### 异常处理 + +```python +from cookiecloud_client import ( + CookieCloudClient, + CookieConfig, + CookieCloudError, + ConnectionError, + AuthenticationError, + DataParseError, + NetworkError +) + +try: + client = CookieCloudClient(config) + result = client.download() + +except AuthenticationError as e: + print(f"认证失败: {e.message}") + print(f"用户名: {e.details.get('username')}") + +except ConnectionError as e: + print(f"连接失败: {e.message}") + print(f"服务器: {e.details.get('server')}") + print(f"状态码: {e.details.get('status_code')}") + +except DataParseError as e: + print(f"数据解析失败: {e.message}") + +except NetworkError as e: + print(f"网络错误: {e.message}") + print(f"原始错误: {e.details.get('original_error')}") + +except CookieCloudError as e: + print(f"CookieCloud错误: {e.message}") +``` + +### 使用Cookie数据对象 + +```python +result = client.download() + +# 获取所有域名 +domains = result.get_domains() +print(f"所有域名: {domains}") + +# 遍历所有cookie +for domain, collection in result.cookies.items(): + print(f"\n域名: {domain}") + for cookie in collection.cookies: + print(f" - {cookie.name}={cookie.value[:20]}...") + print(f" 路径: {cookie.path}") + print(f" 安全: {cookie.secure}") + print(f" HttpOnly: {cookie.http_only}") +``` + +## API文档 + +### CookieConfig + +配置类,用于存储CookieCloud服务器配置。 + +**参数:** +- `server` (str): CookieCloud服务器地址 +- `username` (str): 用户名 +- `password` (str): 密码 +- `timeout` (int): 请求超时时间(秒),默认30 +- `verify_ssl` (bool): 是否验证SSL证书,默认True +- `ignore_cookies` (List[str]): 忽略的cookie名称列表 + +### CookieCloudClient + +客户端类,用于与CookieCloud服务器交互。 + +**方法:** + +#### `download() -> DownloadResult` +下载所有cookies + +**返回:** DownloadResult对象 + +#### `download_for_domain(domain: str) -> Optional[str]` +下载指定域名的cookie字符串 + +**参数:** +- `domain` (str): 目标域名 + +**返回:** cookie字符串或None + +#### `download_for_domains(domains: List[str]) -> Dict[str, Optional[str]]` +批量下载多个域名的cookie字符串 + +**参数:** +- `domains` (List[str]): 目标域名列表 + +**返回:** {domain: cookie_string}字典 + +#### `test_connection() -> Tuple[bool, str]` +测试与CookieCloud服务器的连接 + +**返回:** (是否成功, 消息)元组 + +### DownloadResult + +下载结果类。 + +**属性:** +- `success` (bool): 是否成功 +- `cookies` (Dict[str, CookieCollection]): cookie集合字典 +- `error_message` (str): 错误信息 +- `total_domains` (int): 总域名数 +- `total_cookies` (int): 总cookie数 +- `download_time` (float): 下载耗时(秒) + +**方法:** +- `get_cookie_string(domain: str) -> Optional[str]`: 获取指定域名的cookie字符串 +- `get_domains() -> List[str]`: 获取所有域名列表 + +## 异常类 + +### CookieCloudError +基础异常类,所有其他异常都继承自此类。 + +### ConfigurationError +配置错误异常 + +### ConnectionError +连接错误异常 + +### AuthenticationError +认证错误异常 + +### DataParseError +数据解析错误异常 + +### NetworkError +网络错误异常 + +## 依赖 + +- Python 3.7+ +- 仅使用Python标准库 + +## 许可证 + +MIT License + +## 更新日志 + +### v1.0.0 (2026-03-02) +- 初始版本发布 +- 完整的CookieCloud客户端功能 +- 独立模块,无外部依赖 +- 完善的异常处理 +- 详细的文档和示例 diff --git a/cookiecloud_client/__init__.py b/cookiecloud_client/__init__.py new file mode 100644 index 0000000..9342f33 --- /dev/null +++ b/cookiecloud_client/__init__.py @@ -0,0 +1,33 @@ +""" +CookieCloud客户端模块 +一个独立的、可复用的CookieCloud服务器客户端 + +作者: CookieManager Team +版本: 1.0.0 +许可证: MIT +""" + +from .client import CookieCloudClient +from .exceptions import ( + CookieCloudError, + ConnectionError, + AuthenticationError, + DataParseError, + ConfigurationError, + NetworkError +) +from .models import CookieData, CookieConfig +from .version import __version__ + +__all__ = [ + 'CookieCloudClient', + 'CookieCloudError', + 'ConnectionError', + 'AuthenticationError', + 'DataParseError', + 'ConfigurationError', + 'NetworkError', + 'CookieData', + 'CookieConfig', + '__version__' +] diff --git a/cookiecloud_client/__pycache__/__init__.cpython-310.pyc b/cookiecloud_client/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..bf6e0e0 Binary files /dev/null and b/cookiecloud_client/__pycache__/__init__.cpython-310.pyc differ diff --git a/cookiecloud_client/__pycache__/client.cpython-310.pyc b/cookiecloud_client/__pycache__/client.cpython-310.pyc new file mode 100644 index 0000000..51c4f61 Binary files /dev/null and b/cookiecloud_client/__pycache__/client.cpython-310.pyc differ diff --git a/cookiecloud_client/__pycache__/exceptions.cpython-310.pyc b/cookiecloud_client/__pycache__/exceptions.cpython-310.pyc new file mode 100644 index 0000000..7789ff6 Binary files /dev/null and b/cookiecloud_client/__pycache__/exceptions.cpython-310.pyc differ diff --git a/cookiecloud_client/__pycache__/models.cpython-310.pyc b/cookiecloud_client/__pycache__/models.cpython-310.pyc new file mode 100644 index 0000000..357a5c2 Binary files /dev/null and b/cookiecloud_client/__pycache__/models.cpython-310.pyc differ diff --git a/cookiecloud_client/__pycache__/tests.cpython-310.pyc b/cookiecloud_client/__pycache__/tests.cpython-310.pyc new file mode 100644 index 0000000..a001877 Binary files /dev/null and b/cookiecloud_client/__pycache__/tests.cpython-310.pyc differ diff --git a/cookiecloud_client/__pycache__/version.cpython-310.pyc b/cookiecloud_client/__pycache__/version.cpython-310.pyc new file mode 100644 index 0000000..fa6c679 Binary files /dev/null and b/cookiecloud_client/__pycache__/version.cpython-310.pyc differ diff --git a/cookiecloud_client/client.py b/cookiecloud_client/client.py new file mode 100644 index 0000000..87ba2f1 --- /dev/null +++ b/cookiecloud_client/client.py @@ -0,0 +1,347 @@ +""" +CookieCloud客户端核心实现 +""" +import json +import time +import urllib.request +import urllib.error +from typing import Dict, List, Optional, Tuple +from .models import CookieConfig, CookieData, CookieCollection, DownloadResult +from .exceptions import ( + CookieCloudError, + ConfigurationError, + ConnectionError, + AuthenticationError, + DataParseError, + NetworkError +) + + +class CookieCloudClient: + """ + CookieCloud客户端 + + 用于从CookieCloud服务器下载和管理cookies的独立客户端 + + 示例: + >>> config = CookieConfig( + ... server="https://cookiecloud.example.com", + ... username="your_username", + ... password="your_password" + ... ) + >>> client = CookieCloudClient(config) + >>> result = client.download() + >>> if result.success: + ... print(f"下载成功,共{result.total_domains}个域名") + ... cookie_str = result.get_cookie_string("example.com") + """ + + def __init__(self, config: CookieConfig): + """ + 初始化客户端 + + Args: + config: CookieCloud配置对象 + + Raises: + ConfigurationError: 配置验证失败 + """ + if not isinstance(config, CookieConfig): + raise ConfigurationError("配置参数必须是CookieConfig类型") + + self.config = config + self._last_download_time = None + self._download_count = 0 + + def download(self) -> DownloadResult: + """ + 从CookieCloud服务器下载所有cookies + + Returns: + DownloadResult: 下载结果对象 + + Raises: + ConnectionError: 连接服务器失败 + AuthenticationError: 认证失败 + DataParseError: 数据解析失败 + NetworkError: 网络错误 + """ + start_time = time.time() + + try: + raw_data = self._fetch_data() + cookies = self._parse_cookies(raw_data) + + download_time = time.time() - start_time + self._last_download_time = time.time() + self._download_count += 1 + + total_cookies = sum(len(c.cookies) for c in cookies.values()) + + return DownloadResult( + success=True, + cookies=cookies, + total_domains=len(cookies), + total_cookies=total_cookies, + download_time=download_time + ) + + except CookieCloudError: + raise + except Exception as e: + raise CookieCloudError(f"下载cookies失败: {str(e)}") + + def download_for_domain(self, domain: str) -> Optional[str]: + """ + 下载指定域名的cookie字符串 + + Args: + domain: 目标域名 + + Returns: + Optional[str]: cookie字符串,如果不存在则返回None + + Raises: + ConnectionError: 连接服务器失败 + AuthenticationError: 认证失败 + DataParseError: 数据解析失败 + """ + result = self.download() + return result.get_cookie_string(domain) + + def download_for_domains(self, domains: List[str]) -> Dict[str, Optional[str]]: + """ + 批量下载多个域名的cookie字符串 + + Args: + domains: 目标域名列表 + + Returns: + Dict[str, Optional[str]]: {domain: cookie_string} + """ + result = self.download() + return {domain: result.get_cookie_string(domain) for domain in domains} + + def test_connection(self) -> Tuple[bool, str]: + """ + 测试与CookieCloud服务器的连接 + + Returns: + Tuple[bool, str]: (是否成功, 消息) + """ + try: + self._fetch_data() + return True, "连接成功" + except AuthenticationError as e: + return False, f"认证失败: {e.message}" + except ConnectionError as e: + return False, f"连接失败: {e.message}" + except Exception as e: + return False, f"测试失败: {str(e)}" + + def _fetch_data(self) -> Dict: + """ + 从服务器获取原始数据 + + Returns: + Dict: 原始JSON数据 + + Raises: + ConnectionError: 连接失败 + AuthenticationError: 认证失败 + NetworkError: 网络错误 + """ + url = f"{self.config.server}/get/{self.config.username}" + + try: + data = json.dumps({"password": self.config.password}).encode('utf-8') + + request = urllib.request.Request( + url, + data=data, + headers={ + 'Content-Type': 'application/json', + 'User-Agent': 'CookieCloudClient/1.0' + }, + method='POST' + ) + + response = urllib.request.urlopen( + request, + timeout=self.config.timeout + ) + + if response.status != 200: + if response.status == 401: + raise AuthenticationError( + "认证失败,请检查用户名和密码", + username=self.config.username + ) + elif response.status == 404: + raise ConnectionError( + "用户不存在,请检查用户名", + server=self.config.server, + status_code=response.status + ) + else: + raise ConnectionError( + f"服务器返回错误状态码: {response.status}", + server=self.config.server, + status_code=response.status + ) + + result = json.loads(response.read().decode('utf-8')) + + if not result: + raise DataParseError("服务器返回数据为空") + + return result + + except urllib.error.HTTPError as e: + if e.code == 401: + raise AuthenticationError( + "认证失败,请检查用户名和密码", + username=self.config.username + ) + else: + raise ConnectionError( + f"HTTP错误: {e.code} {e.reason}", + server=self.config.server, + status_code=e.code + ) + except urllib.error.URLError as e: + raise NetworkError( + f"网络连接失败: {e.reason}", + original_error=e + ) + except json.JSONDecodeError as e: + raise DataParseError( + f"JSON解析失败: {str(e)}" + ) + except Exception as e: + if isinstance(e, CookieCloudError): + raise + raise NetworkError( + f"请求失败: {str(e)}", + original_error=e + ) + + def _parse_cookies(self, raw_data: Dict) -> Dict[str, CookieCollection]: + """ + 解析原始cookie数据 + + Args: + raw_data: 原始JSON数据 + + Returns: + Dict[str, CookieCollection]: {domain: CookieCollection} + """ + if raw_data.get("cookie_data"): + contents = raw_data.get("cookie_data") + else: + contents = raw_data + + domain_groups = self._group_by_domain(contents) + + cookies = {} + for domain, cookie_list in domain_groups.items(): + if not cookie_list: + continue + + if self._is_cloudflare_only(cookie_list): + continue + + collection = CookieCollection(domain=domain) + + for cookie_data in cookie_list: + cookie = CookieData( + domain=cookie_data.get('domain', ''), + name=cookie_data.get('name', ''), + value=cookie_data.get('value', ''), + path=cookie_data.get('path', '/'), + secure=cookie_data.get('secure', False), + http_only=cookie_data.get('httpOnly', False) + ) + collection.add_cookie(cookie) + + cookies[domain] = collection + + return cookies + + def _group_by_domain(self, contents: Dict) -> Dict[str, List[Dict]]: + """ + 按域名分组cookies + + Args: + contents: 原始cookie内容 + + Returns: + Dict[str, List[Dict]]: {domain: [cookie_data]} + """ + domain_groups = {} + + for site, cookies in contents.items(): + for cookie in cookies: + domain = cookie.get("domain", "") + if not domain: + continue + + domain_key = self._extract_domain(domain) + if not domain_key: + continue + + if domain_key not in domain_groups: + domain_groups[domain_key] = [] + + domain_groups[domain_key].append(cookie) + + return domain_groups + + def _extract_domain(self, domain: str) -> Optional[str]: + """ + 提取主域名 + + Args: + domain: 原始域名 + + Returns: + Optional[str]: 主域名 + """ + if not domain: + return None + + domain = domain.lstrip('.') + + parts = domain.split('.') + if len(parts) < 2: + return domain + + if len(parts) == 2: + return domain + + return '.'.join(parts[-2:]) + + def _is_cloudflare_only(self, cookie_list: List[Dict]) -> bool: + """ + 检查是否仅包含Cloudflare验证cookie + + Args: + cookie_list: cookie列表 + + Returns: + bool: 是否仅包含cf_clearance + """ + for cookie in cookie_list: + if cookie.get("name") != "cf_clearance": + return False + return True + + @property + def last_download_time(self) -> Optional[float]: + """获取最后下载时间""" + return self._last_download_time + + @property + def download_count(self) -> int: + """获取下载次数""" + return self._download_count diff --git a/cookiecloud_client/examples.py b/cookiecloud_client/examples.py new file mode 100644 index 0000000..9240475 --- /dev/null +++ b/cookiecloud_client/examples.py @@ -0,0 +1,259 @@ +""" +CookieCloud客户端使用示例 +演示如何使用cookiecloud_client模块 +""" +import sys +import os + +# 添加父目录到路径 +sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + +# 示例1: 基本使用 +def example_basic_usage(): + """基本使用示例""" + from cookiecloud_client import CookieCloudClient, CookieConfig + + print("=" * 60) + print("示例1: 基本使用") + print("=" * 60) + + # 创建配置 + config = CookieConfig( + server="https://movie-pilot.org/cookiecloud", + username="hu6n2vcUmzpu7mqUN2rVCg", + password="jtDM5dV9AyqXkZdQVeA9f6" + ) + + # 创建客户端 + client = CookieCloudClient(config) + + # 测试连接 + success, message = client.test_connection() + print(f"连接测试: {message}") + + if success: + # 下载所有cookies + result = client.download() + + if result.success: + print(f"\n✓ 下载成功!") + print(f" 总域名数: {result.total_domains}") + print(f" 总Cookie数: {result.total_cookies}") + print(f" 下载耗时: {result.download_time:.2f}秒") + + # 显示前5个域名 + domains = result.get_domains()[:5] + print(f"\n前5个域名:") + for idx, domain in enumerate(domains, 1): + cookie_str = result.get_cookie_string(domain) + preview = cookie_str[:50] + "..." if len(cookie_str) > 50 else cookie_str + print(f" {idx}. {domain}: {preview}") + else: + print(f"\n✗ 下载失败: {result.error_message}") + + +# 示例2: 下载指定域名的Cookie +def example_download_specific_domain(): + """下载指定域名示例""" + from cookiecloud_client import CookieCloudClient, CookieConfig + + print("\n" + "=" * 60) + print("示例2: 下载指定域名的Cookie") + print("=" * 60) + + config = CookieConfig( + server="https://movie-pilot.org/cookiecloud", + username="hu6n2vcUmzpu7mqUN2rVCg", + password="jtDM5dV9AyqXkZdQVeA9f6" + ) + + client = CookieCloudClient(config) + + # 下载单个域名 + domain = "baidu.com" + cookie_str = client.download_for_domain(domain) + + if cookie_str: + print(f"✓ {domain}的Cookie:") + print(f" {cookie_str[:100]}...") + else: + print(f"✗ 未找到{domain}的Cookie") + + +# 示例3: 批量下载多个域名 +def example_download_multiple_domains(): + """批量下载示例""" + from cookiecloud_client import CookieCloudClient, CookieConfig + + print("\n" + "=" * 60) + print("示例3: 批量下载多个域名") + print("=" * 60) + + config = CookieConfig( + server="https://movie-pilot.org/cookiecloud", + username="hu6n2vcUmzpu7mqUN2rVCg", + password="jtDM5dV9AyqXkZdQVeA9f6" + ) + + client = CookieCloudClient(config) + + # 批量下载 + domains = ["baidu.com", "github.com", "google.com", "bing.com"] + cookies = client.download_for_domains(domains) + + print("批量下载结果:") + for domain, cookie_str in cookies.items(): + if cookie_str: + preview = cookie_str[:50] + "..." + print(f" ✓ {domain}: {preview}") + else: + print(f" ✗ {domain}: 未找到Cookie") + + +# 示例4: 异常处理 +def example_error_handling(): + """异常处理示例""" + from cookiecloud_client import ( + CookieCloudClient, + CookieConfig, + CookieCloudError, + ConnectionError, + AuthenticationError, + NetworkError + ) + + print("\n" + "=" * 60) + print("示例4: 异常处理") + print("=" * 60) + + try: + # 使用错误的凭据 + config = CookieConfig( + server="https://movie-pilot.org/cookiecloud", + username="wrong_user", + password="wrong_pass" + ) + + client = CookieCloudClient(config) + result = client.download() + + except AuthenticationError as e: + print(f"✗ 认证失败: {e.message}") + print(f" 用户名: {e.details.get('username')}") + + except ConnectionError as e: + print(f"✗ 连接失败: {e.message}") + print(f" 服务器: {e.details.get('server')}") + print(f" 状态码: {e.details.get('status_code')}") + + except NetworkError as e: + print(f"✗ 网络错误: {e.message}") + print(f" 原始错误: {e.details.get('original_error')}") + + except CookieCloudError as e: + print(f"✗ CookieCloud错误: {e.message}") + + +# 示例5: 自定义配置 +def example_custom_config(): + """自定义配置示例""" + from cookiecloud_client import CookieCloudClient, CookieConfig + + print("\n" + "=" * 60) + print("示例5: 自定义配置") + print("=" * 60) + + # 自定义配置 + config = CookieConfig( + server="https://movie-pilot.org/cookiecloud", + username="hu6n2vcUmzpu7mqUN2rVCg", + password="jtDM5dV9AyqXkZdQVeA9f6", + timeout=60, # 60秒超时 + verify_ssl=False, # 不验证SSL证书 + ignore_cookies=[ # 忽略的cookie + "CookieAutoDeleteBrowsingDataCleanup", + "CookieAutoDeleteCleaningDiscarded", + "_ga", # 忽略Google Analytics + ] + ) + + client = CookieCloudClient(config) + + print(f"配置信息:") + print(f" 服务器: {config.server}") + print(f" 用户名: {config.username}") + print(f" 超时时间: {config.timeout}秒") + print(f" 验证SSL: {config.verify_ssl}") + print(f" 忽略Cookie: {len(config.ignore_cookies)}个") + + result = client.download() + + if result.success: + print(f"\n✓ 下载成功") + print(f" 总域名数: {result.total_domains}") + print(f" 总Cookie数: {result.total_cookies}") + + +# 示例6: 使用Cookie数据对象 +def example_cookie_data_objects(): + """使用Cookie数据对象示例""" + from cookiecloud_client import CookieCloudClient, CookieConfig + + print("\n" + "=" * 60) + print("示例6: 使用Cookie数据对象") + print("=" * 60) + + config = CookieConfig( + server="https://movie-pilot.org/cookiecloud", + username="hu6n2vcUmzpu7mqUN2rVCg", + password="jtDM5dV9AyqXkZdQVeA9f6" + ) + + client = CookieCloudClient(config) + result = client.download() + + if result.success: + # 获取第一个域名的详细信息 + first_domain = result.get_domains()[0] + collection = result.cookies.get(first_domain) + + if collection: + print(f"域名: {collection.domain}") + print(f"Cookie数量: {len(collection.cookies)}") + print(f"\nCookie详情:") + + for idx, cookie in enumerate(collection.cookies[:3], 1): + print(f" {idx}. {cookie.name}") + print(f" 值: {cookie.value[:30]}...") + print(f" 路径: {cookie.path}") + print(f" 安全: {cookie.secure}") + print(f" HttpOnly: {cookie.http_only}") + print() + + +# 主函数 +def main(): + """运行所有示例""" + print("\n") + print("╔" + "=" * 58 + "╗") + print("║" + " " * 15 + "CookieCloud客户端使用示例" + " " * 17 + "║") + print("╚" + "=" * 58 + "╝") + + try: + example_basic_usage() + example_download_specific_domain() + example_download_multiple_domains() + example_error_handling() + example_custom_config() + example_cookie_data_objects() + + print("\n" + "=" * 60) + print("所有示例执行完成!") + print("=" * 60) + + except Exception as e: + print(f"\n示例执行出错: {str(e)}") + + +if __name__ == "__main__": + main() diff --git a/cookiecloud_client/exceptions.py b/cookiecloud_client/exceptions.py new file mode 100644 index 0000000..5204dbd --- /dev/null +++ b/cookiecloud_client/exceptions.py @@ -0,0 +1,65 @@ +""" +CookieCloud客户端异常类 +""" + + +class CookieCloudError(Exception): + """CookieCloud基础异常类""" + + def __init__(self, message: str, details: dict = None): + self.message = message + self.details = details or {} + super().__init__(self.message) + + def __str__(self): + if self.details: + return f"{self.message} - 详情: {self.details}" + return self.message + + +class ConfigurationError(CookieCloudError): + """配置错误异常""" + + def __init__(self, message: str, config_key: str = None): + details = {'config_key': config_key} if config_key else {} + super().__init__(message, details) + + +class ConnectionError(CookieCloudError): + """连接错误异常""" + + def __init__(self, message: str, server: str = None, status_code: int = None): + details = {} + if server: + details['server'] = server + if status_code: + details['status_code'] = status_code + super().__init__(message, details) + + +class AuthenticationError(CookieCloudError): + """认证错误异常""" + + def __init__(self, message: str, username: str = None): + details = {'username': username} if username else {} + super().__init__(message, details) + + +class DataParseError(CookieCloudError): + """数据解析错误异常""" + + def __init__(self, message: str, raw_data: str = None): + details = {} + if raw_data: + details['raw_data_length'] = len(raw_data) + super().__init__(message, details) + + +class NetworkError(CookieCloudError): + """网络错误异常""" + + def __init__(self, message: str, original_error: Exception = None): + details = {} + if original_error: + details['original_error'] = str(original_error) + super().__init__(message, details) diff --git a/cookiecloud_client/models.py b/cookiecloud_client/models.py new file mode 100644 index 0000000..c0a877b --- /dev/null +++ b/cookiecloud_client/models.py @@ -0,0 +1,126 @@ +""" +CookieCloud数据模型 +""" +from dataclasses import dataclass, field +from typing import Optional, Dict, List +from datetime import datetime + + +@dataclass +class CookieConfig: + """CookieCloud配置""" + server: str + username: str + password: str + timeout: int = 30 + verify_ssl: bool = True + ignore_cookies: List[str] = field(default_factory=lambda: [ + "CookieAutoDeleteBrowsingDataCleanup", + "CookieAutoDeleteCleaningDiscarded" + ]) + + def __post_init__(self): + """验证配置参数""" + if not self.server: + raise ValueError("服务器地址不能为空") + if not self.username: + raise ValueError("用户名不能为空") + if not self.password: + raise ValueError("密码不能为空") + if not self.server.startswith(('http://', 'https://')): + self.server = f"https://{self.server}" + + +@dataclass +class CookieData: + """Cookie数据""" + domain: str + name: str + value: str + path: str = "/" + secure: bool = False + http_only: bool = False + expiry: Optional[datetime] = None + + def to_dict(self) -> Dict: + """转换为字典""" + return { + 'domain': self.domain, + 'name': self.name, + 'value': self.value, + 'path': self.path, + 'secure': self.secure, + 'httpOnly': self.http_only, + 'expiry': self.expiry.isoformat() if self.expiry else None + } + + @classmethod + def from_dict(cls, data: Dict) -> 'CookieData': + """从字典创建""" + expiry = None + if data.get('expiry'): + try: + expiry = datetime.fromisoformat(data['expiry']) + except (ValueError, TypeError): + pass + + return cls( + domain=data.get('domain', ''), + name=data.get('name', ''), + value=data.get('value', ''), + path=data.get('path', '/'), + secure=data.get('secure', False), + http_only=data.get('httpOnly', False), + expiry=expiry + ) + + +@dataclass +class CookieCollection: + """Cookie集合""" + domain: str + cookies: List[CookieData] = field(default_factory=list) + + def to_cookie_string(self, ignore_list: List[str] = None) -> str: + """转换为cookie字符串""" + ignore_list = ignore_list or [] + cookie_parts = [] + + for cookie in self.cookies: + if cookie.name not in ignore_list: + cookie_parts.append(f"{cookie.name}={cookie.value}") + + return ";".join(cookie_parts) + + def add_cookie(self, cookie: CookieData): + """添加cookie""" + self.cookies.append(cookie) + + def get_cookie_by_name(self, name: str) -> Optional[CookieData]: + """根据名称获取cookie""" + for cookie in self.cookies: + if cookie.name == name: + return cookie + return None + + +@dataclass +class DownloadResult: + """下载结果""" + success: bool + cookies: Dict[str, CookieCollection] = field(default_factory=dict) + error_message: str = "" + total_domains: int = 0 + total_cookies: int = 0 + download_time: float = 0.0 + + def get_cookie_string(self, domain: str) -> Optional[str]: + """获取指定域名的cookie字符串""" + collection = self.cookies.get(domain) + if collection: + return collection.to_cookie_string() + return None + + def get_domains(self) -> List[str]: + """获取所有域名列表""" + return list(self.cookies.keys()) diff --git a/cookiecloud_client/requirements.txt b/cookiecloud_client/requirements.txt new file mode 100644 index 0000000..a1561de --- /dev/null +++ b/cookiecloud_client/requirements.txt @@ -0,0 +1,14 @@ +# CookieCloud客户端依赖项 +# 此模块完全独立,仅使用Python标准库 + +# Python版本要求 +Python>=3.7 + +# 无外部依赖 +# 仅使用Python标准库模块: +# - json +# - urllib +# - dataclasses +# - typing +# - datetime +# - unittest (用于测试) diff --git a/cookiecloud_client/setup.py b/cookiecloud_client/setup.py new file mode 100644 index 0000000..f95f740 --- /dev/null +++ b/cookiecloud_client/setup.py @@ -0,0 +1,54 @@ +""" +CookieCloud客户端安装脚本 +""" +from setuptools import setup, find_packages + +with open("README.md", "r", encoding="utf-8") as fh: + long_description = fh.read() + +setup( + name="cookiecloud-client", + version="1.0.0", + author="CookieManager Team", + author_email="support@example.com", + description="一个独立的、可复用的CookieCloud服务器客户端", + long_description=long_description, + long_description_content_type="text/markdown", + url="https://github.com/example/cookiecloud-client", + packages=find_packages(), + classifiers=[ + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Developers", + "Topic :: Software Development :: Libraries :: Python Modules", + "License :: OSI Approved :: MIT License", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + ], + python_requires=">=3.7", + install_requires=[ + # 无外部依赖,仅使用Python标准库 + ], + extras_require={ + "dev": [ + "pytest>=6.0", + "pytest-cov>=2.0", + "black>=21.0", + "flake8>=3.9", + ], + }, + entry_points={ + "console_scripts": [ + "cookiecloud-client=cookiecloud_client.cli:main", + ], + }, + project_urls={ + "Bug Reports": "https://github.com/example/cookiecloud-client/issues", + "Source": "https://github.com/example/cookiecloud-client", + "Documentation": "https://github.com/example/cookiecloud-client#readme", + }, +) diff --git a/cookiecloud_client/tests.py b/cookiecloud_client/tests.py new file mode 100644 index 0000000..6612326 --- /dev/null +++ b/cookiecloud_client/tests.py @@ -0,0 +1,295 @@ +""" +CookieCloud客户端单元测试 +""" +import unittest +from unittest.mock import Mock, patch, MagicMock +import json +from cookiecloud_client import ( + CookieCloudClient, + CookieConfig, + CookieCloudError, + ConfigurationError, + ConnectionError, + AuthenticationError, + DataParseError, + NetworkError +) + + +class TestCookieConfig(unittest.TestCase): + """测试CookieConfig配置类""" + + def test_valid_config(self): + """测试有效配置""" + config = CookieConfig( + server="https://example.com", + username="user", + password="pass" + ) + self.assertEqual(config.server, "https://example.com") + self.assertEqual(config.username, "user") + self.assertEqual(config.password, "pass") + self.assertEqual(config.timeout, 30) + self.assertTrue(config.verify_ssl) + + def test_config_with_custom_params(self): + """测试自定义参数配置""" + config = CookieConfig( + server="https://example.com", + username="user", + password="pass", + timeout=60, + verify_ssl=False, + ignore_cookies=["test_cookie"] + ) + self.assertEqual(config.timeout, 60) + self.assertFalse(config.verify_ssl) + self.assertEqual(config.ignore_cookies, ["test_cookie"]) + + def test_config_auto_add_protocol(self): + """测试自动添加协议""" + config = CookieConfig( + server="example.com", + username="user", + password="pass" + ) + self.assertTrue(config.server.startswith("https://")) + + def test_config_empty_server(self): + """测试空服务器地址""" + with self.assertRaises(ValueError): + CookieConfig(server="", username="user", password="pass") + + def test_config_empty_username(self): + """测试空用户名""" + with self.assertRaises(ValueError): + CookieConfig(server="https://example.com", username="", password="pass") + + def test_config_empty_password(self): + """测试空密码""" + with self.assertRaises(ValueError): + CookieConfig(server="https://example.com", username="user", password="") + + +class TestCookieCloudClient(unittest.TestCase): + """测试CookieCloudClient客户端类""" + + def setUp(self): + """测试前准备""" + self.config = CookieConfig( + server="https://test.example.com", + username="testuser", + password="testpass" + ) + self.client = CookieCloudClient(self.config) + + def test_client_initialization(self): + """测试客户端初始化""" + self.assertIsInstance(self.client.config, CookieConfig) + self.assertIsNone(self.client.last_download_time) + self.assertEqual(self.client.download_count, 0) + + def test_client_invalid_config(self): + """测试无效配置""" + with self.assertRaises(ConfigurationError): + CookieCloudClient("invalid_config") + + @patch('urllib.request.urlopen') + def test_download_success(self, mock_urlopen): + """测试成功下载""" + mock_response = Mock() + mock_response.status = 200 + mock_response.read.return_value = json.dumps({ + "cookie_data": { + "test.com": [ + { + "domain": "test.com", + "name": "session", + "value": "test_value", + "path": "/", + "secure": False, + "httpOnly": False + } + ] + } + }).encode('utf-8') + mock_urlopen.return_value = mock_response + + result = self.client.download() + + self.assertTrue(result.success) + self.assertEqual(result.total_domains, 1) + self.assertEqual(result.total_cookies, 1) + self.assertIsNotNone(self.client.last_download_time) + self.assertEqual(self.client.download_count, 1) + + @patch('urllib.request.urlopen') + def test_download_authentication_error(self, mock_urlopen): + """测试认证失败""" + import urllib.error + + mock_urlopen.side_effect = urllib.error.HTTPError( + url="https://test.example.com/get/testuser", + code=401, + msg="Unauthorized", + hdrs={}, + fp=None + ) + + with self.assertRaises(AuthenticationError): + self.client.download() + + @patch('urllib.request.urlopen') + def test_download_connection_error(self, mock_urlopen): + """测试连接错误""" + import urllib.error + + mock_urlopen.side_effect = urllib.error.HTTPError( + url="https://test.example.com/get/testuser", + code=404, + msg="Not Found", + hdrs={}, + fp=None + ) + + with self.assertRaises(ConnectionError): + self.client.download() + + @patch('urllib.request.urlopen') + def test_download_network_error(self, mock_urlopen): + """测试网络错误""" + import urllib.error + + mock_urlopen.side_effect = urllib.error.URLError("Connection refused") + + with self.assertRaises(NetworkError): + self.client.download() + + @patch('urllib.request.urlopen') + def test_download_empty_data(self, mock_urlopen): + """测试空数据""" + mock_response = Mock() + mock_response.status = 200 + mock_response.read.return_value = json.dumps({}).encode('utf-8') + mock_urlopen.return_value = mock_response + + with self.assertRaises(DataParseError): + self.client.download() + + @patch('urllib.request.urlopen') + def test_test_connection_success(self, mock_urlopen): + """测试连接测试成功""" + mock_response = Mock() + mock_response.status = 200 + mock_response.read.return_value = json.dumps({ + "cookie_data": {} + }).encode('utf-8') + mock_urlopen.return_value = mock_response + + success, message = self.client.test_connection() + + self.assertTrue(success) + self.assertEqual(message, "连接成功") + + @patch('urllib.request.urlopen') + def test_test_connection_failure(self, mock_urlopen): + """测试连接测试失败""" + import urllib.error + + mock_urlopen.side_effect = urllib.error.HTTPError( + url="https://test.example.com/get/testuser", + code=401, + msg="Unauthorized", + hdrs={}, + fp=None + ) + + success, message = self.client.test_connection() + + self.assertFalse(success) + self.assertIn("认证失败", message) + + @patch('urllib.request.urlopen') + def test_download_for_domain(self, mock_urlopen): + """测试下载指定域名""" + mock_response = Mock() + mock_response.status = 200 + mock_response.read.return_value = json.dumps({ + "cookie_data": { + "test.com": [ + { + "domain": "test.com", + "name": "session", + "value": "test_value", + "path": "/", + "secure": False, + "httpOnly": False + } + ] + } + }).encode('utf-8') + mock_urlopen.return_value = mock_response + + cookie_str = self.client.download_for_domain("test.com") + + self.assertIsNotNone(cookie_str) + self.assertIn("session=test_value", cookie_str) + + @patch('urllib.request.urlopen') + def test_download_for_domains(self, mock_urlopen): + """测试批量下载多个域名""" + mock_response = Mock() + mock_response.status = 200 + mock_response.read.return_value = json.dumps({ + "cookie_data": { + "test1.com": [ + {"domain": "test1.com", "name": "cookie1", "value": "value1", "path": "/"} + ], + "test2.com": [ + {"domain": "test2.com", "name": "cookie2", "value": "value2", "path": "/"} + ] + } + }).encode('utf-8') + mock_urlopen.return_value = mock_response + + domains = ["test1.com", "test2.com", "test3.com"] + cookies = self.client.download_for_domains(domains) + + self.assertEqual(len(cookies), 3) + self.assertIn("cookie1=value1", cookies["test1.com"]) + self.assertIn("cookie2=value2", cookies["test2.com"]) + self.assertIsNone(cookies["test3.com"]) + + +class TestExceptions(unittest.TestCase): + """测试异常类""" + + def test_cookie_cloud_error(self): + """测试基础异常""" + error = CookieCloudError("测试错误", {"key": "value"}) + self.assertEqual(error.message, "测试错误") + self.assertEqual(error.details, {"key": "value"}) + self.assertIn("测试错误", str(error)) + + def test_configuration_error(self): + """测试配置错误""" + error = ConfigurationError("配置错误", config_key="server") + self.assertEqual(error.message, "配置错误") + self.assertEqual(error.details["config_key"], "server") + + def test_connection_error(self): + """测试连接错误""" + error = ConnectionError("连接失败", server="example.com", status_code=404) + self.assertEqual(error.message, "连接失败") + self.assertEqual(error.details["server"], "example.com") + self.assertEqual(error.details["status_code"], 404) + + def test_authentication_error(self): + """测试认证错误""" + error = AuthenticationError("认证失败", username="testuser") + self.assertEqual(error.message, "认证失败") + self.assertEqual(error.details["username"], "testuser") + + +if __name__ == '__main__': + unittest.main() diff --git a/cookiecloud_client/version.py b/cookiecloud_client/version.py new file mode 100644 index 0000000..a7edd13 --- /dev/null +++ b/cookiecloud_client/version.py @@ -0,0 +1,8 @@ +""" +版本信息 +""" + +__version__ = '1.0.0' +__author__ = 'CookieManager Team' +__email__ = 'support@example.com' +__license__ = 'MIT' diff --git a/log/app.log b/log/app.log index 39bfe99..f1a2cb2 100644 --- a/log/app.log +++ b/log/app.log @@ -343,3 +343,650 @@ 2026-03-02 16:38:22,061 - INFO - 用户 用户A 处理完成 2026-03-02 16:38:22,075 - INFO - ===== Cookie 监控结束 ===== 2026-03-02 16:38:22,102 - INFO - 成功处理: 1 个用户,失败: 0 个用户 +2026-03-02 18:56:27,278 - INFO - ===== Cookie 监控开始 ===== +2026-03-02 18:56:27,279 - INFO - 开始处理用户: 用户A +2026-03-02 18:56:27,279 - INFO - 处理网站: 用户A - 网站A +2026-03-02 18:56:27,280 - INFO - 尝试使用本地 cookie 登录: 网站A +2026-03-02 18:56:27,280 - INFO - 从 Cookie Cloud 获取 cookie: 网站A +2026-03-02 18:56:54,215 - INFO - Cookie Cloud 登录失败: 网站A +2026-03-02 18:56:54,216 - INFO - 登录失败: 网站A +2026-03-02 18:56:54,435 - INFO - 已发送失败通知: 用户A - 网站A +2026-03-02 18:56:54,508 - INFO - 已达到最大失败次数,已发送通知并重置计数: 网站A +2026-03-02 18:56:55,613 - INFO - 用户 用户A 处理完成 +2026-03-02 18:56:55,615 - INFO - ===== Cookie 监控结束 ===== +2026-03-02 18:56:55,616 - INFO - 成功处理: 1 个用户,失败: 0 个用户 +2026-03-02 18:57:16,772 - INFO - ===== Cookie 监控开始 ===== +2026-03-02 18:57:16,772 - INFO - 开始处理用户: 用户A +2026-03-02 18:57:16,773 - INFO - 处理网站: 用户A - 网站A +2026-03-02 18:57:16,773 - INFO - 尝试使用本地 cookie 登录: 网站A +2026-03-02 18:57:16,774 - INFO - 从 Cookie Cloud 获取 cookie: 网站A +2026-03-02 18:57:38,432 - INFO - Cookie Cloud 登录失败: 网站A +2026-03-02 18:57:38,433 - INFO - 登录失败: 网站A +2026-03-02 18:57:38,434 - INFO - 当前失败次数: 1/3 +2026-03-02 18:57:39,475 - INFO - 用户 用户A 处理完成 +2026-03-02 18:57:39,479 - INFO - ===== Cookie 监控结束 ===== +2026-03-02 18:57:39,480 - INFO - 成功处理: 1 个用户,失败: 0 个用户 +2026-03-02 19:06:12,869 - INFO - ===== Cookie 监控开始 ===== +2026-03-02 19:06:12,870 - INFO - 开始处理用户: 用户A +2026-03-02 19:06:12,871 - INFO - 处理网站: 用户A - 网站A +2026-03-02 19:06:12,871 - INFO - 尝试使用本地 cookie 登录: 网站A +2026-03-02 19:06:12,872 - INFO - 从 Cookie Cloud 获取 cookie: 网站A +2026-03-02 19:06:13,899 - INFO - 获取到 4 个 cookie: +2026-03-02 19:06:13,900 - INFO - - LMKBIUSS: eyJhY2NvdW50SWQiOjIzMzk0OTQ3NTAwMjA0OSwicGhvbmUiOi... +2026-03-02 19:06:13,902 - INFO - - edentoken: 13548569666 +2026-03-02 19:06:13,903 - INFO - - user: 13548569666 +2026-03-02 19:06:13,904 - INFO - - userType: 2 +2026-03-02 19:08:41,909 - INFO - Cookie Cloud 登录失败: 网站A +2026-03-02 19:08:41,910 - INFO - 登录失败: 网站A +2026-03-02 19:08:41,913 - INFO - 当前失败次数: 2/3 +2026-03-02 19:08:43,011 - INFO - 用户 用户A 处理完成 +2026-03-02 19:08:43,019 - INFO - ===== Cookie 监控结束 ===== +2026-03-02 19:08:43,020 - INFO - 成功处理: 1 个用户,失败: 0 个用户 +2026-03-02 19:09:07,392 - INFO - ===== Cookie 监控开始 ===== +2026-03-02 19:09:07,393 - INFO - 开始处理用户: 用户A +2026-03-02 19:09:07,393 - INFO - 处理网站: 用户A - 网站A +2026-03-02 19:09:07,393 - INFO - 尝试使用本地 cookie 登录: 网站A +2026-03-02 19:09:07,394 - INFO - 从 Cookie Cloud 获取 cookie: 网站A +2026-03-02 19:09:08,548 - INFO - 获取到 4 个 cookie: +2026-03-02 19:09:08,549 - INFO - - LMKBIUSS: eyJhY2NvdW50SWQiOjIzMzk0OTQ3NTAwMjA0OSwicGhvbmUiOi... +2026-03-02 19:09:08,549 - INFO - - edentoken: 13548569666 +2026-03-02 19:09:08,549 - INFO - - user: 13548569666 +2026-03-02 19:09:08,550 - INFO - - userType: 2 +2026-03-02 19:09:24,826 - INFO - Cookie Cloud 登录失败: 网站A +2026-03-02 19:09:24,827 - INFO - 登录失败: 网站A +2026-03-02 19:09:24,974 - INFO - 已发送失败通知: 用户A - 网站A +2026-03-02 19:09:24,975 - INFO - 已达到最大失败次数,已发送通知并重置计数: 网站A +2026-03-02 19:09:26,021 - INFO - 用户 用户A 处理完成 +2026-03-02 19:09:26,022 - INFO - ===== Cookie 监控结束 ===== +2026-03-02 19:09:26,023 - INFO - 成功处理: 1 个用户,失败: 0 个用户 +2026-03-02 19:18:24,406 - INFO - ===== Cookie 监控开始 ===== +2026-03-02 19:18:24,408 - INFO - 开始处理用户: 用户A +2026-03-02 19:18:24,408 - INFO - 处理网站: 用户A - 网站A +2026-03-02 19:18:24,409 - INFO - 尝试使用本地 cookie 登录: 网站A +2026-03-02 19:18:24,409 - INFO - 从 Cookie Cloud 获取 cookie: 网站A +2026-03-02 19:18:25,149 - INFO - 获取到 4 个 cookie: +2026-03-02 19:18:25,149 - INFO - - LMKBIUSS: eyJhY2NvdW50SWQiOjIzMzk0OTQ3NTAwMjA0OSwicGhvbmUiOi... +2026-03-02 19:18:25,150 - INFO - - edentoken: 13548569666 +2026-03-02 19:18:25,150 - INFO - - user: 13548569666 +2026-03-02 19:18:25,151 - INFO - - userType: 2 +2026-03-02 19:19:20,811 - INFO - Cookie Cloud 登录失败: 网站A +2026-03-02 19:19:20,812 - INFO - 登录失败: 网站A +2026-03-02 19:19:20,826 - INFO - 当前失败次数: 1/3 +2026-03-02 19:19:21,868 - INFO - 用户 用户A 处理完成 +2026-03-02 19:19:21,875 - INFO - 开始处理用户: 用户B +2026-03-02 19:19:21,876 - INFO - 处理网站: 用户B - 网站A +2026-03-02 19:19:21,879 - INFO - 尝试使用本地 cookie 登录: 网站A +2026-03-02 19:19:21,883 - INFO - 从 Cookie Cloud 获取 cookie: 网站A +2026-03-02 19:19:22,305 - INFO - 获取到 6 个 cookie: +2026-03-02 19:19:22,306 - INFO - - _qimei_uuid42: 1a302130f16100ca18af370a47cb8afce807c4a076 +2026-03-02 19:19:22,306 - INFO - - _qimei_fingerprint: 50aa79f919511142818459f44ac38dee +2026-03-02 19:19:22,307 - INFO - - _qimei_i_3: 64c844d0c35e05dc9796fc355d8470e5a3eba5f0410e00d3e7... +2026-03-02 19:19:22,307 - INFO - - _qimei_h38: f8e0750618af370a47cb8afc0200000c91a302 +2026-03-02 19:19:22,308 - INFO - - merchantType: web +2026-03-02 19:19:22,308 - INFO - - _qimei_i_1: 63f25ad49d0f50dcc497f83153d525e3f0eef5f51508518ae5... +2026-03-02 19:19:42,892 - INFO - Cookie Cloud 登录失败: 网站A +2026-03-02 19:19:42,893 - INFO - 登录失败: 网站A +2026-03-02 19:19:42,897 - INFO - 当前失败次数: 1/3 +2026-03-02 19:19:43,952 - INFO - 用户 用户B 处理完成 +2026-03-02 19:19:43,953 - INFO - ===== Cookie 监控结束 ===== +2026-03-02 19:19:43,956 - INFO - 成功处理: 2 个用户,失败: 0 个用户 +2026-03-02 19:28:58,190 - INFO - ===== Cookie 监控开始 ===== +2026-03-02 19:28:58,195 - INFO - 开始处理用户: 用户A +2026-03-02 19:28:58,196 - INFO - 处理网站: 用户A - 网站A +2026-03-02 19:28:58,197 - INFO - 尝试使用本地 cookie 登录: 网站A +2026-03-02 19:28:58,198 - INFO - 从 Cookie Cloud 获取 cookie: 网站A +2026-03-02 19:28:58,934 - INFO - 获取到 4 个 cookie: +2026-03-02 19:28:58,934 - INFO - - LMKBIUSS: eyJhY2NvdW50SWQiOjIzMzk0OTQ3NTAwMjA0OSwicGhvbmUiOi... +2026-03-02 19:28:58,935 - INFO - - edentoken: 13548569666 +2026-03-02 19:28:58,936 - INFO - - user: 13548569666 +2026-03-02 19:28:58,936 - INFO - - userType: 2 +2026-03-02 19:29:09,680 - INFO - 开始设置 4 个 cookie +2026-03-02 19:29:10,559 - INFO - 设置 cookie: LMKBIUSS +2026-03-02 19:29:10,589 - INFO - 设置 cookie: edentoken +2026-03-02 19:29:10,616 - INFO - 设置 cookie: user +2026-03-02 19:29:10,649 - INFO - 设置 cookie: userType +2026-03-02 19:31:57,850 - INFO - Cookie Cloud 登录失败: 网站A +2026-03-02 19:31:57,874 - INFO - 登录失败: 网站A +2026-03-02 19:31:57,908 - INFO - 当前失败次数: 2/3 +2026-03-02 19:31:59,017 - INFO - 用户 用户A 处理完成 +2026-03-02 19:31:59,026 - INFO - 开始处理用户: 用户B +2026-03-02 19:31:59,034 - INFO - 处理网站: 用户B - 网站A +2026-03-02 19:31:59,049 - INFO - 尝试使用本地 cookie 登录: 网站A +2026-03-02 19:31:59,057 - INFO - 从 Cookie Cloud 获取 cookie: 网站A +2026-03-02 19:31:59,548 - INFO - 获取到 6 个 cookie: +2026-03-02 19:31:59,549 - INFO - - _qimei_uuid42: 1a302130f16100ca18af370a47cb8afce807c4a076 +2026-03-02 19:31:59,549 - INFO - - _qimei_fingerprint: 50aa79f919511142818459f44ac38dee +2026-03-02 19:31:59,550 - INFO - - _qimei_i_3: 64c844d0c35e05dc9796fc355d8470e5a3eba5f0410e00d3e7... +2026-03-02 19:31:59,551 - INFO - - _qimei_h38: f8e0750618af370a47cb8afc0200000c91a302 +2026-03-02 19:31:59,555 - INFO - - merchantType: web +2026-03-02 19:31:59,557 - INFO - - _qimei_i_1: 63f25ad49d0f50dcc497f83153d525e3f0eef5f51508518ae5... +2026-03-02 19:32:04,579 - INFO - 开始设置 6 个 cookie +2026-03-02 19:32:05,108 - INFO - 设置 cookie: _qimei_uuid42 +2026-03-02 19:32:05,123 - INFO - 设置 cookie: _qimei_fingerprint +2026-03-02 19:32:05,134 - INFO - 设置 cookie: _qimei_i_3 +2026-03-02 19:32:05,152 - INFO - 设置 cookie: _qimei_h38 +2026-03-02 19:32:05,176 - INFO - 设置 cookie: merchantType +2026-03-02 19:32:05,191 - INFO - 设置 cookie: _qimei_i_1 +2026-03-02 19:32:20,855 - INFO - Cookie Cloud 登录失败: 网站A +2026-03-02 19:32:20,856 - INFO - 登录失败: 网站A +2026-03-02 19:32:20,861 - INFO - 当前失败次数: 2/3 +2026-03-02 19:32:21,948 - INFO - 用户 用户B 处理完成 +2026-03-02 19:32:21,953 - INFO - ===== Cookie 监控结束 ===== +2026-03-02 19:32:21,970 - INFO - 成功处理: 2 个用户,失败: 0 个用户 +2026-03-02 19:41:26,080 - INFO - ===== Cookie 监控开始 ===== +2026-03-02 19:41:26,081 - INFO - 开始处理用户: 用户A +2026-03-02 19:41:26,082 - INFO - 处理网站: 用户A - 网站A +2026-03-02 19:41:26,082 - INFO - 尝试使用本地 cookie 登录: 网站A +2026-03-02 19:41:26,082 - INFO - 从 Cookie Cloud 获取 cookie: 网站A +2026-03-02 19:41:27,131 - INFO - 获取到 4 个 cookie: +2026-03-02 19:41:27,132 - INFO - - LMKBIUSS: eyJhY2NvdW50SWQiOjIzMzk0OTQ3NTAwMjA0OSwicGhvbmUiOi... +2026-03-02 19:41:27,134 - INFO - - edentoken: 13548569666 +2026-03-02 19:41:27,135 - INFO - - user: 13548569666 +2026-03-02 19:41:27,137 - INFO - - userType: 2 +2026-03-02 19:41:32,196 - INFO - 开始设置 4 个 cookie +2026-03-02 19:41:32,218 - INFO - 设置 cookie: LMKBIUSS +2026-03-02 19:41:32,256 - INFO - 设置 cookie: edentoken +2026-03-02 19:41:32,291 - INFO - 设置 cookie: user +2026-03-02 19:41:32,315 - INFO - 设置 cookie: userType +2026-03-02 19:41:47,436 - INFO - Cookie Cloud 登录失败: 网站A +2026-03-02 19:41:47,436 - INFO - 登录失败: 网站A +2026-03-02 19:41:47,804 - INFO - 已发送失败通知: 用户A - 网站A +2026-03-02 19:41:47,828 - INFO - 已达到最大失败次数,已发送通知并重置计数: 网站A +2026-03-02 19:41:48,879 - INFO - 用户 用户A 处理完成 +2026-03-02 19:41:48,880 - INFO - 开始处理用户: 用户B +2026-03-02 19:41:48,881 - INFO - 处理网站: 用户B - 网站A +2026-03-02 19:41:48,882 - INFO - 尝试使用本地 cookie 登录: 网站A +2026-03-02 19:41:48,882 - INFO - 从 Cookie Cloud 获取 cookie: 网站A +2026-03-02 19:41:49,422 - INFO - 获取到 17 个 cookie: +2026-03-02 19:41:49,426 - INFO - - _qimei_uuid42: 1a302130f16100ca18af370a47cb8afce807c4a076 +2026-03-02 19:41:49,435 - INFO - - _qimei_fingerprint: 50aa79f919511142818459f44ac38dee +2026-03-02 19:41:49,481 - INFO - - _qimei_i_3: 64c844d0c35e05dc9796fc355d8470e5a3eba5f0410e00d3e7... +2026-03-02 19:41:49,561 - INFO - - _qimei_h38: f8e0750618af370a47cb8afc0200000c91a302 +2026-03-02 19:41:49,583 - INFO - - merchantType: web +2026-03-02 19:41:49,596 - INFO - - _qimei_i_1: 63f25ad49d0f50dcc497f83153d525e3f0eef5f51508518ae5... +2026-03-02 19:41:49,609 - INFO - - JSESSIONID: B93CC37376EFC653A59B550FE761CC72 +2026-03-02 19:41:49,612 - INFO - - _qimei_i_1: 0 +2026-03-02 19:41:49,662 - INFO - - merchantType: 0 +2026-03-02 19:41:49,665 - INFO - - _qimei_h38: 0 +2026-03-02 19:41:49,666 - INFO - - _qimei_i_3: 0 +2026-03-02 19:41:49,668 - INFO - - _qimei_fingerprint: 0 +2026-03-02 19:41:49,672 - INFO - - _qimei_uuid42: 0 +2026-03-02 19:41:49,672 - INFO - - LMKBIUSS: eyJhY2NvdW50SWQiOjIzMzk0OTQ3NTAwMjA0OSwicGhvbmUiOi... +2026-03-02 19:41:49,674 - INFO - - edentoken: 13548569666 +2026-03-02 19:41:49,675 - INFO - - user: 13548569666 +2026-03-02 19:41:49,676 - INFO - - userType: 2 +2026-03-02 19:41:53,678 - INFO - 开始设置 17 个 cookie +2026-03-02 19:41:54,168 - INFO - 设置 cookie: _qimei_uuid42 +2026-03-02 19:41:54,177 - INFO - 设置 cookie: _qimei_fingerprint +2026-03-02 19:41:54,191 - INFO - 设置 cookie: _qimei_i_3 +2026-03-02 19:41:54,205 - INFO - 设置 cookie: _qimei_h38 +2026-03-02 19:41:54,218 - INFO - 设置 cookie: merchantType +2026-03-02 19:41:54,228 - INFO - 设置 cookie: _qimei_i_1 +2026-03-02 19:41:54,240 - INFO - 设置 cookie: JSESSIONID +2026-03-02 19:41:54,250 - INFO - 设置 cookie: _qimei_i_1 +2026-03-02 19:41:54,263 - INFO - 设置 cookie: merchantType +2026-03-02 19:41:54,277 - INFO - 设置 cookie: _qimei_h38 +2026-03-02 19:41:54,290 - INFO - 设置 cookie: _qimei_i_3 +2026-03-02 19:41:54,302 - INFO - 设置 cookie: _qimei_fingerprint +2026-03-02 19:41:54,315 - INFO - 设置 cookie: _qimei_uuid42 +2026-03-02 19:41:54,328 - INFO - 设置 cookie: LMKBIUSS +2026-03-02 19:41:54,339 - INFO - 设置 cookie: edentoken +2026-03-02 19:41:54,350 - INFO - 设置 cookie: user +2026-03-02 19:41:54,362 - INFO - 设置 cookie: userType +2026-03-02 19:44:10,871 - INFO - Cookie Cloud 登录失败: 网站A +2026-03-02 19:44:10,872 - INFO - 登录失败: 网站A +2026-03-02 19:44:11,050 - INFO - 已发送失败通知: 用户B - 网站A +2026-03-02 19:44:11,111 - INFO - 已达到最大失败次数,已发送通知并重置计数: 网站A +2026-03-02 19:44:12,178 - INFO - 用户 用户B 处理完成 +2026-03-02 19:44:12,181 - INFO - ===== Cookie 监控结束 ===== +2026-03-02 19:44:12,187 - INFO - 成功处理: 2 个用户,失败: 0 个用户 +2026-03-02 19:44:29,316 - INFO - ===== Cookie 监控开始 ===== +2026-03-02 19:44:29,316 - INFO - 开始处理用户: 用户A +2026-03-02 19:44:29,317 - INFO - 处理网站: 用户A - 网站A +2026-03-02 19:44:29,317 - INFO - 尝试使用本地 cookie 登录: 网站A +2026-03-02 19:44:29,318 - INFO - 从 Cookie Cloud 获取 cookie: 网站A +2026-03-02 19:44:31,320 - INFO - 获取到 4 个 cookie: +2026-03-02 19:44:31,321 - INFO - - LMKBIUSS: eyJhY2NvdW50SWQiOjIzMzk0OTQ3NTAwMjA0OSwicGhvbmUiOi... +2026-03-02 19:44:31,324 - INFO - - edentoken: 13548569666 +2026-03-02 19:44:31,325 - INFO - - user: 13548569666 +2026-03-02 19:44:31,326 - INFO - - userType: 2 +2026-03-02 19:44:34,631 - INFO - 开始设置 4 个 cookie +2026-03-02 19:44:34,649 - INFO - 设置 cookie: LMKBIUSS +2026-03-02 19:44:34,666 - INFO - 设置 cookie: edentoken +2026-03-02 19:44:34,684 - INFO - 设置 cookie: user +2026-03-02 19:44:34,709 - INFO - 设置 cookie: userType +2026-03-02 19:44:49,463 - INFO - Cookie Cloud 登录失败: 网站A +2026-03-02 19:44:49,465 - INFO - 登录失败: 网站A +2026-03-02 19:44:49,466 - INFO - 当前失败次数: 1/3 +2026-03-02 19:44:50,505 - INFO - 用户 用户A 处理完成 +2026-03-02 19:44:50,506 - INFO - 开始处理用户: 用户B +2026-03-02 19:44:50,506 - INFO - 处理网站: 用户B - 网站A +2026-03-02 19:44:50,507 - INFO - 尝试使用本地 cookie 登录: 网站A +2026-03-02 19:44:50,508 - INFO - 从 Cookie Cloud 获取 cookie: 网站A +2026-03-02 19:44:50,863 - INFO - 获取到 17 个 cookie: +2026-03-02 19:44:50,864 - INFO - - _qimei_uuid42: 1a302130f16100ca18af370a47cb8afce807c4a076 +2026-03-02 19:44:50,865 - INFO - - _qimei_fingerprint: 50aa79f919511142818459f44ac38dee +2026-03-02 19:44:50,865 - INFO - - _qimei_i_3: 64c844d0c35e05dc9796fc355d8470e5a3eba5f0410e00d3e7... +2026-03-02 19:44:50,866 - INFO - - _qimei_h38: f8e0750618af370a47cb8afc0200000c91a302 +2026-03-02 19:44:50,866 - INFO - - merchantType: web +2026-03-02 19:44:50,867 - INFO - - _qimei_i_1: 63f25ad49d0f50dcc497f83153d525e3f0eef5f51508518ae5... +2026-03-02 19:44:50,868 - INFO - - JSESSIONID: B93CC37376EFC653A59B550FE761CC72 +2026-03-02 19:44:50,868 - INFO - - _qimei_i_1: 0 +2026-03-02 19:44:50,868 - INFO - - merchantType: 0 +2026-03-02 19:44:50,869 - INFO - - _qimei_h38: 0 +2026-03-02 19:44:50,870 - INFO - - _qimei_i_3: 0 +2026-03-02 19:44:50,870 - INFO - - _qimei_fingerprint: 0 +2026-03-02 19:44:50,871 - INFO - - _qimei_uuid42: 0 +2026-03-02 19:44:50,872 - INFO - - LMKBIUSS: eyJhY2NvdW50SWQiOjIzMzk0OTQ3NTAwMjA0OSwicGhvbmUiOi... +2026-03-02 19:44:50,872 - INFO - - edentoken: 13548569666 +2026-03-02 19:44:50,873 - INFO - - user: 13548569666 +2026-03-02 19:44:50,873 - INFO - - userType: 2 +2026-03-02 19:44:54,212 - INFO - 开始设置 17 个 cookie +2026-03-02 19:44:54,239 - INFO - 设置 cookie: _qimei_uuid42 +2026-03-02 19:44:54,258 - INFO - 设置 cookie: _qimei_fingerprint +2026-03-02 19:44:54,286 - INFO - 设置 cookie: _qimei_i_3 +2026-03-02 19:44:54,306 - INFO - 设置 cookie: _qimei_h38 +2026-03-02 19:44:54,323 - INFO - 设置 cookie: merchantType +2026-03-02 19:44:54,344 - INFO - 设置 cookie: _qimei_i_1 +2026-03-02 19:44:54,366 - INFO - 设置 cookie: JSESSIONID +2026-03-02 19:44:54,391 - INFO - 设置 cookie: _qimei_i_1 +2026-03-02 19:44:54,414 - INFO - 设置 cookie: merchantType +2026-03-02 19:44:54,436 - INFO - 设置 cookie: _qimei_h38 +2026-03-02 19:44:54,456 - INFO - 设置 cookie: _qimei_i_3 +2026-03-02 19:44:54,474 - INFO - 设置 cookie: _qimei_fingerprint +2026-03-02 19:44:54,503 - INFO - 设置 cookie: _qimei_uuid42 +2026-03-02 19:44:54,529 - INFO - 设置 cookie: LMKBIUSS +2026-03-02 19:44:54,554 - INFO - 设置 cookie: edentoken +2026-03-02 19:44:54,571 - INFO - 设置 cookie: user +2026-03-02 19:44:54,590 - INFO - 设置 cookie: userType +2026-03-02 19:45:10,024 - INFO - Cookie Cloud 登录失败: 网站A +2026-03-02 19:45:10,025 - INFO - 登录失败: 网站A +2026-03-02 19:45:10,026 - INFO - 当前失败次数: 1/3 +2026-03-02 19:45:11,099 - INFO - 用户 用户B 处理完成 +2026-03-02 19:45:11,100 - INFO - ===== Cookie 监控结束 ===== +2026-03-02 19:45:11,100 - INFO - 成功处理: 2 个用户,失败: 0 个用户 +2026-03-02 19:47:06,309 - INFO - ================================================== +2026-03-02 19:47:06,310 - INFO - Cookie 监控开始 +2026-03-02 19:47:06,310 - INFO - 时间: 2026-03-02 19:47:06 +2026-03-02 19:47:06,311 - INFO - ================================================== +2026-03-02 19:47:06,311 - INFO - ==================== 开始处理用户: 用户A ==================== +2026-03-02 19:47:06,312 - INFO - 步骤1: 从 Cookie Cloud 获取所有 cookies +2026-03-02 19:47:08,398 - INFO - 获取到 278 个域名,共 803 个 cookie +2026-03-02 19:47:08,399 - INFO - +处理网站: 网站A +2026-03-02 19:47:08,399 - INFO - URL: https://lmkbi.95155.com/bi-system/#/carUnpetrifiedComAll +2026-03-02 19:47:08,400 - INFO - 找到 4 个 cookie +2026-03-02 19:47:19,291 - INFO - 开始设置 4 个 cookie +2026-03-02 19:47:19,316 - INFO - 设置 cookie: LMKBIUSS +2026-03-02 19:47:19,336 - INFO - 设置 cookie: edentoken +2026-03-02 19:47:19,351 - INFO - 设置 cookie: user +2026-03-02 19:47:19,367 - INFO - 设置 cookie: userType +2026-03-02 19:49:03,692 - WARNING - ✗ 登录状态验证失败(cookies可能已过期或选择器配置错误) +2026-03-02 19:49:03,700 - INFO - +==================== 用户 用户A 处理结果汇总 ==================== +2026-03-02 19:49:03,703 - INFO - 总网站数: 1 +2026-03-02 19:49:03,704 - INFO - 成功: 0, 失败: 1 +2026-03-02 19:49:03,705 - INFO - +失败详情: +2026-03-02 19:49:03,706 - INFO - - 网站A: 登录状态验证失败(cookies可能已过期或选择器配置错误) +2026-03-02 19:49:04,751 - INFO - ==================== 开始处理用户: 用户B ==================== +2026-03-02 19:49:04,790 - INFO - 步骤1: 从 Cookie Cloud 获取所有 cookies +2026-03-02 19:49:05,278 - INFO - 获取到 814 个域名,共 2941 个 cookie +2026-03-02 19:49:05,282 - INFO - +处理网站: 网站A +2026-03-02 19:49:05,299 - INFO - URL: https://lmkbi.95155.com/bi-system/#/carUnpetrifiedComAll +2026-03-02 19:49:05,303 - INFO - 找到 17 个 cookie +2026-03-02 19:49:19,363 - INFO - 开始设置 17 个 cookie +2026-03-02 19:49:19,378 - INFO - 设置 cookie: _qimei_uuid42 +2026-03-02 19:49:19,392 - INFO - 设置 cookie: _qimei_fingerprint +2026-03-02 19:49:19,408 - INFO - 设置 cookie: _qimei_i_3 +2026-03-02 19:49:19,426 - INFO - 设置 cookie: _qimei_h38 +2026-03-02 19:49:19,443 - INFO - 设置 cookie: merchantType +2026-03-02 19:49:19,459 - INFO - 设置 cookie: _qimei_i_1 +2026-03-02 19:49:19,475 - INFO - 设置 cookie: JSESSIONID +2026-03-02 19:49:19,490 - INFO - 设置 cookie: _qimei_i_1 +2026-03-02 19:49:19,506 - INFO - 设置 cookie: merchantType +2026-03-02 19:49:19,521 - INFO - 设置 cookie: _qimei_h38 +2026-03-02 19:49:19,534 - INFO - 设置 cookie: _qimei_i_3 +2026-03-02 19:49:19,549 - INFO - 设置 cookie: _qimei_fingerprint +2026-03-02 19:49:19,562 - INFO - 设置 cookie: _qimei_uuid42 +2026-03-02 19:49:19,578 - INFO - 设置 cookie: LMKBIUSS +2026-03-02 19:49:19,592 - INFO - 设置 cookie: edentoken +2026-03-02 19:49:19,601 - INFO - 设置 cookie: user +2026-03-02 19:49:19,615 - INFO - 设置 cookie: userType +2026-03-02 19:51:34,896 - WARNING - ✗ 登录状态验证失败(cookies可能已过期或选择器配置错误) +2026-03-02 19:51:34,902 - INFO - +==================== 用户 用户B 处理结果汇总 ==================== +2026-03-02 19:51:34,904 - INFO - 总网站数: 1 +2026-03-02 19:51:34,905 - INFO - 成功: 0, 失败: 1 +2026-03-02 19:51:34,906 - INFO - +失败详情: +2026-03-02 19:51:34,907 - INFO - - 网站A: 登录状态验证失败(cookies可能已过期或选择器配置错误) +2026-03-02 19:51:35,961 - INFO - +================================================== +2026-03-02 19:51:35,976 - INFO - Cookie 监控结束 +2026-03-02 19:51:35,988 - INFO - ================================================== +2026-03-02 19:52:33,025 - INFO - ================================================== +2026-03-02 19:52:33,026 - INFO - Cookie 监控开始 +2026-03-02 19:52:33,026 - INFO - 时间: 2026-03-02 19:52:33 +2026-03-02 19:52:33,027 - INFO - ================================================== +2026-03-02 19:52:33,027 - INFO - ==================== 开始处理用户: 用户A ==================== +2026-03-02 19:52:33,028 - INFO - 步骤1: 从 Cookie Cloud 获取所有 cookies +2026-03-02 19:52:35,067 - INFO - 获取到 278 个域名,共 801 个 cookie +2026-03-02 19:52:35,069 - INFO - +处理网站: 网站A +2026-03-02 19:52:35,070 - INFO - URL: https://lmkbi.95155.com/bi-system/#/carUnpetrifiedComAll +2026-03-02 19:52:35,071 - INFO - 找到 4 个 cookie +2026-03-02 19:52:37,975 - INFO - 开始设置 4 个 cookie +2026-03-02 19:52:37,997 - INFO - 设置 cookie: LMKBIUSS +2026-03-02 19:52:38,014 - INFO - 设置 cookie: edentoken +2026-03-02 19:52:38,032 - INFO - 设置 cookie: user +2026-03-02 19:52:38,056 - INFO - 设置 cookie: userType +2026-03-02 19:52:53,369 - WARNING - ✗ 登录状态验证失败(cookies可能已过期或选择器配置错误) +2026-03-02 19:52:53,618 - INFO - 已发送失败通知: 用户A - 网站A +2026-03-02 19:52:53,621 - INFO - +==================== 用户 用户A 处理结果汇总 ==================== +2026-03-02 19:52:53,623 - INFO - 总网站数: 1 +2026-03-02 19:52:53,628 - INFO - 成功: 0, 失败: 1 +2026-03-02 19:52:53,630 - INFO - +失败详情: +2026-03-02 19:52:53,632 - INFO - - 网站A: 登录状态验证失败(cookies可能已过期或选择器配置错误) +2026-03-02 19:52:54,675 - INFO - ==================== 开始处理用户: 用户B ==================== +2026-03-02 19:52:54,676 - INFO - 步骤1: 从 Cookie Cloud 获取所有 cookies +2026-03-02 19:52:55,007 - INFO - 获取到 814 个域名,共 2940 个 cookie +2026-03-02 19:52:55,009 - INFO - +处理网站: 网站A +2026-03-02 19:52:55,011 - INFO - URL: https://lmkbi.95155.com/bi-system/#/carUnpetrifiedComAll +2026-03-02 19:52:55,012 - INFO - 找到 17 个 cookie +2026-03-02 19:52:58,102 - INFO - 开始设置 17 个 cookie +2026-03-02 19:52:58,116 - INFO - 设置 cookie: _qimei_uuid42 +2026-03-02 19:52:58,131 - INFO - 设置 cookie: _qimei_fingerprint +2026-03-02 19:52:58,145 - INFO - 设置 cookie: _qimei_i_3 +2026-03-02 19:52:58,161 - INFO - 设置 cookie: _qimei_h38 +2026-03-02 19:52:58,178 - INFO - 设置 cookie: merchantType +2026-03-02 19:52:58,191 - INFO - 设置 cookie: _qimei_i_1 +2026-03-02 19:52:58,206 - INFO - 设置 cookie: JSESSIONID +2026-03-02 19:52:58,221 - INFO - 设置 cookie: _qimei_i_1 +2026-03-02 19:52:58,236 - INFO - 设置 cookie: merchantType +2026-03-02 19:52:58,251 - INFO - 设置 cookie: _qimei_h38 +2026-03-02 19:52:58,266 - INFO - 设置 cookie: _qimei_i_3 +2026-03-02 19:52:58,283 - INFO - 设置 cookie: _qimei_fingerprint +2026-03-02 19:52:58,299 - INFO - 设置 cookie: _qimei_uuid42 +2026-03-02 19:52:58,316 - INFO - 设置 cookie: LMKBIUSS +2026-03-02 19:52:58,332 - INFO - 设置 cookie: edentoken +2026-03-02 19:52:58,348 - INFO - 设置 cookie: user +2026-03-02 19:52:58,368 - INFO - 设置 cookie: userType +2026-03-02 19:53:12,951 - WARNING - ✗ 登录状态验证失败(cookies可能已过期或选择器配置错误) +2026-03-02 19:53:13,096 - INFO - 已发送失败通知: 用户B - 网站A +2026-03-02 19:53:13,102 - INFO - +==================== 用户 用户B 处理结果汇总 ==================== +2026-03-02 19:53:13,104 - INFO - 总网站数: 1 +2026-03-02 19:53:13,107 - INFO - 成功: 0, 失败: 1 +2026-03-02 19:53:13,109 - INFO - +失败详情: +2026-03-02 19:53:13,110 - INFO - - 网站A: 登录状态验证失败(cookies可能已过期或选择器配置错误) +2026-03-02 19:53:14,193 - INFO - +================================================== +2026-03-02 19:53:14,194 - INFO - Cookie 监控结束 +2026-03-02 19:53:14,194 - INFO - ================================================== +2026-03-02 19:57:38,356 - INFO - ================================================== +2026-03-02 19:57:38,357 - INFO - Cookie 监控开始 +2026-03-02 19:57:38,357 - INFO - 时间: 2026-03-02 19:57:38 +2026-03-02 19:57:38,358 - INFO - ================================================== +2026-03-02 19:57:38,358 - INFO - ==================== 开始处理用户: 用户A ==================== +2026-03-02 19:57:38,359 - INFO - 步骤1: 从 Cookie Cloud 获取所有 cookies +2026-03-02 19:57:39,474 - INFO - 获取到 278 个域名,共 801 个 cookie +2026-03-02 19:57:39,475 - INFO - 步骤2: 一次性导入所有 cookies 到浏览器 +2026-03-02 19:57:50,386 - INFO - 开始导入 801 个 cookie +2026-03-02 19:59:53,803 - INFO - Cookie 导入完成: 成功 801, 失败 0 +2026-03-02 19:59:53,805 - INFO - 成功导入 801 个 cookie +2026-03-02 19:59:53,805 - INFO - 步骤3: 依次验证各网站登录状态 +2026-03-02 19:59:53,806 - INFO - +验证网站: 网站A +2026-03-02 19:59:53,806 - INFO - URL: https://lmkbi.95155.com/bi-system/#/carUnpetrifiedComAll +2026-03-02 20:00:09,296 - WARNING - ✗ 登录状态验证失败(cookies可能已过期或选择器配置错误) +2026-03-02 20:00:09,298 - INFO - +==================== 用户 用户A 处理结果汇总 ==================== +2026-03-02 20:00:09,299 - INFO - 总网站数: 1 +2026-03-02 20:00:09,299 - INFO - 成功: 0, 失败: 1 +2026-03-02 20:00:09,300 - INFO - +失败详情: +2026-03-02 20:00:09,301 - INFO - - 网站A: 登录状态验证失败(cookies可能已过期或选择器配置错误) +2026-03-02 20:00:10,370 - INFO - ==================== 开始处理用户: 用户B ==================== +2026-03-02 20:00:10,374 - INFO - 步骤1: 从 Cookie Cloud 获取所有 cookies +2026-03-02 20:00:10,940 - INFO - 获取到 814 个域名,共 2947 个 cookie +2026-03-02 20:00:10,948 - INFO - 步骤2: 一次性导入所有 cookies 到浏览器 +2026-03-02 20:00:14,614 - INFO - 开始导入 2947 个 cookie +2026-03-02 20:16:17,328 - INFO - ================================================== +2026-03-02 20:16:17,331 - INFO - Cookie 监控开始 +2026-03-02 20:16:17,331 - INFO - 时间: 2026-03-02 20:16:17 +2026-03-02 20:16:17,332 - INFO - ================================================== +2026-03-02 20:16:17,332 - INFO - ==================== 开始处理用户: 用户A ==================== +2026-03-02 20:16:17,332 - INFO - 步骤1: 从 Cookie Cloud 获取所有 cookies +2026-03-02 20:16:19,422 - INFO - 获取到 278 个域名,共 801 个 cookie +2026-03-02 20:16:19,423 - INFO - 步骤2: 一次性导入所有 cookies 到浏览器 +2026-03-02 20:16:33,797 - INFO - 开始导入 801 个 cookie +2026-03-02 20:30:29,314 - INFO - Cookie 导入完成: 成功 801, 失败 0 +2026-03-02 20:30:29,347 - INFO - 成功导入 801 个 cookie +2026-03-02 20:30:29,349 - INFO - 步骤3: 依次验证各网站登录状态 +2026-03-02 20:30:29,351 - INFO - +验证网站: 网站A +2026-03-02 20:30:29,352 - INFO - URL: https://lmkbi.95155.com/bi-system/#/carUnpetrifiedComAll +2026-03-02 20:31:15,938 - WARNING - ✗ 登录状态验证失败(cookies可能已过期或选择器配置错误) +2026-03-02 20:31:16,156 - INFO - +==================== 用户 用户A 处理结果汇总 ==================== +2026-03-02 20:31:16,174 - INFO - 总网站数: 1 +2026-03-02 20:31:16,188 - INFO - 成功: 0, 失败: 1 +2026-03-02 20:31:16,195 - INFO - +失败详情: +2026-03-02 20:31:16,204 - INFO - - 网站A: 登录状态验证失败(cookies可能已过期或选择器配置错误) +2026-03-02 20:31:17,772 - INFO - ==================== 开始处理用户: 用户B ==================== +2026-03-02 20:31:17,832 - INFO - 步骤1: 从 Cookie Cloud 获取所有 cookies +2026-03-02 20:31:19,351 - INFO - 获取到 814 个域名,共 2946 个 cookie +2026-03-02 20:31:19,469 - INFO - 步骤2: 一次性导入所有 cookies 到浏览器 +2026-03-02 20:31:40,232 - INFO - 开始导入 2946 个 cookie +2026-03-02 23:12:19,893 - INFO - ================================================== +2026-03-02 23:12:19,894 - INFO - Cookie 监控开始 +2026-03-02 23:12:19,894 - INFO - 时间: 2026-03-02 23:12:19 +2026-03-02 23:12:19,894 - INFO - ================================================== +2026-03-02 23:12:19,895 - INFO - ==================== 开始处理用户: 用户A ==================== +2026-03-02 23:12:19,895 - INFO - 步骤1: 从 Cookie Cloud 获取所有 cookies +2026-03-02 23:12:21,973 - INFO - 获取到 278 个域名,共 801 个 cookie +2026-03-02 23:12:21,973 - INFO - 步骤2: 一次性导入所有 cookies 到浏览器 +2026-03-02 23:12:26,570 - INFO - 开始导入 801 个 cookie +2026-03-02 23:14:33,586 - INFO - Cookie 导入完成: 成功 801, 失败 0 +2026-03-02 23:14:33,587 - INFO - 成功导入 801 个 cookie +2026-03-02 23:14:33,587 - INFO - 步骤3: 依次验证各网站登录状态 +2026-03-02 23:14:33,588 - INFO - +验证网站: 网站A +2026-03-02 23:14:33,588 - INFO - URL: https://lmkbi.95155.com/bi-system/#/carUnpetrifiedComAll +2026-03-02 23:14:52,444 - WARNING - ✗ 登录状态验证失败(cookies可能已过期或选择器配置错误) +2026-03-02 23:14:52,659 - INFO - 已发送失败通知: 用户A - 网站A +2026-03-02 23:14:52,660 - INFO - +==================== 用户 用户A 处理结果汇总 ==================== +2026-03-02 23:14:52,661 - INFO - 总网站数: 1 +2026-03-02 23:14:52,661 - INFO - 成功: 0, 失败: 1 +2026-03-02 23:14:52,662 - INFO - +失败详情: +2026-03-02 23:14:52,662 - INFO - - 网站A: 登录状态验证失败(cookies可能已过期或选择器配置错误) +2026-03-02 23:14:53,782 - INFO - ==================== 开始处理用户: 用户B ==================== +2026-03-02 23:14:53,782 - INFO - 步骤1: 从 Cookie Cloud 获取所有 cookies +2026-03-02 23:14:54,433 - INFO - 获取到 814 个域名,共 2950 个 cookie +2026-03-02 23:14:54,433 - INFO - 步骤2: 一次性导入所有 cookies 到浏览器 +2026-03-02 23:14:58,023 - INFO - 开始导入 2950 个 cookie +2026-03-02 23:22:13,589 - INFO - Cookie 导入完成: 成功 2949, 失败 1 +2026-03-02 23:22:13,690 - WARNING - 部分 cookie 导入失败 +2026-03-02 23:22:13,701 - INFO - 步骤3: 依次验证各网站登录状态 +2026-03-02 23:22:13,704 - INFO - +验证网站: 网站A +2026-03-02 23:22:13,706 - INFO - URL: https://lmkbi.95155.com/bi-system/#/carUnpetrifiedComAll +2026-03-02 23:22:13,709 - ERROR - 验证登录状态失败: +与页面的连接已断开。 +版本: 4.1.0.18 +2026-03-02 23:22:13,710 - WARNING - ✗ 登录状态验证失败(cookies可能已过期或选择器配置错误) +2026-03-02 23:22:13,720 - INFO - +==================== 用户 用户B 处理结果汇总 ==================== +2026-03-02 23:22:13,732 - INFO - 总网站数: 1 +2026-03-02 23:22:13,748 - INFO - 成功: 0, 失败: 1 +2026-03-02 23:22:13,755 - INFO - +失败详情: +2026-03-02 23:22:13,759 - INFO - - 网站A: 登录状态验证失败(cookies可能已过期或选择器配置错误) +2026-03-02 23:22:14,205 - INFO - +================================================== +2026-03-02 23:22:14,206 - INFO - Cookie 监控结束 +2026-03-02 23:22:14,206 - INFO - ================================================== +2026-03-02 23:24:45,469 - INFO - ================================================== +2026-03-02 23:24:45,470 - INFO - Cookie 监控开始 +2026-03-02 23:24:45,470 - INFO - 时间: 2026-03-02 23:24:45 +2026-03-02 23:24:45,471 - INFO - ================================================== +2026-03-02 23:24:45,471 - INFO - ==================== 开始处理用户: 用户A ==================== +2026-03-02 23:24:45,472 - INFO - 步骤1: 从 Cookie Cloud 获取所有 cookies +2026-03-02 23:24:51,705 - INFO - 获取到 278 个域名,共 801 个 cookie +2026-03-02 23:24:51,705 - INFO - 步骤2: 一次性导入所有 cookies 到浏览器 +2026-03-02 23:25:00,772 - INFO - 开始导入 801 个 cookie +2026-03-02 23:26:59,941 - INFO - Cookie 导入完成: 成功 801, 失败 0 +2026-03-02 23:26:59,942 - INFO - 成功导入 801 个 cookie +2026-03-02 23:26:59,942 - INFO - 步骤3: 依次验证各网站登录状态 +2026-03-02 23:26:59,943 - INFO - +验证网站: 网站A +2026-03-02 23:26:59,943 - INFO - URL: https://lmkbi.95155.com/bi-system/#/carUnpetrifiedComAll +2026-03-02 23:27:14,779 - WARNING - ✗ 登录状态验证失败(cookies可能已过期或选择器配置错误) +2026-03-02 23:27:14,783 - INFO - +==================== 用户 用户A 处理结果汇总 ==================== +2026-03-02 23:27:14,785 - INFO - 总网站数: 1 +2026-03-02 23:27:14,786 - INFO - 成功: 0, 失败: 1 +2026-03-02 23:27:14,787 - INFO - +失败详情: +2026-03-02 23:27:14,788 - INFO - - 网站A: 登录状态验证失败(cookies可能已过期或选择器配置错误) +2026-03-02 23:27:15,886 - INFO - ==================== 开始处理用户: 用户B ==================== +2026-03-02 23:27:15,889 - INFO - 步骤1: 从 Cookie Cloud 获取所有 cookies +2026-03-02 23:27:16,629 - INFO - 获取到 814 个域名,共 2948 个 cookie +2026-03-02 23:27:16,631 - INFO - 步骤2: 一次性导入所有 cookies 到浏览器 +2026-03-02 23:27:18,380 - INFO - 开始导入 2948 个 cookie +2026-03-02 23:31:37,244 - INFO - Cookie 导入完成: 成功 2947, 失败 1 +2026-03-02 23:31:37,246 - WARNING - 部分 cookie 导入失败 +2026-03-02 23:31:37,247 - INFO - 步骤3: 依次验证各网站登录状态 +2026-03-02 23:31:37,248 - INFO - +验证网站: 网站A +2026-03-02 23:31:37,248 - INFO - URL: https://lmkbi.95155.com/bi-system/#/carUnpetrifiedComAll +2026-03-02 23:31:37,254 - ERROR - 验证登录状态失败: +与页面的连接已断开。 +版本: 4.1.0.18 +2026-03-02 23:31:37,256 - WARNING - ✗ 登录状态验证失败(cookies可能已过期或选择器配置错误) +2026-03-02 23:31:37,260 - INFO - +==================== 用户 用户B 处理结果汇总 ==================== +2026-03-02 23:31:37,263 - INFO - 总网站数: 1 +2026-03-02 23:31:37,264 - INFO - 成功: 0, 失败: 1 +2026-03-02 23:31:37,264 - INFO - +失败详情: +2026-03-02 23:31:37,265 - INFO - - 网站A: 登录状态验证失败(cookies可能已过期或选择器配置错误) +2026-03-02 23:31:38,233 - INFO - +================================================== +2026-03-02 23:31:38,235 - INFO - Cookie 监控结束 +2026-03-02 23:31:38,236 - INFO - ================================================== +2026-03-03 08:29:26,844 - INFO - ================================================== +2026-03-03 08:29:26,845 - INFO - Cookie 监控开始 +2026-03-03 08:29:26,845 - INFO - 时间: 2026-03-03 08:29:26 +2026-03-03 08:29:26,846 - INFO - ================================================== +2026-03-03 08:29:26,846 - INFO - ==================== 开始处理用户: 用户A ==================== +2026-03-03 08:29:26,846 - INFO - 步骤1: 从 Cookie Cloud 获取所有 cookies +2026-03-03 08:29:33,515 - INFO - 获取到 278 个域名,共 801 个 cookie +2026-03-03 08:29:33,515 - INFO - 步骤2: 一次性导入所有 cookies 到浏览器 +2026-03-03 08:29:34,818 - INFO - 开始导入 801 个 cookie +2026-03-03 08:34:05,206 - INFO - Cookie 导入完成: 成功 800, 失败 1 +2026-03-03 08:34:05,227 - WARNING - 部分 cookie 导入失败 +2026-03-03 08:34:05,228 - INFO - 步骤3: 依次验证各网站登录状态 +2026-03-03 08:34:05,234 - INFO - +验证网站: 网站A +2026-03-03 08:34:05,236 - INFO - URL: https://lmkbi.95155.com/bi-system/#/carUnpetrifiedComAll +2026-03-03 08:34:05,239 - ERROR - 验证登录状态失败: +与页面的连接已断开。 +版本: 4.1.0.18 +2026-03-03 08:34:05,240 - WARNING - ✗ 登录状态验证失败(cookies可能已过期或选择器配置错误) +2026-03-03 08:34:05,255 - INFO - +==================== 用户 用户A 处理结果汇总 ==================== +2026-03-03 08:34:05,259 - INFO - 总网站数: 1 +2026-03-03 08:34:05,261 - INFO - 成功: 0, 失败: 1 +2026-03-03 08:34:05,270 - INFO - +失败详情: +2026-03-03 08:34:05,271 - INFO - - 网站A: 登录状态验证失败(cookies可能已过期或选择器配置错误) +2026-03-03 08:34:05,974 - INFO - ==================== 开始处理用户: 用户B ==================== +2026-03-03 08:34:05,976 - INFO - 步骤1: 从 Cookie Cloud 获取所有 cookies +2026-03-03 08:34:06,647 - INFO - 获取到 813 个域名,共 2940 个 cookie +2026-03-03 08:34:06,648 - INFO - 步骤2: 一次性导入所有 cookies 到浏览器 +2026-03-03 08:34:09,040 - INFO - 开始导入 2940 个 cookie +2026-03-03 08:47:28,987 - INFO - Cookie 导入完成: 成功 2939, 失败 1 +2026-03-03 08:47:29,120 - WARNING - 部分 cookie 导入失败 +2026-03-03 08:47:29,235 - INFO - 步骤3: 依次验证各网站登录状态 +2026-03-03 08:47:29,271 - INFO - +验证网站: 网站A +2026-03-03 08:47:29,313 - INFO - URL: https://lmkbi.95155.com/bi-system/#/carUnpetrifiedComAll +2026-03-03 08:47:29,334 - ERROR - 验证登录状态失败: +与页面的连接已断开。 +版本: 4.1.0.18 +2026-03-03 08:47:29,359 - WARNING - ✗ 登录状态验证失败(cookies可能已过期或选择器配置错误) +2026-03-03 08:47:30,226 - INFO - 已发送失败通知: 用户B - 网站A +2026-03-03 08:47:30,227 - INFO - +==================== 用户 用户B 处理结果汇总 ==================== +2026-03-03 08:47:30,229 - INFO - 总网站数: 1 +2026-03-03 08:47:30,230 - INFO - 成功: 0, 失败: 1 +2026-03-03 08:47:30,231 - INFO - +失败详情: +2026-03-03 08:47:30,234 - INFO - - 网站A: 登录状态验证失败(cookies可能已过期或选择器配置错误) +2026-03-03 08:47:30,235 - INFO - +================================================== +2026-03-03 08:47:30,236 - INFO - Cookie 监控结束 +2026-03-03 08:47:30,237 - INFO - ================================================== +2026-03-03 08:47:50,074 - INFO - ================================================== +2026-03-03 08:47:50,075 - INFO - Cookie 监控开始 +2026-03-03 08:47:50,076 - INFO - 时间: 2026-03-03 08:47:50 +2026-03-03 08:47:50,076 - INFO - ================================================== +2026-03-03 08:47:50,077 - INFO - ==================== 开始处理用户: 用户A ==================== +2026-03-03 08:47:50,077 - INFO - 步骤1: 从 Cookie Cloud 获取所有 cookies +2026-03-03 08:47:56,370 - INFO - 获取到 278 个域名,共 801 个 cookie +2026-03-03 08:47:56,371 - INFO - 步骤2: 一次性导入所有 cookies 到浏览器 +2026-03-03 08:48:03,542 - INFO - 开始导入 801 个 cookie +2026-03-03 08:49:19,995 - INFO - Cookie 导入完成: 成功 800, 失败 1 +2026-03-03 08:49:19,995 - WARNING - 部分 cookie 导入失败 +2026-03-03 08:49:19,996 - INFO - 步骤3: 依次验证各网站登录状态 +2026-03-03 08:49:19,996 - INFO - +验证网站: 网站A +2026-03-03 08:49:19,997 - INFO - URL: https://lmkbi.95155.com/bi-system/#/carUnpetrifiedComAll +2026-03-03 08:49:20,040 - ERROR - 验证登录状态失败: +与页面的连接已断开。 +版本: 4.1.0.18 +2026-03-03 08:49:20,042 - WARNING - ✗ 登录状态验证失败(cookies可能已过期或选择器配置错误) +2026-03-03 08:49:22,005 - INFO - 已发送失败通知: 用户A - 网站A +2026-03-03 08:49:22,007 - INFO - +==================== 用户 用户A 处理结果汇总 ==================== +2026-03-03 08:49:22,007 - INFO - 总网站数: 1 +2026-03-03 08:49:22,008 - INFO - 成功: 0, 失败: 1 +2026-03-03 08:49:22,008 - INFO - +失败详情: +2026-03-03 08:49:22,009 - INFO - - 网站A: 登录状态验证失败(cookies可能已过期或选择器配置错误) +2026-03-03 08:49:22,009 - INFO - ==================== 开始处理用户: 用户B ==================== +2026-03-03 08:49:22,009 - INFO - 步骤1: 从 Cookie Cloud 获取所有 cookies +2026-03-03 08:49:22,809 - INFO - 获取到 813 个域名,共 2940 个 cookie +2026-03-03 08:49:22,819 - INFO - 步骤2: 一次性导入所有 cookies 到浏览器 +2026-03-03 08:49:30,875 - INFO - 开始导入 2940 个 cookie +2026-03-03 08:49:43,208 - INFO - Cookie 导入完成: 成功 2939, 失败 1 +2026-03-03 08:49:43,209 - WARNING - 部分 cookie 导入失败 +2026-03-03 08:49:43,209 - INFO - 步骤3: 依次验证各网站登录状态 +2026-03-03 08:49:43,209 - INFO - +验证网站: 网站A +2026-03-03 08:49:43,210 - INFO - URL: https://lmkbi.95155.com/bi-system/#/carUnpetrifiedComAll +2026-03-03 08:49:43,212 - ERROR - 验证登录状态失败: +与页面的连接已断开。 +版本: 4.1.0.18 +2026-03-03 08:49:43,214 - WARNING - ✗ 登录状态验证失败(cookies可能已过期或选择器配置错误) +2026-03-03 08:49:43,219 - INFO - +==================== 用户 用户B 处理结果汇总 ==================== +2026-03-03 08:49:43,223 - INFO - 总网站数: 1 +2026-03-03 08:49:43,227 - INFO - 成功: 0, 失败: 1 +2026-03-03 08:49:43,236 - INFO - +失败详情: +2026-03-03 08:49:43,236 - INFO - - 网站A: 登录状态验证失败(cookies可能已过期或选择器配置错误) +2026-03-03 08:49:43,691 - INFO - +================================================== +2026-03-03 08:49:43,692 - INFO - Cookie 监控结束 +2026-03-03 08:49:43,692 - INFO - ================================================== diff --git a/moviepilot_cookie_demo.py b/moviepilot_cookie_demo.py new file mode 100644 index 0000000..59727fd --- /dev/null +++ b/moviepilot_cookie_demo.py @@ -0,0 +1,394 @@ +""" +MoviePilot Cookie功能示例程序 +从CookieCloud下载数据并注入浏览器访问网站 +""" + +import json +import time +import hashlib +import base64 +from typing import Dict, List, Optional, Tuple +from dataclasses import dataclass +import urllib.request +import urllib.error +from Crypto.Cipher import AES + + +@dataclass +class CookieCloudConfig: + """CookieCloud配置""" + server: str + username: str + password: str + timeout: int = 30 + + +class CookieCloudDownloader: + """从CookieCloud服务器下载cookie数据""" + + def __init__(self, config: CookieCloudConfig): + self.config = config + + def _get_crypt_key(self) -> bytes: + combined_string = f"{self.config.username}-{self.config.password}" + return hashlib.md5(combined_string.encode('utf-8')).hexdigest()[:16].encode("utf-8") + + def _bytes_to_key(self, data: bytes, salt: bytes, output: int = 48) -> bytes: + assert len(salt) == 8, len(salt) + data += salt + key = hashlib.md5(data).digest() + final_key = key + while len(final_key) < output: + key = hashlib.md5(key + data).digest() + final_key += key + return final_key[:output] + + def _decrypt(self, encrypted: str, passphrase: bytes) -> bytes: + encrypted_bytes = base64.b64decode(encrypted) + assert encrypted_bytes.startswith(b"Salted__"), "Invalid encrypted data format" + salt = encrypted_bytes[8:16] + key_iv = self._bytes_to_key(passphrase, salt, 32 + 16) + key = key_iv[:32] + iv = key_iv[32:] + aes = AES.new(key, AES.MODE_CBC, iv) + decrypted_padded = aes.decrypt(encrypted_bytes[16:]) + padding_length = decrypted_padded[-1] + if isinstance(padding_length, str): + padding_length = ord(padding_length) + return decrypted_padded[:-padding_length] + + def _get_url_domain(self, domain: str) -> str: + if not domain: + return "" + domain = domain.lstrip('.') + if ":" in domain: + domain = domain.split(":")[0] + parts = domain.split(".") + if all(part.isdigit() for part in parts): + return domain + if len(parts) >= 2: + return ".".join(parts[-2:]) + return domain + + def download_all(self) -> Tuple[Optional[Dict], str]: + """下载所有cookie和local storage数据""" + try: + url = f"{self.config.server}/get/{self.config.username}" + request = urllib.request.Request( + url, + headers={ + 'Content-Type': 'application/json', + 'User-Agent': 'MoviePilot-Cookie-Client/1.0' + }, + method='GET' + ) + + response = urllib.request.urlopen(request, timeout=self.config.timeout) + + if response.status != 200: + return None, f"服务器返回错误状态码: {response.status}" + + result = json.loads(response.read().decode('utf-8')) + + if not result: + return None, "服务器返回数据为空" + + encrypted = result.get("encrypted") + if not encrypted: + return None, "未获取到cookie密文" + + crypt_key = self._get_crypt_key() + try: + decrypted_data = self._decrypt(encrypted, crypt_key) + result = json.loads(decrypted_data.decode("utf-8")) + except Exception as e: + return None, f"cookie解密失败: {str(e)}" + + if not result: + return None, "cookie解密为空" + + cookie_data = result.get("cookie_data", {}) + local_storage_data = result.get("local_storage_data", {}) + + total_cookies = sum(len(v) for v in cookie_data.values()) + total_ls = sum(len(v) for v in local_storage_data.values()) + print(f" 下载完成: {len(cookie_data)} 个域名的Cookie({total_cookies}个), {len(local_storage_data)} 个域名的Local Storage({total_ls}个)") + + return {"cookies": cookie_data, "local_storage": local_storage_data}, "" + + except urllib.error.HTTPError as e: + if e.code == 401: + return None, "认证失败,请检查用户名和密码" + else: + return None, f"HTTP错误: {e.code} {e.reason}" + except urllib.error.URLError as e: + return None, f"网络连接失败: {e.reason}" + except Exception as e: + return None, f"下载失败: {str(e)}" + + +class BrowserController: + """使用Playwright控制浏览器""" + + def __init__(self, headless: bool = False, use_edge: bool = False): + self.headless = headless + self.use_edge = use_edge + self.browser = None + self.context = None + self.page = None + self.playwright = None + + def start(self) -> bool: + try: + from playwright.sync_api import sync_playwright + + self.playwright = sync_playwright().start() + + if self.use_edge: + self.browser = self.playwright.chromium.launch( + channel="msedge", + headless=self.headless, + args=['--disable-blink-features=AutomationControlled', '--disable-dev-shm-usage', '--no-sandbox'] + ) + else: + self.browser = self.playwright.chromium.launch( + headless=self.headless, + args=['--disable-blink-features=AutomationControlled', '--disable-dev-shm-usage', '--no-sandbox'] + ) + + self.context = self.browser.new_context( + user_agent='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36' + ) + + self.page = self.context.new_page() + + print(f"✓ 浏览器启动成功 ({'Edge' if self.use_edge else 'Chromium'})") + return True + + except Exception as e: + print(f"✗ 浏览器启动失败: {str(e)}") + return False + + def inject_cookies(self, cookies: Dict): + """注入cookie到浏览器""" + if not self.context or not cookies: + return + + try: + playwright_cookies = [] + for domain_key, cookie_list in cookies.items(): + for cookie in cookie_list: + cookie_domain = cookie.get("domain", "").lstrip('.') + if not cookie_domain: + cookie_domain = domain_key.lstrip('.') + + pw_cookie = { + "name": cookie.get("name", ""), + "value": cookie.get("value", ""), + "domain": cookie_domain, + "path": cookie.get("path", "/"), + } + if cookie.get("secure"): + pw_cookie["secure"] = True + if cookie.get("httpOnly"): + pw_cookie["httpOnly"] = True + if cookie.get("expirationDate"): + pw_cookie["expires"] = int(cookie.get("expirationDate")) + playwright_cookies.append(pw_cookie) + + self.context.add_cookies(playwright_cookies) + print(f"✓ Cookie注入成功 ({len(playwright_cookies)} 个)") + except Exception as e: + print(f"✗ Cookie注入失败: {str(e)}") + + def navigate(self, url: str, wait_time: int = 5, timeout: int = 60) -> bool: + if not self.page: + return False + + try: + print(f"正在访问: {url}") + + self.page.goto(url, timeout=timeout * 1000) + + try: + self.page.wait_for_load_state("networkidle", timeout=timeout * 1000) + except Exception: + print(" networkidle超时,尝试load状态...") + try: + self.page.wait_for_load_state("load", timeout=30000) + except Exception: + print(" load超时,尝试domcontentloaded状态...") + self.page.wait_for_load_state("domcontentloaded", timeout=10000) + + time.sleep(wait_time) + + print(f"✓ 页面加载完成") + return True + + except Exception as e: + print(f"✗ 页面访问失败: {str(e)}") + return False + + def check_login_status(self, login_indicator: str) -> bool: + if not self.page: + return False + + try: + html_content = self.page.content() + + if login_indicator: + if login_indicator in html_content: + print(f"✓ 检测到登录指示器: {login_indicator}") + return True + else: + print(f"✗ 未检测到登录指示器: {login_indicator}") + return False + + return False + + except Exception as e: + print(f"✗ 登录状态检查失败: {str(e)}") + return False + + def take_screenshot(self, filepath: str): + if not self.page: + return + + try: + self.page.screenshot(path=filepath) + print(f"✓ 截图已保存: {filepath}") + except Exception as e: + print(f"✗ 截图失败: {str(e)}") + + def close(self): + try: + if self.browser: + self.browser.close() + + if self.playwright: + self.playwright.stop() + + print("✓ 浏览器已关闭") + + except Exception as e: + print(f"✗ 关闭浏览器失败: {str(e)}") + + +class CookieDemo: + """Cookie功能示例""" + + def __init__(self, cookiecloud_config: CookieCloudConfig): + self.downloader = CookieCloudDownloader(cookiecloud_config) + self.browser = None + + def run_sites(self, sites: list, headless: bool = False, use_edge: bool = False): + """下载一次数据,依次访问多个网站""" + # 下载数据 + print("=" * 60) + print("从CookieCloud下载数据") + print("=" * 60) + + data, error = self.downloader.download_all() + + if error: + print(f"✗ 数据下载失败: {error}") + return + + # 启动浏览器 + print(f"\n启动浏览器") + print("-" * 60) + + self.browser = BrowserController(headless=headless, use_edge=use_edge) + + if not self.browser.start(): + return + + # 注入所有cookie + if data and data.get("cookies"): + print(f"\n注入所有cookie到浏览器") + print("-" * 60) + self.browser.inject_cookies(data["cookies"]) + + # 依次访问每个网站 + for idx, site in enumerate(sites, 1): + print(f"\n\n{'=' * 60}") + print(f"访问网站 {idx}/{len(sites)}: {site['name']}") + print(f"URL: {site['url']}") + print("=" * 60) + + if not self.browser.navigate(site['url']): + print(f"✗ 页面访问失败") + continue + + # 验证登录状态 + from urllib.parse import urlparse + parsed = urlparse(site['url']) + target_domain = parsed.netloc + + login_indicator = site.get('login_indicator') + is_logged_in = self.browser.check_login_status(login_indicator) + + if is_logged_in: + print("✓ Cookie验证成功:已登录状态") + else: + print("⚠ Cookie验证失败:未检测到登录状态") + + # 截图 + screenshot_path = f"{target_domain.replace('.', '_').replace(':', '_')}_screenshot.png" + self.browser.take_screenshot(screenshot_path) + + if idx < len(sites): + print("\n等待3秒后继续下一个网站...") + time.sleep(3) + + # 关闭浏览器 + print(f"\n关闭浏览器") + print("-" * 60) + self.browser.close() + + print("\n" + "=" * 60) + print("所有网站访问完成") + print("=" * 60) + + +def main(): + # 配置CookieCloud + config = CookieCloudConfig( + server="http://192.168.1.100:3000/cookiecloud", + username="qyZpTrgiP5mVwiRZwfBhjz", + password="iGn1B4FnWftp3oj4Ko3jxA", + timeout=30 + ) + + # 创建示例程序 + demo = CookieDemo(config) + + # 测试网站列表 + test_sites = [ + { + "url": "https://lmkbi.95155.com/bi-system/#/login", + "name": "BI系统", + "login_indicator": "彭峰" + }, + { + "url": "http://192.168.1.100:3000/#/subscribe/movie", + "name": "MoviePilot订阅", + "login_indicator": "搜索" + }, + { + "url": "http://192.168.1.102:8418/bwadmin/QueryCarInfo2", + "name": "QueryCarInfo2", + "login_indicator": "工单管理" + } + ] + + # 下载一次数据,依次访问所有网站 + demo.run_sites( + sites=test_sites, + headless=False, + use_edge=True + ) + + +if __name__ == "__main__": + main() diff --git a/state.json b/state.json index e474d5d..5f3b5ad 100644 --- a/state.json +++ b/state.json @@ -1,3 +1,4 @@ { - "用户A_网站A": 2 + "用户A_网站A": 0, + "用户B_网站A": 1 } \ No newline at end of file