增加了代理
This commit is contained in:
+110
-35
@@ -12,6 +12,7 @@ from loguru import logger
|
||||
from .config import Account
|
||||
from .crypto import encrypt_password, encrypt_nickname_or_phone
|
||||
from .email_verifier import EmailVerifier
|
||||
from .proxy import ProxyManager, get_proxy_manager
|
||||
|
||||
# 导入geetest滑块模块
|
||||
import sys
|
||||
@@ -49,12 +50,19 @@ class DouyuLogin:
|
||||
self,
|
||||
account: Account,
|
||||
proxy: Optional[str | Mapping[str, str]] = None,
|
||||
proxy_api_url: Optional[str] = None,
|
||||
timeout: tuple[float, float] = REQUEST_TIMEOUT,
|
||||
max_geetest_retries: int = 5,
|
||||
):
|
||||
self.account = account
|
||||
self.proxy = proxy
|
||||
self.timeout = timeout
|
||||
self.max_geetest_retries = max_geetest_retries
|
||||
self.session = requests.Session()
|
||||
|
||||
# 初始化代理管理器
|
||||
self.proxy_manager = get_proxy_manager(proxy_api_url)
|
||||
|
||||
self._setup_session()
|
||||
|
||||
def _setup_session(self) -> None:
|
||||
@@ -71,7 +79,19 @@ class DouyuLogin:
|
||||
'X-Requested-With': 'XMLHttpRequest',
|
||||
})
|
||||
|
||||
if self.proxy:
|
||||
# 设置代理
|
||||
self._apply_proxy()
|
||||
|
||||
def _apply_proxy(self, proxy: str = None) -> None:
|
||||
"""应用代理到Session"""
|
||||
if proxy:
|
||||
# 使用指定的代理
|
||||
self.session.proxies = {
|
||||
'http': proxy,
|
||||
'https': proxy,
|
||||
}
|
||||
elif self.proxy:
|
||||
# 使用配置的代理
|
||||
if isinstance(self.proxy, str):
|
||||
self.session.proxies = {
|
||||
'http': self.proxy,
|
||||
@@ -83,6 +103,22 @@ class DouyuLogin:
|
||||
for scheme, url in self.proxy.items()
|
||||
if url
|
||||
}
|
||||
else:
|
||||
# 从代理管理器获取代理
|
||||
new_proxy = self.proxy_manager.get_proxy()
|
||||
if new_proxy:
|
||||
self.session.proxies = {
|
||||
'http': new_proxy,
|
||||
'https': new_proxy,
|
||||
}
|
||||
|
||||
def _refresh_proxy(self) -> Optional[str]:
|
||||
"""刷新代理IP"""
|
||||
new_proxy = self.proxy_manager.get_proxy()
|
||||
if new_proxy:
|
||||
self._apply_proxy(new_proxy)
|
||||
logger.info(f"已切换代理: {new_proxy}")
|
||||
return new_proxy
|
||||
|
||||
def _safe_url(self, url: str) -> str:
|
||||
"""隐藏查询参数,避免日志泄露登录回调 code。"""
|
||||
@@ -226,62 +262,101 @@ class DouyuLogin:
|
||||
|
||||
def _solve_geetest(self, gt: str, challenge: str) -> Tuple[str, str]:
|
||||
"""
|
||||
解决极验滑块验证
|
||||
解决极验滑块验证(带重试机制)
|
||||
|
||||
Args:
|
||||
gt: 极验gt参数
|
||||
challenge: 极验challenge参数
|
||||
challenge: 极验challenge参数(第一次登录返回的)
|
||||
|
||||
Returns:
|
||||
(validate, seccode)
|
||||
"""
|
||||
logger.info("开始极验滑块验证...")
|
||||
|
||||
# 使用geetest-v3-silde-crack的完整流程
|
||||
str_16 = _generate_seed()
|
||||
for attempt in range(self.max_geetest_retries):
|
||||
try:
|
||||
logger.info(f"极验验证尝试 {attempt + 1}/{self.max_geetest_retries}")
|
||||
|
||||
# 获取JS地址
|
||||
get_js_address(gt)
|
||||
# 使用geetest-v3-silde-crack的完整流程
|
||||
str_16 = _generate_seed()
|
||||
|
||||
# 获取第一个w值
|
||||
w1 = get_w1(gt, challenge, str_16)
|
||||
# 获取JS地址
|
||||
get_js_address(gt)
|
||||
|
||||
# 获取c和s
|
||||
c, s = get_c_s(gt, challenge, w1)
|
||||
# 获取第一个w值
|
||||
w1 = get_w1(gt, challenge, str_16)
|
||||
|
||||
# 获取第二个w值
|
||||
w2 = get_w2(gt, challenge, c, s, str_16)
|
||||
# 获取c和s
|
||||
c, s = get_c_s(gt, challenge, w1)
|
||||
|
||||
# 请求滑块
|
||||
req_slide(gt, challenge, w2)
|
||||
# 获取第二个w值
|
||||
w2 = get_w2(gt, challenge, c, s, str_16)
|
||||
|
||||
# 获取图片
|
||||
bg, fullbg, c, s, slice, challenge_new = get_picture(gt, challenge)
|
||||
# 请求滑块
|
||||
req_slide(gt, challenge, w2)
|
||||
|
||||
# 识别缺口位置
|
||||
hkjl = download_picture(bg, fullbg, slice)
|
||||
# 获取图片
|
||||
result = get_picture(gt, challenge)
|
||||
|
||||
# 获取第三个w值
|
||||
w3 = get_w3(str_16, challenge_new, hkjl, c, s, gt)
|
||||
# 检查返回的数据结构
|
||||
if len(result) == 6:
|
||||
bg, fullbg, c, s, slice, challenge_new = result
|
||||
else:
|
||||
# 如果返回的数据结构不对,可能是点选验证码,需要重试
|
||||
logger.warning(f"返回数据结构异常,可能是点选验证码,重试中...")
|
||||
# 刷新代理
|
||||
self._refresh_proxy()
|
||||
time.sleep(1)
|
||||
continue
|
||||
|
||||
# 最后验证
|
||||
result = req_end(gt, challenge_new, w3)
|
||||
# 检查是否是滑块验证码(bg和slice不为空)
|
||||
if not bg or not slice:
|
||||
logger.warning(f"未获取到滑块图片,可能是点选验证码,重试中...")
|
||||
# 刷新代理
|
||||
self._refresh_proxy()
|
||||
time.sleep(1)
|
||||
continue
|
||||
|
||||
# 从req_end的响应中提取validate
|
||||
# req_end返回的是验证结果,成功时包含validate
|
||||
if isinstance(result, dict):
|
||||
validate = result.get('validate', '')
|
||||
else:
|
||||
# 如果返回的是字符串,可能是直接的validate值
|
||||
validate = str(result)
|
||||
# 识别缺口位置
|
||||
hkjl = download_picture(bg, fullbg, slice)
|
||||
|
||||
if not validate:
|
||||
raise ValueError("极验验证失败,未获取到validate")
|
||||
# 获取第三个w值
|
||||
w3 = get_w3(str_16, challenge_new, hkjl, c, s, gt)
|
||||
|
||||
seccode = f"{validate}|jordan"
|
||||
# 最后验证
|
||||
result = req_end(gt, challenge_new, w3)
|
||||
|
||||
logger.success(f"极验滑块验证成功! validate={validate[:20]}...")
|
||||
return validate, seccode
|
||||
# 从req_end的响应中提取validate
|
||||
if isinstance(result, dict):
|
||||
validate = result.get('validate', '')
|
||||
success = result.get('success', 0)
|
||||
message = result.get('message', '')
|
||||
|
||||
if success == 1 and validate:
|
||||
seccode = f"{validate}|jordan"
|
||||
logger.success(f"极验滑块验证成功! validate={validate[:20]}...")
|
||||
return validate, seccode
|
||||
else:
|
||||
logger.warning(f"极验验证失败: {message},重试中...")
|
||||
# 刷新代理
|
||||
self._refresh_proxy()
|
||||
time.sleep(1)
|
||||
continue
|
||||
else:
|
||||
validate = str(result)
|
||||
if validate:
|
||||
seccode = f"{validate}|jordan"
|
||||
logger.success(f"极验滑块验证成功! validate={validate[:20]}...")
|
||||
return validate, seccode
|
||||
|
||||
except Exception as e:
|
||||
logger.warning(f"极验验证异常: {e},重试中...")
|
||||
# 刷新代理
|
||||
self._refresh_proxy()
|
||||
time.sleep(1)
|
||||
continue
|
||||
|
||||
raise ValueError(f"极验验证失败,已重试 {self.max_geetest_retries} 次")
|
||||
|
||||
def _second_login(self, gt: str, challenge: str, validate: str,
|
||||
seccode: str, code_token: str) -> str:
|
||||
|
||||
Reference in New Issue
Block a user