增加了代理平台和日志
This commit is contained in:
+58
-8
@@ -23,6 +23,9 @@ from core.geetest.common.network import (
|
||||
req_fullpage_validate,
|
||||
)
|
||||
|
||||
# ── 全局极验并发限制:同一时刻最多2个线程做极验验证 ──
|
||||
_geetest_semaphore = threading.Semaphore(2)
|
||||
|
||||
|
||||
class AccountLike(Protocol):
|
||||
"""DouyuLogin 所需的最小账号接口,ORM 对象或 SimpleNamespace 均可满足。"""
|
||||
@@ -75,6 +78,8 @@ class DouyuLogin:
|
||||
max_total_time: float = 0,
|
||||
whitelist_uid: str = "",
|
||||
whitelist_ukey: str = "",
|
||||
whitelist_platform: str = "xiequ",
|
||||
whitelist_credentials: dict = None,
|
||||
proxy_manager: Optional[ProxyManager] = None,
|
||||
stop_event: Optional[threading.Event] = None,
|
||||
):
|
||||
@@ -95,6 +100,8 @@ class DouyuLogin:
|
||||
proxy_api_url,
|
||||
whitelist_uid=whitelist_uid,
|
||||
whitelist_ukey=whitelist_ukey,
|
||||
whitelist_platform=whitelist_platform,
|
||||
whitelist_credentials=whitelist_credentials,
|
||||
)
|
||||
else:
|
||||
self.proxy_manager = None
|
||||
@@ -220,6 +227,22 @@ class DouyuLogin:
|
||||
return True
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def _is_geetest_network_error(err_str: str) -> bool:
|
||||
"""
|
||||
判断异常是否为极验接口的网络类错误(代理到极验不可达/被限流)。
|
||||
这类错误说明当前代理已被极验识别或限流,应立即换代理而非软重试。
|
||||
"""
|
||||
err_lower = err_str.lower()
|
||||
network_error_keywords = [
|
||||
'网络不给力', # 极验 get.php 返回的网络错误
|
||||
'connection aborted', # 远程断开连接
|
||||
'remotedisconnected', # requests RemoteDisconnected
|
||||
'read timed out', # 读取超时(极验接口)
|
||||
'api.geetest.com', # 极验接口相关错误
|
||||
]
|
||||
return any(kw in err_lower for kw in network_error_keywords)
|
||||
|
||||
@staticmethod
|
||||
def _truncate_error(err_str: str, max_len: int = 80) -> str:
|
||||
"""截断错误信息,避免日志刷屏。"""
|
||||
@@ -461,28 +484,41 @@ class DouyuLogin:
|
||||
"""
|
||||
解决极验 fullpage 验证(带重试机制)
|
||||
|
||||
区分两类错误:
|
||||
- 临时波动(slide、网络不给力、KeyError):先用原代理重试,连续失败才换
|
||||
区分三类错误:
|
||||
- 代理死亡(ProxyError、连接超时):立即换代理
|
||||
- 网络类错误(网络不给力、Connection aborted):立即换代理
|
||||
- 极验逻辑失败(slide等):原代理重试,连续2次才换
|
||||
|
||||
Args:
|
||||
gt: 极验gt参数
|
||||
challenge: 极验challenge参数(第一次登录返回的)
|
||||
deadline: 登录整体超时截止时间(monotonic),0=不限
|
||||
challenge: 极验challenge参数(第一次登录返回的)
|
||||
|
||||
Returns:
|
||||
(validate, seccode)
|
||||
"""
|
||||
logger.info("开始极验 fullpage 验证...")
|
||||
|
||||
# max_proxy_retries=0 表示不限代理切换次数。
|
||||
# ── 全局并发限制:同一时刻最多2个线程做极验,避免被极验限流 ──
|
||||
acquired = _geetest_semaphore.acquire(timeout=120)
|
||||
if not acquired:
|
||||
raise ValueError("极验验证等待超时(并发排队120秒未获得信号量)")
|
||||
try:
|
||||
return self._solve_geetest_inner(gt, challenge, deadline)
|
||||
finally:
|
||||
_geetest_semaphore.release()
|
||||
|
||||
def _solve_geetest_inner(self, gt: str, challenge: str, deadline: float = 0) -> Tuple[str, str]:
|
||||
"""极验验证内部实现(已获取并发信号量)。"""
|
||||
max_proxy_switches = self.max_proxy_retries if self.max_proxy_retries > 0 else 999999
|
||||
proxy_switches = 0
|
||||
|
||||
# ── 极验验证最大尝试次数(超出后回到 login 整体重试)──
|
||||
_MAX_GEETEST_ATTEMPTS = 15
|
||||
|
||||
# 连续临时失败计数(同一代理下),超过阈值才换代理
|
||||
_soft_fail_streak = 0
|
||||
_SOFT_FAIL_THRESHOLD = 2 # 同一代理连续临时失败2次才换
|
||||
_SOFT_FAIL_THRESHOLD = 2
|
||||
|
||||
def refresh_proxy(mark_bad: bool) -> None:
|
||||
"""按代理切换上限刷新代理。"""
|
||||
@@ -499,12 +535,20 @@ class DouyuLogin:
|
||||
while True:
|
||||
attempt += 1
|
||||
self._ensure_not_stopped()
|
||||
|
||||
# ── 极验验证尝试上限 ──
|
||||
if attempt > _MAX_GEETEST_ATTEMPTS:
|
||||
raise ValueError(
|
||||
f"极验验证已尝试 {attempt} 次,超过上限 {_MAX_GEETEST_ATTEMPTS},"
|
||||
f"回到 login 整体重试换新代理"
|
||||
)
|
||||
|
||||
# 超时兜底:极验验证不应超过登录整体时间上限
|
||||
if deadline and time.monotonic() > deadline:
|
||||
raise ValueError(f"极验验证超时(登录整体时间耗尽)")
|
||||
|
||||
try:
|
||||
logger.info(f"极验验证尝试 {attempt} (无限重试)")
|
||||
logger.info(f"极验验证尝试 {attempt}/{_MAX_GEETEST_ATTEMPTS}")
|
||||
|
||||
# 按斗鱼登录页 HAR:fullpage 智能检测流程,不进入图片滑块。
|
||||
str_16 = _generate_seed()
|
||||
@@ -556,17 +600,23 @@ class DouyuLogin:
|
||||
|
||||
except Exception as e:
|
||||
err_str = str(e)
|
||||
if "代理切换次数已达上限" in err_str:
|
||||
if "代理切换次数已达上限" in err_str or "超过上限" in err_str:
|
||||
raise
|
||||
is_proxy_dead = self._is_proxy_connection_error(err_str)
|
||||
is_network_error = self._is_geetest_network_error(err_str)
|
||||
|
||||
if is_proxy_dead:
|
||||
# 代理确实不可用:立即换,标记坏
|
||||
logger.warning(f"极验验证代理连接失败: {self._truncate_error(err_str)},换代理")
|
||||
refresh_proxy(mark_bad=True)
|
||||
_soft_fail_streak = 0
|
||||
elif is_network_error:
|
||||
# 网络类错误(极验限流/网络不给力/Connection aborted):立即换代理
|
||||
logger.warning(f"极验网络错误: {self._truncate_error(err_str)},立即换代理")
|
||||
refresh_proxy(mark_bad=False)
|
||||
_soft_fail_streak = 0
|
||||
else:
|
||||
# 临时异常(KeyError、网络不给力等):先原代理重试
|
||||
# 其他临时异常(KeyError 等):先原代理重试
|
||||
_soft_fail_streak += 1
|
||||
if _soft_fail_streak >= _SOFT_FAIL_THRESHOLD:
|
||||
logger.warning(f"极验验证临时异常: {self._truncate_error(err_str)},连续 {_soft_fail_streak} 次,换代理")
|
||||
|
||||
Reference in New Issue
Block a user