"""易盾滑块验证码:云码识别 + 模拟拖拽。""" 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