python

关注公众号 jb51net

关闭
首页 > 脚本专栏 > python > Python滑块验证

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

作者:冷月半明

滑块验证是一种常见的反爬虫手段,多缺口滑块验证则是在背景图上有多个缺口,滑块需要精确拖动到正确的缺口位置,下面我们就来看看如何使用Python实现自动化通过多缺口滑块验证吧

一、什么是滑块验证

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

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

二、核心思路

三、代码实战

1. 环境准备

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_puzzle_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': base64.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

原理说明:

2.2 登录与滑块拖动

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

import random
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操作元素例如:

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

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

3. 关键技术点说明

四、常见问题与排查

1.滑块没动?

2.识别距离不准?

3.验证失败?

五、总结

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

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

您可能感兴趣的文章:
阅读全文