| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152 |
- """易盾滑块验证码:云码识别 + 模拟拖拽。"""
- import base64
- import math
- import random
- import time
- import requests
- from commons.Logger import logger
- CAPTCHA_TOKEN = "zPzmt1mG1ouCU6GTzsZN2Lmm8pdZypapPcLJTBRETco"
- CAPTCHA_API_URL = "http://api.jfbym.com/api/YmServer/customApi"
- SLIDER_OFFSET_FIX = 10
- def call_captcha_api(image_bytes):
- """调用云码平台识别滑块距离,失败返回 None。"""
- try:
- b64 = base64.b64encode(image_bytes).decode()
- resp = requests.post(
- CAPTCHA_API_URL,
- json={"token": CAPTCHA_TOKEN, "type": "22222", "image": b64},
- headers={"Content-Type": "application/json"},
- timeout=15,
- ).json()
- logger.info("验证码 API 返回: %s", resp)
- if not isinstance(resp, dict):
- return None
- data = resp.get("data")
- if isinstance(data, dict):
- dist = data.get("data")
- else:
- dist = data
- if dist is None:
- logger.error("验证码 API 未返回距离字段: %s", resp)
- return None
- try:
- d = float(dist)
- except (TypeError, ValueError):
- logger.error("验证码距离无法解析为数字: %r", dist)
- return None
- if not math.isfinite(d):
- logger.error("验证码距离非有限数值: %r", dist)
- return None
- return d
- except Exception as e:
- logger.exception("验证码 API 调用失败: %s", e)
- return None
- def generate_human_track(distance):
- try:
- distance = float(distance)
- except (TypeError, ValueError):
- return []
- if distance <= 0 or not math.isfinite(distance):
- return []
- tracks = []
- current = 0
- mid = distance * 0.7
- t = 0.2
- v = 0
- move_points = []
- while current < mid:
- a = random.uniform(2, 4)
- v0 = v
- v = v0 + a * t
- move = v0 * t + 0.5 * a * t * t
- current += move
- move_points.append(move)
- while current < distance:
- a = -random.uniform(0.5, 1.5)
- v0 = v
- v = v0 + a * t
- if v < 0.5:
- v = 0.5
- move = v0 * t + 0.5 * a * t * t
- current += move
- move_points.append(move)
- total_points = len(move_points)
- for i, move in enumerate(move_points):
- y_offset = random.randint(-2, 2) if i % random.randint(2, 4) == 0 else 0
- if i < total_points * 0.3:
- duration = random.uniform(0.01, 0.03)
- elif i > total_points * 0.7:
- duration = random.uniform(0.03, 0.08)
- else:
- duration = random.uniform(0.02, 0.05)
- if random.random() < 0.05:
- duration += random.uniform(0.05, 0.1)
- tracks.append((move, y_offset, duration))
- if random.random() < 0.7:
- tracks.append((-random.randint(1, 3), 0, 0.05))
- return tracks
- def simulate_slider_drag(driver, slider_element, target_distance):
- if target_distance <= 0:
- logger.warning("滑块目标距离无效: %s", target_distance)
- return
- driver.actions.move_to(slider_element).hold()
- for offset_x, offset_y, duration in generate_human_track(target_distance):
- driver.actions.move(offset_x, offset_y, duration=duration / 1000)
- driver.actions.release()
- def solve_slider_captcha(driver):
- """检测并处理易盾滑块验证码,成功返回 True。"""
- driver.wait.doc_loaded()
- time.sleep(2)
- yidun = driver.ele("xpath://div[@class='yidun_modal']", timeout=3)
- if not yidun:
- return True
- logger.info("检测到滑块验证码,开始处理")
- jpg_bytes = yidun.get_screenshot(as_bytes="jpg")
- distance = call_captcha_api(jpg_bytes)
- if distance is None:
- logger.error("验证码识别失败")
- return False
- logger.info("滑块距离: %s", distance)
- slider = driver.ele(
- "xpath://div[contains(@class,'yidun_slider--hover')]", timeout=5
- )
- if not slider:
- logger.error("未找到滑块元素")
- return False
- try:
- drag_distance = float(distance) + SLIDER_OFFSET_FIX
- except (TypeError, ValueError):
- logger.error("滑块距离非数字: %r", distance)
- return False
- if not math.isfinite(drag_distance) or drag_distance <= 0:
- logger.error("滑块距离无效: %s", drag_distance)
- return False
- simulate_slider_drag(driver, slider, drag_distance-6)
- time.sleep(3)
- return True
|