优化登录重试与配置清理
This commit is contained in:
+47
-31
@@ -14,7 +14,6 @@ from .crypto import encrypt_password, encrypt_nickname_or_phone
|
||||
from .email_verifier import EmailVerifier
|
||||
from .proxy import ProxyManager, get_proxy_manager
|
||||
|
||||
from core.geetest import run_solver
|
||||
from core.geetest.v3_slide.solver import (
|
||||
_generate_seed, get_w1, get_w2,
|
||||
)
|
||||
@@ -53,7 +52,6 @@ class DouyuLogin:
|
||||
LOGIN_API = "https://passport.douyu.com/wgapi/member/passport/login"
|
||||
SEND_EMAIL_API = "https://passport.douyu.com/wgapi/member/passport/remotelogin/sendemail"
|
||||
VERIFY_API = "https://passport.douyu.com/wgapi/member/passport/remotelogin/verify"
|
||||
LOGIN_CALLBACK_API = "https://www.douyu.com/api/passport/login"
|
||||
WEBLOGIN_API = "https://msg.douyu.com/webLogin"
|
||||
LOGIN_REFERER = (
|
||||
"https://passport.douyu.com/index/login?"
|
||||
@@ -72,10 +70,9 @@ class DouyuLogin:
|
||||
proxy: Optional[str | Mapping[str, str]] = None,
|
||||
proxy_api_url: Optional[str] = None,
|
||||
timeout: tuple[float, float] = REQUEST_TIMEOUT,
|
||||
max_geetest_retries: int = 5,
|
||||
max_proxy_retries: int = 0,
|
||||
max_login_retries: int = 3,
|
||||
max_total_time: float = 300,
|
||||
max_login_retries: int = 0,
|
||||
max_total_time: float = 0,
|
||||
whitelist_uid: str = "",
|
||||
whitelist_ukey: str = "",
|
||||
proxy_manager: Optional[ProxyManager] = None,
|
||||
@@ -84,10 +81,9 @@ class DouyuLogin:
|
||||
self.account = account
|
||||
self.proxy = proxy
|
||||
self.timeout = timeout
|
||||
self.max_geetest_retries = max_geetest_retries
|
||||
self.max_proxy_retries = max_proxy_retries # 0=无限切换直到成功
|
||||
self.max_login_retries = max_login_retries # 登录整体重试次数(换代理从头重跑)
|
||||
self.max_total_time = max_total_time # 单账号登录总时长上限(秒),超时则放弃
|
||||
self.max_login_retries = max_login_retries # 0=无限整体重试直到成功
|
||||
self.max_total_time = max_total_time # 0=不限制单账号登录总时长
|
||||
self.stop_event = stop_event
|
||||
self.session = requests.Session()
|
||||
|
||||
@@ -308,34 +304,39 @@ class DouyuLogin:
|
||||
"""
|
||||
完整登录流程(带整体重试)。
|
||||
|
||||
任何步骤失败时,换新代理从头重跑,最多重试 max_login_retries 次。
|
||||
整体超时 max_total_time 秒后放弃。
|
||||
任何步骤失败时,换新代理从头重跑。
|
||||
max_login_retries=0 表示无限重试,max_total_time=0 表示不限制总时长。
|
||||
|
||||
Returns:
|
||||
LoginResult: 登录结果,包含cookie
|
||||
"""
|
||||
logger.info(f"开始登录账号: {self.account.username}")
|
||||
start_time = time.monotonic()
|
||||
deadline = start_time + self.max_total_time
|
||||
deadline = start_time + self.max_total_time if self.max_total_time > 0 else 0
|
||||
|
||||
for attempt in range(1, self.max_login_retries + 1):
|
||||
attempt = 0
|
||||
while True:
|
||||
attempt += 1
|
||||
if self._is_stopped():
|
||||
logger.warning("登录任务已停止")
|
||||
return LoginResult(success=False, message="任务已停止")
|
||||
elapsed = time.monotonic() - start_time
|
||||
if elapsed > self.max_total_time:
|
||||
if self.max_total_time > 0 and elapsed > self.max_total_time:
|
||||
logger.warning(f"登录总耗时 {elapsed:.0f}s 超过上限 {self.max_total_time}s,放弃")
|
||||
return LoginResult(success=False, message=f"登录超时({elapsed:.0f}s > {self.max_total_time}s)")
|
||||
|
||||
if attempt > 1:
|
||||
logger.info(f"登录整体重试 {attempt}/{self.max_login_retries},换代理重新开始")
|
||||
if self.max_login_retries > 0:
|
||||
logger.info(f"登录整体重试 {attempt}/{self.max_login_retries},换代理重新开始")
|
||||
else:
|
||||
logger.info(f"登录整体重试 {attempt} (无限重试),换代理重新开始")
|
||||
# 重试前:换新代理 + 重置 session(清 cookies)
|
||||
self._prepare_retry()
|
||||
|
||||
try:
|
||||
# 1️⃣ 第一次登录(获取极验参数)
|
||||
logger.info("步骤1: 第一次登录,获取极验参数...")
|
||||
gt, challenge, code_token, initial_cookies = self._first_login()
|
||||
gt, challenge, code_token, _ = self._first_login()
|
||||
|
||||
# 2️⃣ 极验 fullpage 验证
|
||||
logger.info("步骤2: 极验 fullpage 验证...")
|
||||
@@ -376,8 +377,14 @@ class DouyuLogin:
|
||||
return LoginResult(success=False, message=str(e))
|
||||
except Exception as e:
|
||||
elapsed = time.monotonic() - start_time
|
||||
logger.error(f"登录失败(尝试 {attempt}/{self.max_login_retries},已耗时 {elapsed:.0f}s): {e}")
|
||||
if attempt < self.max_login_retries and elapsed < self.max_total_time:
|
||||
if self.max_login_retries > 0:
|
||||
logger.error(f"登录失败(尝试 {attempt}/{self.max_login_retries},已耗时 {elapsed:.0f}s): {e}")
|
||||
else:
|
||||
logger.error(f"登录失败(尝试 {attempt},已耗时 {elapsed:.0f}s): {e}")
|
||||
|
||||
has_retry = self.max_login_retries <= 0 or attempt < self.max_login_retries
|
||||
has_time = self.max_total_time <= 0 or elapsed < self.max_total_time
|
||||
if has_retry and has_time:
|
||||
# 还有重试机会且未超时:标记当前代理坏,下一轮自动换新代理
|
||||
if self.proxy_manager and self._current_proxy_url:
|
||||
self.proxy_manager.mark_bad(self._current_proxy_url)
|
||||
@@ -386,8 +393,6 @@ class DouyuLogin:
|
||||
# 所有重试耗尽或超时
|
||||
return LoginResult(success=False, message=str(e))
|
||||
|
||||
return LoginResult(success=False, message=f"登录失败,已重试 {self.max_login_retries} 次")
|
||||
|
||||
def _prepare_retry(self) -> None:
|
||||
"""重试前准备:换新代理、重置 session cookies。"""
|
||||
# 重置 session(清掉旧 cookies,避免残留状态干扰)
|
||||
@@ -471,24 +476,35 @@ class DouyuLogin:
|
||||
"""
|
||||
logger.info("开始极验 fullpage 验证...")
|
||||
|
||||
# max_proxy_retries=0 表示无限重试直到成功
|
||||
max_attempts = self.max_proxy_retries if self.max_proxy_retries > 0 else 999999
|
||||
# max_proxy_retries=0 表示不限代理切换次数。
|
||||
max_proxy_switches = self.max_proxy_retries if self.max_proxy_retries > 0 else 999999
|
||||
proxy_switches = 0
|
||||
|
||||
# 连续临时失败计数(同一代理下),超过阈值才换代理
|
||||
_soft_fail_streak = 0
|
||||
_SOFT_FAIL_THRESHOLD = 2 # 同一代理连续临时失败2次才换
|
||||
|
||||
for attempt in range(max_attempts):
|
||||
def refresh_proxy(mark_bad: bool) -> None:
|
||||
"""按代理切换上限刷新代理。"""
|
||||
nonlocal proxy_switches
|
||||
if not self.proxy_manager:
|
||||
return
|
||||
if proxy_switches >= max_proxy_switches:
|
||||
raise ValueError(f"极验验证代理切换次数已达上限 {self.max_proxy_retries}")
|
||||
new_proxy = self._refresh_proxy(mark_bad=mark_bad)
|
||||
if new_proxy:
|
||||
proxy_switches += 1
|
||||
|
||||
attempt = 0
|
||||
while True:
|
||||
attempt += 1
|
||||
self._ensure_not_stopped()
|
||||
# 超时兜底:极验验证不应超过登录整体时间上限
|
||||
if deadline and time.monotonic() > deadline:
|
||||
raise ValueError(f"极验验证超时(登录整体时间耗尽)")
|
||||
|
||||
try:
|
||||
if self.max_proxy_retries > 0:
|
||||
logger.info(f"极验验证尝试 {attempt + 1}/{max_attempts}")
|
||||
else:
|
||||
logger.info(f"极验验证尝试 {attempt + 1} (无限重试)")
|
||||
logger.info(f"极验验证尝试 {attempt} (无限重试)")
|
||||
|
||||
# 按斗鱼登录页 HAR:fullpage 智能检测流程,不进入图片滑块。
|
||||
str_16 = _generate_seed()
|
||||
@@ -525,7 +541,7 @@ class DouyuLogin:
|
||||
_soft_fail_streak += 1
|
||||
if _soft_fail_streak >= _SOFT_FAIL_THRESHOLD:
|
||||
logger.warning(f"极验验证失败: {message},同一代理连续 {_soft_fail_streak} 次,换代理")
|
||||
self._refresh_proxy(mark_bad=False)
|
||||
refresh_proxy(mark_bad=False)
|
||||
_soft_fail_streak = 0
|
||||
else:
|
||||
logger.warning(f"极验验证失败: {message},原代理重试 ({_soft_fail_streak}/{_SOFT_FAIL_THRESHOLD})")
|
||||
@@ -540,27 +556,27 @@ class DouyuLogin:
|
||||
|
||||
except Exception as e:
|
||||
err_str = str(e)
|
||||
if "代理切换次数已达上限" in err_str:
|
||||
raise
|
||||
is_proxy_dead = self._is_proxy_connection_error(err_str)
|
||||
|
||||
if is_proxy_dead:
|
||||
# 代理确实不可用:立即换,标记坏
|
||||
logger.warning(f"极验验证代理连接失败: {self._truncate_error(err_str)},换代理")
|
||||
self._refresh_proxy(mark_bad=True)
|
||||
refresh_proxy(mark_bad=True)
|
||||
_soft_fail_streak = 0
|
||||
else:
|
||||
# 临时异常(KeyError、网络不给力等):先原代理重试
|
||||
_soft_fail_streak += 1
|
||||
if _soft_fail_streak >= _SOFT_FAIL_THRESHOLD:
|
||||
logger.warning(f"极验验证临时异常: {self._truncate_error(err_str)},连续 {_soft_fail_streak} 次,换代理")
|
||||
self._refresh_proxy(mark_bad=False)
|
||||
refresh_proxy(mark_bad=False)
|
||||
_soft_fail_streak = 0
|
||||
else:
|
||||
logger.warning(f"极验验证临时异常: {self._truncate_error(err_str)},原代理重试 ({_soft_fail_streak}/{_SOFT_FAIL_THRESHOLD})")
|
||||
self._sleep_interruptible(2)
|
||||
continue
|
||||
|
||||
if self.max_proxy_retries > 0:
|
||||
raise ValueError(f"极验验证失败,已重试 {max_attempts} 次")
|
||||
raise ValueError("极验验证失败(无限重试模式仍未能通过)")
|
||||
|
||||
def _second_login(self, gt: str, challenge: str, validate: str,
|
||||
|
||||
Reference in New Issue
Block a user