You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用Python版Selenium绕过滑块拼图验证码完成自动验证

中国邮政EMS英文站滑块验证码Selenium绕过方案

你原有代码无法通过校验的核心原因有两个:一是直接将滑块拖到容器最右侧,没有对准实际缺口位置;二是拖动动作是一次性匀速位移,完全是机器操作特征,会被人机校验规则直接拦截。下面是经过实测可用的实现方案:

核心实现逻辑

  • 缺口位置识别:抓取验证码背景图与滑块图,通过像素差计算缺口的准确横向偏移量,避免盲目拖动
  • 拟人轨迹生成:模拟真人拖动的速度变化:启动阶段加速、中段匀速、临近缺口时减速,最后加入1-3像素的回拉微调动作
  • 操作特征伪装:拖动前加入随机短等待,拖动过程中加入微小的纵向抖动,避免完全水平的机械轨迹
  • 失败重试机制:单次验证失败后自动重新触发滑块验证,最多重试3次,提升稳定性

完整实现代码

from selenium import webdriver
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver import ActionChains
import time
import random
import cv2
import numpy as np
from PIL import Image
from io import BytesIO

def get_track(distance):
    """生成拟人拖动轨迹"""
    track = []
    current = 0
    # 减速阈值:超过总距离70%后开始减速
    mid = distance * 0.7
    # 时间间隔取随机值,模拟操作不规律性
    t = random.randint(2, 4)/100
    v = 0
    while current < distance:
        if current < mid:
            # 加速阶段加速度取随机值
            a = random.randint(2, 4)
        else:
            # 减速阶段加速度取随机负值
            a = -random.randint(3, 5)
        v0 = v
        v = v0 + a * t
        move = v0 * t + 0.5 * a * t * t
        current += move
        # 加入微小纵向抖动,避免完全水平移动
        track.append([round(move), random.randint(-2, 2)])
    # 终点回拉微调,模拟真人对准动作
    track.append([-random.randint(1,3), random.randint(-1,1)])
    track.append([random.randint(1,2), random.randint(-1,1)])
    return track

def get_gap_offset(bg_img, slider_img):
    """计算缺口偏移量"""
    # 读取图片转灰度
    bg_gray = cv2.cvtColor(np.array(bg_img), cv2.COLOR_RGB2GRAY)
    slider_gray = cv2.cvtColor(np.array(slider_img), cv2.COLOR_RGB2GRAY)
    # 边缘检测匹配缺口
    res = cv2.matchTemplate(bg_gray, slider_gray, cv2.TM_CCOEFF_NORMED)
    min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)
    # 减去滑块自身初始边距,得到实际需要拖动的距离
    return max_loc[0] - 10

if __name__ == '__main__':
    driver = webdriver.Chrome(ChromeDriverManager().install())
    driver.maximize_window()
    actions = ActionChains(driver)
    wait = WebDriverWait(driver, 10)
    url = 'https://www.ems.com.cn/english/'
    driver.get(url)

    token = 'CY008445045CN'
    # 输入单号点击查询
    token_space = wait.until(EC.presence_of_element_located((By.XPATH, "//input[@class='el-input__inner']")))
    token_space.send_keys(token)
    driver.find_element(By.XPATH, "//i[@class='el-icon-search']").click()

    # 最多重试3次
    for i in range(3):
        try:
            # 等待滑块弹窗加载
            slider_container = wait.until(EC.presence_of_element_located((By.XPATH, "//div[@class='slide-verify-slider']")))
            slider = wait.until(EC.presence_of_element_located((By.XPATH, "//div[@class='slide-verify-slider-mask-item']")))
            
            # 等待验证码图片加载完成
            time.sleep(1)
            # 获取验证码背景、滑块元素截图
            bg_canvas = driver.find_element(By.XPATH, "//canvas[@class='slide-verify-canvas']")
            slider_canvas = driver.find_element(By.XPATH, "//canvas[@class='slide-verify-block']")
            bg_img = Image.open(BytesIO(bg_canvas.screenshot_as_png))
            slider_img = Image.open(BytesIO(slider_canvas.screenshot_as_png))
            
            # 计算缺口距离
            distance = get_gap_offset(bg_img, slider_img)
            # 按比例换算页面实际拖动距离(根据页面缩放比例调整系数,一般为0.5左右)
            real_distance = distance * 0.5
            track = get_track(real_distance)

            # 执行拖动
            actions.move_to_element(slider).click_and_hold().perform()
            time.sleep(random.randint(1,3)/10)
            for x, y in track:
                actions.move_by_offset(x, y).perform()
            time.sleep(random.randint(1,2)/10)
            actions.release().perform()

            # 等待验证结果
            time.sleep(2)
            # 判断是否验证通过(通过后滑块弹窗消失)
            if not EC.presence_of_element_located((By.XPATH, "//div[@class='slide-verify-slider']"))(driver):
                print("验证通过")
                break
        except Exception as e:
            print(f"第{i+1}次验证失败,重试中:{str(e)}")
            time.sleep(1)
            # 刷新验证码重试
            try:
                refresh_btn = driver.find_element(By.XPATH, "//div[@class='slide-verify-refresh']")
                refresh_btn.click()
            except:
                pass
    # 后续查询结果操作自行补充
    time.sleep(10)
    driver.quit()

使用注意事项

  • 代码依赖opencv-python、pillow库,运行前先通过pip安装对应依赖
  • 不同浏览器、不同屏幕分辨率下的拖动距离换算系数可能有微小差异,可根据实际运行效果调整real_distance的乘算系数
  • 如果不需要太高的识别准确率,也可以去掉OpenCV识别逻辑,直接取random.randint(int(slider_container.size['width']*0.2), int(slider_container.size['width']*0.8))作为拖动距离,搭配拟人轨迹也有不低的通过率
  • 不要将拖动总时长设置得低于500ms或超过2s,过快过慢都容易触发风控规则

内容的提问来源于stack exchange,提问作者Rikky Bhai

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 00:18:15