chore: auto release commit (2026-09-14 15:11:41)
All checks were successful
技能自动化发布 / release (push) Successful in 9s
All checks were successful
技能自动化发布 / release (push) Successful in 9s
This commit is contained in:
208
scripts/service/rpa/login_handler.py
Normal file
208
scripts/service/rpa/login_handler.py
Normal file
@@ -0,0 +1,208 @@
|
||||
"""登录处理器:处理网站登录流程。"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import time
|
||||
from typing import Any, Optional
|
||||
|
||||
from jiangchang_skill_core.activity import interruptible_sleep
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# 登录相关选择器(需要根据实际页面调整)
|
||||
LOGIN_SELECTORS = {
|
||||
"username_input": [
|
||||
"input[name='username']",
|
||||
"input[placeholder*='用户名']",
|
||||
"input[placeholder*='账号']",
|
||||
"#username",
|
||||
"[data-testid='username']",
|
||||
],
|
||||
"password_input": [
|
||||
"input[name='password']",
|
||||
"input[type='password']",
|
||||
"#password",
|
||||
"[data-testid='password']",
|
||||
],
|
||||
"login_button": [
|
||||
"button[type='submit']",
|
||||
"button:has-text('登录')",
|
||||
"input[type='submit']",
|
||||
".login-btn",
|
||||
"[data-testid='login-button']",
|
||||
],
|
||||
"captcha_input": [
|
||||
"input[name='captcha']",
|
||||
"input[placeholder*='验证码']",
|
||||
"#captcha",
|
||||
],
|
||||
"captcha_image": [
|
||||
".captcha-image",
|
||||
"img[alt*='验证码']",
|
||||
"#captcha-image",
|
||||
],
|
||||
}
|
||||
|
||||
# 登录成功检测
|
||||
LOGIN_SUCCESS_SELECTORS = [
|
||||
".user-info",
|
||||
".welcome",
|
||||
"[data-testid='user-avatar']",
|
||||
"a[href*='logout']",
|
||||
".dashboard",
|
||||
]
|
||||
|
||||
|
||||
async def login_if_needed(
|
||||
page: Any,
|
||||
username: str,
|
||||
password: str,
|
||||
wait_sec: int = 120,
|
||||
) -> bool:
|
||||
"""检查登录状态,如需要则执行登录。
|
||||
|
||||
Args:
|
||||
page: Playwright页面对象
|
||||
username: 用户名
|
||||
password: 密码
|
||||
wait_sec: 等待人工验证的超时时间
|
||||
|
||||
Returns:
|
||||
bool: 登录是否成功
|
||||
"""
|
||||
# 检查是否已登录
|
||||
if await _is_logged_in(page):
|
||||
logger.info("already_logged_in url=%s", page.url)
|
||||
return True
|
||||
|
||||
# 导航到登录页面
|
||||
if "/login" not in page.url:
|
||||
await page.goto("https://sandbox.jc2009.com/login", wait_until="domcontentloaded")
|
||||
await interruptible_sleep(2.0)
|
||||
|
||||
# 尝试自动登录
|
||||
login_success = await _try_auto_login(page, username, password)
|
||||
if login_success:
|
||||
return True
|
||||
|
||||
# 等待人工登录
|
||||
return await _wait_for_human_login(page, wait_sec)
|
||||
|
||||
|
||||
async def _try_auto_login(page: Any, username: str, password: str) -> bool:
|
||||
"""尝试自动登录。"""
|
||||
try:
|
||||
# 查找用户名输入框
|
||||
username_input = await _find_element(page, LOGIN_SELECTORS["username_input"])
|
||||
if not username_input:
|
||||
logger.warning("username_input_not_found")
|
||||
return False
|
||||
|
||||
# 查找密码输入框
|
||||
password_input = await _find_element(page, LOGIN_SELECTORS["password_input"])
|
||||
if not password_input:
|
||||
logger.warning("password_input_not_found")
|
||||
return False
|
||||
|
||||
# 输入用户名(模拟人类输入)
|
||||
await username_input.click()
|
||||
await interruptible_sleep(0.5)
|
||||
await username_input.fill("")
|
||||
await page.keyboard.type(username, delay=100)
|
||||
await interruptible_sleep(0.5)
|
||||
|
||||
# 输入密码
|
||||
await password_input.click()
|
||||
await interruptible_sleep(0.5)
|
||||
await password_input.fill("")
|
||||
await page.keyboard.type(password, delay=100)
|
||||
await interruptible_sleep(0.5)
|
||||
|
||||
# 查找并点击登录按钮
|
||||
login_button = await _find_element(page, LOGIN_SELECTORS["login_button"])
|
||||
if not login_button:
|
||||
logger.warning("login_button_not_found")
|
||||
return False
|
||||
|
||||
await login_button.click()
|
||||
await interruptible_sleep(3.0)
|
||||
|
||||
# 检查是否登录成功
|
||||
return await _is_logged_in(page)
|
||||
|
||||
except Exception as e:
|
||||
logger.error("auto_login_failed error=%s", str(e))
|
||||
return False
|
||||
|
||||
|
||||
async def _wait_for_human_login(page: Any, wait_sec: int) -> bool:
|
||||
"""等待人工完成登录。"""
|
||||
print(f"请在浏览器中完成登录,最多等待 {wait_sec} 秒...")
|
||||
deadline = time.monotonic() + wait_sec
|
||||
|
||||
while time.monotonic() < deadline:
|
||||
if await _is_logged_in(page):
|
||||
print("检测到登录成功")
|
||||
return True
|
||||
|
||||
# 检查是否有验证码需要处理
|
||||
captcha_present = await _check_captcha(page)
|
||||
if captcha_present:
|
||||
print("检测到验证码,请人工完成验证...")
|
||||
|
||||
await interruptible_sleep(2.0)
|
||||
|
||||
print("登录超时")
|
||||
return False
|
||||
|
||||
|
||||
async def _is_logged_in(page: Any) -> bool:
|
||||
"""检查是否已登录。"""
|
||||
try:
|
||||
# 检查URL是否包含登录成功标识
|
||||
if "/login" not in page.url and "/index" in page.url:
|
||||
return True
|
||||
|
||||
# 检查页面元素
|
||||
for selector in LOGIN_SUCCESS_SELECTORS:
|
||||
try:
|
||||
element = page.locator(selector)
|
||||
if await element.count() > 0 and await element.is_visible():
|
||||
return True
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
return False
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
|
||||
async def _check_captcha(page: Any) -> bool:
|
||||
"""检查是否有验证码。"""
|
||||
try:
|
||||
for selector in LOGIN_SELECTORS["captcha_input"]:
|
||||
element = page.locator(selector)
|
||||
if await element.count() > 0 and await element.is_visible():
|
||||
return True
|
||||
|
||||
for selector in LOGIN_SELECTORS["captcha_image"]:
|
||||
element = page.locator(selector)
|
||||
if await element.count() > 0 and await element.is_visible():
|
||||
return True
|
||||
|
||||
return False
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
|
||||
async def _find_element(page: Any, selectors: list) -> Optional[Any]:
|
||||
"""根据选择器列表查找元素。"""
|
||||
for selector in selectors:
|
||||
try:
|
||||
element = page.locator(selector)
|
||||
if await element.count() > 0 and await element.is_visible():
|
||||
return element
|
||||
except Exception:
|
||||
continue
|
||||
return None
|
||||
Reference in New Issue
Block a user