232 lines
7.7 KiB
Python
232 lines
7.7 KiB
Python
|
|
# -*- 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()
|