Initial commit with translated description

This commit is contained in:
2026-03-29 13:03:48 +08:00
commit 4d7c15be2b
10 changed files with 2548 additions and 0 deletions

231
scripts/smart_login.py Normal file
View File

@@ -0,0 +1,231 @@
# -*- coding: utf-8 -*-
"""
智能登录脚本 - 先静默尝试,失败再显示窗口
支持断点续传和错误恢复
"""
from DrissionPage import ChromiumPage, ChromiumOptions
import time
import json
import sys
from pathlib import Path
from datetime import datetime
SESSIONS_DIR = Path.home() / '.clawdbot' / 'browser-sessions'
ATTEMPTS_LOG = Path.home() / '.clawdbot' / 'browser-sessions' / 'attempts.json'
# 设置输出编码
if sys.platform == 'win32':
sys.stdout.reconfigure(encoding='utf-8', errors='replace')
sys.stderr.reconfigure(encoding='utf-8', errors='replace')
def log_attempt(url, success, method, notes=''):
"""记录尝试过的网站"""
SESSIONS_DIR.mkdir(parents=True, exist_ok=True)
attempts = []
if ATTEMPTS_LOG.exists():
attempts = json.loads(ATTEMPTS_LOG.read_text())
attempts.append({
'url': url,
'success': success,
'method': method, # headless / headed
'notes': notes,
'timestamp': time.time()
})
ATTEMPTS_LOG.write_text(json.dumps(attempts, indent=2, ensure_ascii=False))
def get_browser(headless=True):
"""获取浏览器实例"""
options = ChromiumOptions()
if headless:
options.headless()
options.set_argument('--disable-blink-features=AutomationControlled')
options.set_argument('--no-sandbox')
options.set_argument('--disable-dev-shm-usage')
options.set_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'
)
return ChromiumPage(options)
def save_session(page, name):
"""保存session"""
SESSIONS_DIR.mkdir(parents=True, exist_ok=True)
session_data = {
'name': name,
'url': page.url,
'title': page.title,
'cookies': dict(page.cookies()), # 用dict()转换
'timestamp': time.time()
}
path = SESSIONS_DIR / f'{name}.json'
path.write_text(json.dumps(session_data, indent=2, ensure_ascii=False))
print(f'Session saved: {path}')
return path
def load_session(page, name):
"""加载session"""
path = SESSIONS_DIR / f'{name}.json'
if not path.exists():
return False
data = json.loads(path.read_text())
for k, v in data.get('cookies', {}).items():
try:
page.set.cookies({k: v})
except:
pass
return True
def smart_login(url, session_name, account=None, password=None):
"""
智能登录 - 先静默尝试,失败再显示窗口
"""
print(f'=== 登录 {url} ===')
# 第一步静默尝试加载已有session
print('1. 尝试静默加载已有session...')
page = get_browser(headless=True)
try:
page.get(url)
time.sleep(2)
if load_session(page, session_name):
page.refresh()
time.sleep(3)
# 检查是否登录成功(简单检查:不在登录页)
if 'login' not in page.url.lower() and 'passport' not in page.url.lower():
print('已有session有效登录成功')
log_attempt(url, True, 'headless', 'session_reuse')
return page
print('无有效session')
except Exception as e:
print(f'静默尝试失败: {e}')
finally:
page.quit()
# 第二步:如果有账号密码,尝试静默登录
if account and password:
print('2. 尝试静默自动登录...')
page = get_browser(headless=True)
try:
page.get(url)
time.sleep(3)
# 找输入框
inputs = page.eles('tag:input')
text_input = None
pwd_input = None
for inp in inputs:
t = inp.attr('type') or ''
if t == 'text' or t == 'tel' or t == 'email':
if not text_input:
text_input = inp
if t == 'password':
pwd_input = inp
if text_input and pwd_input:
text_input.clear()
text_input.input(account)
time.sleep(0.3)
pwd_input.clear()
pwd_input.input(password)
time.sleep(0.3)
# 找登录按钮并点击
btn = page.ele('tag:button') or page.ele('.btn') or page.ele('[type=submit]')
if btn:
try:
page.run_js('arguments[0].click()', btn)
except:
btn.click()
time.sleep(5)
# 检查是否成功
if 'login' not in page.url.lower() and 'passport' not in page.url.lower():
print('静默登录成功!')
save_session(page, session_name)
log_attempt(url, True, 'headless', 'auto_login')
return page
else:
print('可能需要验证码,切换到显示模式')
except Exception as e:
print(f'静默登录失败: {e}')
finally:
page.quit()
# 第三步:显示窗口让用户手动操作
print('3. 打开浏览器窗口,请手动登录...')
page = get_browser(headless=False)
try:
page.get(url)
# 如果有账号密码,先填入
if account and password:
time.sleep(3)
inputs = page.eles('tag:input')
for inp in inputs:
t = inp.attr('type') or ''
if t in ('text', 'tel', 'email'):
inp.clear()
inp.input(account)
elif t == 'password':
inp.clear()
inp.input(password)
# 点击登录按钮
time.sleep(0.5)
btns = page.eles('tag:button')
for btn in btns:
txt = btn.text.lower() if btn.text else ''
if '登录' in txt or 'login' in txt or '登入' in txt:
try:
page.run_js('arguments[0].click()', btn)
print('已自动点击登录按钮')
except:
pass
break
# 等待登录完成
print('等待登录完成...(检测到跳转后自动保存)')
original_url = page.url
for i in range(120): # 最多等2分钟
time.sleep(1)
current = page.url
if 'login' not in current.lower() and 'passport' not in current.lower():
if current != original_url:
print('检测到登录成功!')
time.sleep(2) # 等cookie写入
break
if i % 15 == 0 and i > 0:
print(f'等待中... {i}')
save_session(page, session_name)
log_attempt(url, True, 'headed', 'manual_login')
print('登录完成session已保存')
return page
except Exception as e:
print(f'错误: {e}')
log_attempt(url, False, 'headed', str(e))
page.quit()
return None
if __name__ == '__main__':
import sys
if len(sys.argv) < 3:
print('Usage: python smart_login.py <url> <session_name> [account] [password]')
sys.exit(1)
url = sys.argv[1]
name = sys.argv[2]
account = sys.argv[3] if len(sys.argv) > 3 else None
password = sys.argv[4] if len(sys.argv) > 4 else None
page = smart_login(url, name, account, password)
if page:
print(f'当前页面: {page.title}')
print(f'URL: {page.url}')
page.quit()