344 lines
12 KiB
Python
Executable File
344 lines
12 KiB
Python
Executable File
#!/usr/bin/env python3
|
||
"""
|
||
小红书Cookie管理器
|
||
用于管理Cookie的导入、验证和更新
|
||
"""
|
||
|
||
import json
|
||
import os
|
||
import sys
|
||
import time
|
||
from datetime import datetime
|
||
import subprocess
|
||
|
||
class XiaohongshuCookieManager:
|
||
"""小红书Cookie管理器"""
|
||
|
||
def __init__(self):
|
||
self.cookie_dir = os.path.expanduser("~/.xiaohongshu-cli")
|
||
self.cookie_file = os.path.join(self.cookie_dir, "cookies.json")
|
||
self.backup_dir = os.path.join(self.cookie_dir, "backups")
|
||
|
||
# 确保目录存在
|
||
os.makedirs(self.cookie_dir, exist_ok=True)
|
||
os.makedirs(self.backup_dir, exist_ok=True)
|
||
|
||
def backup_current_cookies(self):
|
||
"""备份当前Cookie"""
|
||
if os.path.exists(self.cookie_file):
|
||
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||
backup_file = os.path.join(self.backup_dir, f"cookies_{timestamp}.json")
|
||
|
||
with open(self.cookie_file, 'r', encoding='utf-8') as f:
|
||
cookies = json.load(f)
|
||
|
||
with open(backup_file, 'w', encoding='utf-8') as f:
|
||
json.dump(cookies, f, indent=2, ensure_ascii=False)
|
||
|
||
print(f"✅ Cookie已备份到: {backup_file}")
|
||
return backup_file
|
||
return None
|
||
|
||
def import_from_editthiscookie(self, editthis_file):
|
||
"""
|
||
从EditThisCookie导出的文件导入Cookie
|
||
EditThisCookie导出的是列表格式,需要转换为字典格式
|
||
"""
|
||
print(f"📥 从EditThisCookie文件导入: {editthis_file}")
|
||
|
||
try:
|
||
with open(editthis_file, 'r', encoding='utf-8') as f:
|
||
cookies_list = json.load(f)
|
||
|
||
print(f" 原始Cookie数量: {len(cookies_list)}")
|
||
|
||
# 转换为字典格式
|
||
cookies_dict = {}
|
||
for cookie in cookies_list:
|
||
name = cookie.get('name', '')
|
||
value = cookie.get('value', '')
|
||
|
||
if name and value:
|
||
cookies_dict[name] = value
|
||
|
||
print(f" 转换后Cookie数量: {len(cookies_dict)}")
|
||
|
||
# 备份当前Cookie
|
||
self.backup_current_cookies()
|
||
|
||
# 保存新Cookie
|
||
with open(self.cookie_file, 'w', encoding='utf-8') as f:
|
||
json.dump(cookies_dict, f, indent=2, ensure_ascii=False)
|
||
|
||
print(f"✅ Cookie已导入到: {self.cookie_file}")
|
||
|
||
# 显示关键Cookie
|
||
self.show_key_cookies(cookies_dict)
|
||
|
||
return True
|
||
|
||
except Exception as e:
|
||
print(f"❌ 导入失败: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
return False
|
||
|
||
def import_from_browser_export(self, export_file):
|
||
"""
|
||
从浏览器导出的Cookie文件导入
|
||
支持多种格式
|
||
"""
|
||
print(f"📥 从浏览器导出文件导入: {export_file}")
|
||
|
||
try:
|
||
with open(export_file, 'r', encoding='utf-8') as f:
|
||
content = f.read()
|
||
|
||
cookies_dict = {}
|
||
|
||
# 尝试解析不同格式
|
||
if content.strip().startswith('['):
|
||
# JSON列表格式(EditThisCookie)
|
||
cookies_list = json.loads(content)
|
||
for cookie in cookies_list:
|
||
name = cookie.get('name', '')
|
||
value = cookie.get('value', '')
|
||
if name and value:
|
||
cookies_dict[name] = value
|
||
|
||
elif '=' in content and '\n' in content:
|
||
# Netscape格式(浏览器导出)
|
||
for line in content.split('\n'):
|
||
if line.strip() and not line.startswith('#'):
|
||
parts = line.strip().split('\t')
|
||
if len(parts) >= 7:
|
||
name = parts[5]
|
||
value = parts[6]
|
||
cookies_dict[name] = value
|
||
|
||
else:
|
||
# 尝试作为JSON字典
|
||
try:
|
||
cookies_dict = json.loads(content)
|
||
except:
|
||
print("❌ 无法识别的Cookie格式")
|
||
return False
|
||
|
||
print(f" 解析到Cookie数量: {len(cookies_dict)}")
|
||
|
||
# 备份当前Cookie
|
||
self.backup_current_cookies()
|
||
|
||
# 保存新Cookie
|
||
with open(self.cookie_file, 'w', encoding='utf-8') as f:
|
||
json.dump(cookies_dict, f, indent=2, ensure_ascii=False)
|
||
|
||
print(f"✅ Cookie已导入到: {self.cookie_file}")
|
||
|
||
# 显示关键Cookie
|
||
self.show_key_cookies(cookies_dict)
|
||
|
||
return True
|
||
|
||
except Exception as e:
|
||
print(f"❌ 导入失败: {e}")
|
||
return False
|
||
|
||
def show_key_cookies(self, cookies_dict):
|
||
"""显示关键Cookie信息"""
|
||
print("\n🔑 关键Cookie:")
|
||
|
||
key_cookies = ['a1', 'webId', 'web_session', 'x-user-id-creator.xiaohongshu.com']
|
||
|
||
for key in key_cookies:
|
||
if key in cookies_dict:
|
||
value = cookies_dict[key]
|
||
print(f" ✅ {key}: {value[:30]}...")
|
||
else:
|
||
print(f" ❌ {key}: 不存在")
|
||
|
||
def check_cookie_validity(self):
|
||
"""检查Cookie有效性"""
|
||
print("🔍 检查Cookie有效性...")
|
||
|
||
if not os.path.exists(self.cookie_file):
|
||
print("❌ Cookie文件不存在")
|
||
return False
|
||
|
||
try:
|
||
# 使用xhs命令检查登录状态
|
||
result = subprocess.run(
|
||
['xhs', 'status', '--json'],
|
||
capture_output=True,
|
||
text=True,
|
||
timeout=10
|
||
)
|
||
|
||
if result.returncode == 0:
|
||
data = json.loads(result.stdout)
|
||
if data.get('ok') and data.get('data', {}).get('authenticated'):
|
||
user = data['data']['user']
|
||
print(f"✅ Cookie有效")
|
||
print(f" 用户ID: {user.get('id')}")
|
||
print(f" 用户名: {user.get('username')}")
|
||
print(f" 昵称: {user.get('nickname')}")
|
||
return True
|
||
else:
|
||
print(f"❌ Cookie无效: {data}")
|
||
return False
|
||
else:
|
||
print(f"❌ 检查失败: {result.stderr}")
|
||
return False
|
||
|
||
except subprocess.TimeoutExpired:
|
||
print("⏰ 检查超时")
|
||
return False
|
||
except Exception as e:
|
||
print(f"❌ 检查异常: {e}")
|
||
return False
|
||
|
||
def get_cookie_info(self):
|
||
"""获取Cookie详细信息"""
|
||
print("📋 Cookie信息:")
|
||
|
||
if not os.path.exists(self.cookie_file):
|
||
print("❌ Cookie文件不存在")
|
||
return
|
||
|
||
try:
|
||
with open(self.cookie_file, 'r', encoding='utf-8') as f:
|
||
cookies = json.load(f)
|
||
|
||
print(f" 文件: {self.cookie_file}")
|
||
print(f" 大小: {os.path.getsize(self.cookie_file)} 字节")
|
||
print(f" Cookie数量: {len(cookies)}")
|
||
print(f" 修改时间: {datetime.fromtimestamp(os.path.getmtime(self.cookie_file)).strftime('%Y-%m-%d %H:%M:%S')}")
|
||
|
||
# 显示关键Cookie
|
||
self.show_key_cookies(cookies)
|
||
|
||
except Exception as e:
|
||
print(f"❌ 读取失败: {e}")
|
||
|
||
def list_backups(self):
|
||
"""列出所有备份"""
|
||
print("📚 Cookie备份列表:")
|
||
|
||
if not os.path.exists(self.backup_dir):
|
||
print(" 无备份")
|
||
return
|
||
|
||
backups = []
|
||
for filename in os.listdir(self.backup_dir):
|
||
if filename.startswith('cookies_') and filename.endswith('.json'):
|
||
filepath = os.path.join(self.backup_dir, filename)
|
||
mtime = os.path.getmtime(filepath)
|
||
size = os.path.getsize(filepath)
|
||
|
||
backups.append({
|
||
'filename': filename,
|
||
'path': filepath,
|
||
'mtime': mtime,
|
||
'size': size
|
||
})
|
||
|
||
if not backups:
|
||
print(" 无备份")
|
||
return
|
||
|
||
backups.sort(key=lambda x: x['mtime'], reverse=True)
|
||
|
||
for i, backup in enumerate(backups[:10], 1):
|
||
time_str = datetime.fromtimestamp(backup['mtime']).strftime('%Y-%m-%d %H:%M:%S')
|
||
print(f" {i}. {backup['filename']}")
|
||
print(f" 时间: {time_str}, 大小: {backup['size']} 字节")
|
||
|
||
def restore_backup(self, backup_filename):
|
||
"""从备份恢复Cookie"""
|
||
backup_file = os.path.join(self.backup_dir, backup_filename)
|
||
|
||
if not os.path.exists(backup_file):
|
||
print(f"❌ 备份文件不存在: {backup_filename}")
|
||
return False
|
||
|
||
print(f"🔄 从备份恢复: {backup_filename}")
|
||
|
||
try:
|
||
# 备份当前Cookie
|
||
self.backup_current_cookies()
|
||
|
||
# 恢复备份
|
||
with open(backup_file, 'r', encoding='utf-8') as f:
|
||
cookies = json.load(f)
|
||
|
||
with open(self.cookie_file, 'w', encoding='utf-8') as f:
|
||
json.dump(cookies, f, indent=2, ensure_ascii=False)
|
||
|
||
print(f"✅ Cookie已从备份恢复")
|
||
|
||
# 验证恢复的Cookie
|
||
return self.check_cookie_validity()
|
||
|
||
except Exception as e:
|
||
print(f"❌ 恢复失败: {e}")
|
||
return False
|
||
|
||
def main():
|
||
"""主函数"""
|
||
import argparse
|
||
|
||
parser = argparse.ArgumentParser(description='小红书Cookie管理器')
|
||
subparsers = parser.add_subparsers(dest='command', help='命令')
|
||
|
||
# import命令
|
||
import_parser = subparsers.add_parser('import', help='导入Cookie')
|
||
import_parser.add_argument('file', help='Cookie文件路径')
|
||
import_parser.add_argument('--type', choices=['editthis', 'browser'], default='editthis',
|
||
help='文件类型 (editthis: EditThisCookie导出, browser: 浏览器导出)')
|
||
|
||
# check命令
|
||
subparsers.add_parser('check', help='检查Cookie有效性')
|
||
|
||
# info命令
|
||
subparsers.add_parser('info', help='显示Cookie信息')
|
||
|
||
# backup命令
|
||
subparsers.add_parser('backup', help='备份当前Cookie')
|
||
|
||
# list命令
|
||
subparsers.add_parser('list', help='列出备份')
|
||
|
||
# restore命令
|
||
restore_parser = subparsers.add_parser('restore', help='从备份恢复')
|
||
restore_parser.add_argument('backup', help='备份文件名')
|
||
|
||
args = parser.parse_args()
|
||
|
||
manager = XiaohongshuCookieManager()
|
||
|
||
if args.command == 'import':
|
||
if args.type == 'editthis':
|
||
manager.import_from_editthiscookie(args.file)
|
||
else:
|
||
manager.import_from_browser_export(args.file)
|
||
|
||
elif args.command == 'check':
|
||
manager.check_cookie_validity()
|
||
|
||
elif args.command == 'info':
|
||
manager.get_cookie_info()
|
||
|
||
elif args.command == 'backup':
|
||
manager.backup_current_cookies()
|
||
|
||
elif args.command == 'list':
|
||
manager.list_backups()
|
||
|
||
elif args.command == 'restore':
|
||
manager.restore_backup(args.backup)
|
||
|
||
else:
|
||
parser.print_help()
|
||
|
||
if __name__ == "__main__":
|
||
main() |