This commit is contained in:
sjk
2026-01-07 12:18:55 +08:00
parent a7305908de
commit cb267e8d5e
18 changed files with 411 additions and 133 deletions

View File

@@ -497,6 +497,83 @@ class XHSLoginService:
except Exception as e:
print(f"关闭浏览器异常: {str(e)}", file=sys.stderr)
async def extract_verification_qrcode(self) -> Optional[str]:
"""
提取验证页面的二维码图片
Returns:
二维码图片的base64数据如果提取失败则返回none
"""
try:
if not self.page:
return None
print("正在提取验证二维码...", file=sys.stderr)
# 尝试查找二维码图片元素
qrcode_selectors = [
'.qrcode-img', # 根据您提供的HTML
'img.qrcode-img',
'.qrcode-container img',
'img[src*="data:image"]', # base64图片
'img[src*="qrcode"]',
'img[alt*="二维码"]',
'img[alt*="qrcode"]',
]
for selector in qrcode_selectors:
try:
qrcode_img = await self.page.wait_for_selector(selector, timeout=3000)
if qrcode_img:
print(f"✅ 找到二维码图片: {selector}", file=sys.stderr)
# 获取图片src属性
src = await qrcode_img.get_attribute('src')
if src:
# 如果是base64格式直接返回
if src.startswith('data:image'):
print("✅ 二维码已是base64格式直接返回", file=sys.stderr)
return src
# 如果是URL尝试下载并转换为base64
print(f"二维码是URL格式: {src[:100]}...", file=sys.stderr)
try:
async with aiohttp.ClientSession() as session:
async with session.get(src, timeout=aiohttp.ClientTimeout(total=10)) as response:
if response.status == 200:
img_data = await response.read()
import base64
img_base64 = base64.b64encode(img_data).decode('utf-8')
# 根据内容类型确定格式
content_type = response.headers.get('Content-Type', 'image/png')
base64_str = f"data:{content_type};base64,{img_base64}"
print("✅ 成功下载并转换为base64", file=sys.stderr)
return base64_str
except Exception as e:
print(f"⚠️ 下载二维码图片失败: {str(e)}", file=sys.stderr)
# 如果src方法失败尝试截图
print("尝试截取二维码区域...", file=sys.stderr)
screenshot_bytes = await qrcode_img.screenshot()
if screenshot_bytes:
import base64
img_base64 = base64.b64encode(screenshot_bytes).decode('utf-8')
base64_str = f"data:image/png;base64,{img_base64}"
print("✅ 成功截取二维码并转换为base64", file=sys.stderr)
return base64_str
break
except Exception as e:
print(f"尝试选择器 {selector} 失败: {str(e)}", file=sys.stderr)
continue
print("⚠️ 未找到二维码图片", file=sys.stderr)
return None
except Exception as e:
print(f"⚠️ 提取二维码失败: {str(e)}", file=sys.stderr)
return None
async def send_verification_code(self, phone: str, country_code: str = "+86", login_page: str = "creator") -> Dict[str, Any]:
"""
发送验证码
@@ -819,16 +896,32 @@ class XHSLoginService:
await send_code_btn.click()
print("✅ 已点击发送验证码", file=sys.stderr)
# # 优化:简化二次协议处理
# await asyncio.sleep(0.3) # 等待协议弹窗可能出现
# try:
# agreement_btn = await self.page.query_selector('text="同意并继续"')
# if agreement_btn:
# await agreement_btn.click()
# print(f"✅ 再次点击协议按钮", file=sys.stderr)
# await asyncio.sleep(0.2)
# except Exception:
# pass # 无二次协议弹窗
# 等待页面响应,检测是否出现验证二维码
await asyncio.sleep(1.5)
# 检查当前页面URL是否包含captcha验证页面
current_url = self.page.url
if 'captcha' in current_url or 'verify' in current_url:
print(f"⚠️ 检测到验证页面: {current_url}", file=sys.stderr)
# 尝试提取二维码图片
qrcode_data = await self.extract_verification_qrcode()
if qrcode_data:
print("✅ 成功提取验证二维码", file=sys.stderr)
return {
"success": False,
"need_captcha": True,
"captcha_type": "qrcode",
"qrcode_image": qrcode_data,
"message": "需要扫码验证请使用小红书APP扫描二维码"
}
else:
return {
"success": False,
"need_captcha": True,
"captcha_type": "unknown",
"message": "出现验证码验证,请稍后重试"
}
# 直接返回成功,不再检测滑块
print("\n✅ 验证码发送流程完成,请查看手机短信", file=sys.stderr)