开发者

Python+dddocr实现自动化通过多缺口滑块验证

目录
  • 一、什么是滑块验证
  • 二、核心思路
  • 三、代码实战
    • 1. 环境准备
    • 2. 主要代码结构
    • 3. 关键技术点说明
  • 四、常见问题与排查
    • 五、总结

      一、什么是滑块验证

      滑块验证是一种常见的反爬虫手段,用户需要按住滑块拖动到指定位置,才能通过验证。多缺口滑块验证则是在背景图上有多个缺口,滑块需要精确拖动到正确的缺口位置。

      手动操作很简单,但自动化通过却难倒了不少爬虫新手。本文将带你用 python + DrissionPage 实现自动化滑块验证。

      二、核心思路

      • 识别滑块和背景图:获取页面上的滑块和背景图片。
      • 识别缺口位置:用 OCR 或图像处理算法识别缺口的准确位置。
      • 模拟人类拖动滑块:用代码模拟鼠标按下、移动、释放,完成滑块验证。

      三、代码实战

      1. 环境准备

      • Python 3.8+
      • DrissionPage
      • pillow(用于图片处理)
      • ddddocr(滑块识别)
      pip install DrissionPage pillow ddddocr
      

      2. 主要代码结构

      2.1 滑块识别核心:recognize_slider_distance

      滑块验证的关键是如何准确识别滑块需要移动的距离。下面详细讲解 recognize_slider_distance 的实现原理和代码:

      import base64
      import numpy as np
      from PIL import Image
      import io
      import ddddocr
      
      def base64_decode(str):
          return base64.b64decode(str)
      
      def slider_puzzle_qg(p_puzzle_path: str = ''):
          """
          将小拼图去除透明边缘,返回处理后的base64图片和位置信息
          """
          image = Image.open(p_puzzle_path)
          width, height = image.size
          pixels = image.load()
          _a = 0  # 顶部透明行数
          _b = 0  # 有效拼图高度
          _c = 0  # 底部透明行数
          k = []  # 有效像素
          buffered = io.BytesIO()
          for x in range(height):
              data_pixel = []
              for y in range(width):
                  r, g, b, a = pixels[y, x]
                  data_pixel += [(r, g, b, a)]
              NumPy_data_pixel = np.array(data_pixel)
              if np.all(NumPy_data_pixel == 0):
                  if _b == 0:
                      _a += 1
                  else:
                      if _c == 0:
                          new_image = Image.new('RGBA', (width, _b), color=(0, 0, 0, 0))
                          new_image.putdata(k)
                          new_image.save(buffered, format="PNG")
                      _c += 1
              else:
                  _b += 1
                  k += data_pixel
          return {
              '最顶层-顶层距离': _a,
              '中间层-拼图的高度': _b,
              '最底层-底层距离': _c,
              'base64': base64.b64encode(buffered.getvalue()).decode()
          }
      
      def background_cutting(b_ppythonuzzle_path: str = '', b_size_h: int = 0, p_size_h: int = 0):
          """
          按小拼图的高度切割背景图,返回base64
          """
          image = Image.open(b_puzzle_path)
          width, height = image.size
          left, right = 0, width
          top, bottom = b_size_h, b_size_h + p_size_h
          cropped_image = image.crop((left, top, right, bottom))
          buffered = io.BytesIO()
          cropped_image.save(buffered, format="PNG")
          return {'base64': bas编程e64.b64encode(buffered.getvalue()).decode()}
      
      def recognize_slider_distance(xiaopintu_path, beijingtu_path):
          """
          识别滑块需要移动的距离
          :param xiaopintu_path: 小拼图路径
          :param beijingtu_path: 背景图路径
          :return: 滑动距离
          """
          det = ddddocr.DdddOcr(det=False, ocr=False, show_ad=False)
          xiaopintu_data = slider_puzzle_qg(p_puzzle_path=xiaopintu_path)
          beijingtu_data = background_cutting(b_puzzle_path=beijingtu_path, b_size_h=xiaopintu_data['最顶层-顶层距离'],
                                              p_size_h=xiaopintu_data['中间层-拼图的高度'])
          res1 = det.slide_match(base64_decode(beijingtu_data['base64']), base64_decode(xiaopintu_data['base64']),
                                 simple_target=True)
          res1 = res1['target'][0]
          # 为了更像人类操作,可以加一点偏移
          res = res1 * 1.1 - 4
          return res
      

      原理说明:

      • 先用 slider_puzzle_qg 去除小拼图的透明边缘,获得有效区域和高度。
      • background_cutting 按小拼图的高度切割背景图,获得滑道。
      • 用 ddddocr 的 slide_match 方法,计算小拼图在滑道中的最佳匹配位置(即缺口横坐标)。
      • 最后加一点偏移,模拟人类误差,防止被反爬虫检测。

      2.2 登录与滑块拖动

      下面是完整的登录与滑块拖动自动化代码示例,适合新手理解:

      import ranjavascriptdom
      import time
      import base64
      from DrissionPage import Chromium, ChromiumOptions
      from utils.ocr_util import recognize_slider_distance  # 上文已详细讲解
      from service.logger import logger
      
      # 创建浏览器实例
      co = ChromiumOptions().set_browser_path(r'C:\Path\To\chrome.exe')
      tab = Chromium().latest_tab
      
      def base64_decode(data):
          return base64.b64decode(data + '==')
      
      def login_with_slider(tab, username, password):
          try:
              # 1. 打开登录页面(URL已脱敏)
              tab.get('https://your-domain.com/login')
              # 2. 输入账号密码
              username_ele = tab.ele('@@tag()=input@@placeholder=请输入账号')
              password_ele = tab.ele('@@tag()=input@@placeholder=请输入密码')
              username_ele.input(username)
              password_ele.input(password)
              # 3. 点击登录按钮
              login_button = tab.ele('@@tag()=span@@text():登 录')
              login_button.click(by_js=True)
      
              # 4. 获取滑块和背景图片
              background_image_ele = tab.ele('@@tag()=div@@class=verify-img-panel').ele('@@tag()=img')
              gap_image_ele = tab.ele('@@tag()=div@@class=verify-sub-block').ele('@@tag()=img')
              xiaopintu_data = gap_image_ele.attr('src').split("data:image/png;base64,")[-1]
              background_img_data = background_image_ele.attr('src').split("data:image/png;base64,")[-1]
              with open('beijingtu.png', 'wb') as f:
                  f.write(base64_decode(background_img_data))
              with open('xiaopintu.png', 'wb') as f:
                  f.write(base64_decode(xiaopintu_data))
      
              # 5. 识别滑块需要移动的距离
              distance = recognize_slider_distance(xiaopintu_path="xiaopintu.png", beijingtu_path="beijingtu.png")
              tab.wait.ele_displayed('@@tag()=div@@class=verify-move-block', timeout=10)
              slider = tab.ele('@@tag()=div@@class=verify-move-block').wait.clickable(timeout=10)
              if not slider:
                  logger.error('未找到滑块元素,请检查类名或页面结构')
                  return False
              else:
                  tab.run_js('arguments[0].style.border="2px solid red"', slider)
                  logger.info(' 找到滑块元素,开始滑动')
                  # 6. 获取滑块中心点坐标
                  slider_rect = tab.run_js('''
                      const rect = arguments[0].getBoundingClientRect();
                      return {
                          x: rect.x + rect.width / 2,
                          y: rect.y + rect.height / 2
                      }
                  ''', slider)
                  try:
                      # 7. 用 JS 模拟鼠标事件拖动滑块
                      tab.run_js('''
                          function simulateMouseEvent(element, eventType, x, y) {
                              const event = new MouseEvent(eventType, {
                                  view: window,
                                  bubbles: true,
                                  cancelable: true,
                                  clientX: x,
                                  clientY: y
                              });
                              element.dispatchEvent(event);
                          }
                          const slider = arguments[0];
                          const startX = arguments[1];
                          const startY = arguments[2];
                          const distance = arguments[3];
                          simulateMouseEvent(slider, 'mousedown', startX, startY);
                          const segments = 5;
                          const segmentDistance = distance / segments;
                          let currentX = startX;
                          for (let i = 0; i < segments; i++) {
                              currentX += segmentDistance;
                              simulateMouseEvent(slider, 'mousemove', currentX, startY);
                          }
                          currentX -= 5;
                          simulateMouseEvent(slider, 'mousemove', currentX, startY);
                          simulateMouseEvent(slider, 'mouseup', currentX, startY);
                      ''', slider, slider_rect['x'], slider_rect['y'], distance)
                      logger.info(' 滑动操作已发送')
                      time.sleep(1)
                  except Exception as e:
                      logger.error(f' 滑动出错: {e}')
                      return False
              # 8. 后续验证、UKey等操作略
              return True
          except Exception as e:
              logger.error(f"登录失败,错误信息:{e}")
              return False
      

      代码说明:

      步骤1-3:自动化输入账号、密码并点击登录。

      步骤4:获取滑块和背景图片,保存为本地文件。

      步骤5:调用 recognize_slider_distance 识别滑块需要移动的距离。

      步骤6:获取滑块元素的中心点坐标。

      步骤7:用 JavaScript 分段模拟鼠标拖动滑块,模拟人类操作。此处也可以使用drisionpage操作元素例如:

      # 向右移动鼠标
      # jstab_.actions.right(distance - 10)
      time.sleep(0.05)
      # tab_.actions.right(10)
      

      步骤8:后续如UKey验证等可根据实际需求补充。

      3. 关键技术点说明

      • 图片识别recognize_slider_distance 利用 ddddocr 的滑块识别能力,极大简化了滑块距离的计算。
      • 图片预处理:去除透明边缘、切割滑道,是提升识别准确率的关键。
      • JS 模拟鼠标事件:直接用 JS 触发 mousedownmousemovemouseup,比传统动作链更容易绕过部分反爬虫。
      • 分段移动:模拟人手抖动,分多段移动更像真人。
      • 异常处理:每一步都要加 try/except,方便调试。

      四、常见问题与排查

      1.滑块没动?

      • 检查元素选择器是否准确。
      • 检查 JS 事件是否被页面拦截。
      • 尝试加大分段数,或调整每段距离。

      2.识别距离不准?

      • 检查图片保存是否完整。
      • 调整识别算法参数。
      • 检查小拼图和背景图的预处理是否正确。

      3.验证失败?

      • 检查是否有额外的行为验证(如轨迹分析、行为分析)。
      • 尝试加大拖动时间,模拟更慢的拖动。

      五、总结

      自动化通过多缺口滑块验证并不神秘,关键在于:

      • 正确识别滑块和缺口位置(图片预处理+滑块识别)
      • 用 JS 或动作链模拟真实拖动
      • 多调试、多尝试,遇到问题多看日志

      到此这篇关于Python+dddocr实现自动化通过多编程客栈缺口滑块验证的文章就介绍到这了,更多相关Python滑块验证内容请搜索编程客栈(www.devze.com)以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程客栈(www.devze.com)!

      0

      上一篇:

      下一篇:

      精彩评论

      暂无评论...
      验证码 换一张
      取 消

      最新开发

      开发排行榜