欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  科技

识别滑动拼图验证码的新策略

程序员文章站 2022-06-15 10:18:40
原理你好! 这是你第一次使用 Markdown编辑器 所展示的欢迎页。如果你想学习如何使用Markdown编辑器, 可以仔细阅读这篇文章,了解一下Markdown的基本语法知识。新的改变我们对Markdown编辑器进行了一些功能拓展与语法支持,除了标准的Markdown编辑器功能,我们增加了如下几点新功能,帮助你用它写博客:全新的界面设计 ,将会带来全新的写作体验;在创作中心设置你喜爱的代码高亮样式,Markdown 将代码片显示选择的高亮样式 进行展示;增加了 图片拖拽 功能,你可以将本地的...

背景

关于滑动拼图验证码,网上识别的一般流程为先获取切割后的验证码背景图,如图1,然后再找到坐标拼接为完整的图片,之后比较出缺口图片与不带缺口图片之间像素不同的地方,从而计算出缺口位置,最后移动滑块。这里关键点在于找到不带缺口的图片和根据坐标拼接正确图片,对于有些网站,完成这两步还是有一定难度的,这里介绍一种可以略过这两步的方法。
识别滑动拼图验证码的新策略

原理

大家都知道,滑动拼图验证码一般由一张可移动的小图和带缺口的背景图组成,其中为了方便观察,缺口位置会做的比周围图片更暗或者更亮。根据这个现象,对缺口图片进行二值化处理,如果缺口位置比较亮,如图2(为了消除小图的干扰,先把小图切割掉了),阈值可以设置为255,然后识别图片中较亮的轮廓,如果没有识别出这样的轮廓,则减小阈值,处理后的结果类似图3;反之,如果缺口位置比较暗,阈值可以设置为0,后面的操作与上述较亮的情况一致。这样总能识别出含缺口位置的轮廓,理想情况下就能获得缺口边界的坐标,后面就是常规的拖动滑块移动操作了。
识别滑动拼图验证码的新策略

识别滑动拼图验证码的新策略

代码及测试

以下操作使用的是天安保险登录中的滑动拼图验证码。代码如下:

import cv2
import numpy as np
from PIL import Image
from selenium import webdriver
from selenium.webdriver import ActionChains
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from time import sleep
import random

SCRIPT = 'Object.defineProperty(navigator,"webdriver",{get:()=>undefined})'


def getTrack(gap):
    # 生成滑动轨迹
    track = []
    gap = min(gap) + 70
    # 当前位移
    current = 0
    # 减速阈值
    mid = gap * 4 / 5  # 前4/5段加速 后1/5段减速
    # 计算间隔
    t = 0.2
    # 初速度
    v = 0

    while current < gap:
        if current < mid:
            a = 3  # 加速度为+3
        else:
            a = -3  # 加速度为-3

        # 初速度v0
        v0 = v
        # 当前速度
        v = v0 + a * t
        # 移动距离
        move = v0 * t + 1 / 2 * a * t * t
        # 当前位移
        current += move
        # 加入轨迹
        track.append(round(move))

    return track


# 处理图片
def handle(path, threshold):
    img = Image.open(path)
    img = img.convert('L')
    table = []
    for i in range(256):
        if i < threshold:
            table.append(0)
        else:
            table.append(1)

    bim = img.point(table, '1')
    bim.save('./imgs/handled.png')


def contou():
    # 加载图片
    img = cv2.imread('./imgs/handled.png')
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    ret, binary = cv2.threshold(gray, 100, 255, cv2.THRESH_BINARY)
    contours, hierarchy = cv2.findContours(
        binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
    # 所有轮廓x坐标的列表
    x_list = []
    for i in range(len(contours)):
        M = cv2.moments(contours[i])
        if M["m00"] != 0:
            center_x = int(M["m10"] / M["m00"])
            center_y = int(M["m01"] / M["m00"])
            x_list.append(center_x)
        else:
            pass
    return x_list


def getImg():
    # 设置二值化阈值
    threshold = 255
    # 天安保险登录网址
    url = 'https://tianaw.95505.cn/tacpc/#/login'
    phone_num = '15537123105'
    pwd = '111111111'
    browser = webdriver.Firefox()
    browser.execute_script(SCRIPT)
    wait = WebDriverWait(browser, 10)
    browser.get(url)
    # 切换到账号登录标签
    account = wait.until(EC.presence_of_element_located(
        (By.CSS_SELECTOR, '.tabForm > button:nth-child(3)')))

    account.click()

    input_phone = wait.until(EC.presence_of_element_located(
        (By.CSS_SELECTOR, '#app > nz-content > app-login > nz-content > div > div > div > app-log-form > div > form > nz-form-item:nth-child(1) > nz-form-control > div > span > nz-input-group > span > input')))
    # 输入手机号
    input_phone.send_keys(phone_num)
    input_pwd = wait.until(EC.presence_of_element_located(
        (By.CSS_SELECTOR, '#app > nz-content > app-login > nz-content > div > div > div > app-log-form > div > form > nz-form-item:nth-child(2) > nz-form-control > div > span > nz-input-group > span > input')))
    # 输入密码
    input_pwd.send_keys(pwd)
    # 点击同意协议
    wait.until(EC.presence_of_element_located(
        (By.CSS_SELECTOR, '.ant-checkbox-input'))).click()
    click_btn = wait.until(EC.presence_of_element_located(
        (By.CSS_SELECTOR, '#app > nz-content > app-login > nz-content > div > div > div > app-log-form > div > form > nz-form-item:nth-child(4) > nz-form-control > div > span > button')))
    click_btn.click()
    # 获取验证码图片
    canvas = wait.until(EC.presence_of_element_located(
        (By.CSS_SELECTOR, '#captchasli > canvas:nth-child(1)')))
    canvas.screenshot('./imgs/1.png')
    btn = wait.until(EC.presence_of_element_located(
        (By.CSS_SELECTOR, '.sliderIcon')))
    cropImg()
    # 判断在该threshold下是否识别出轮廓,如果没有增大或减小阈值
    while True:
        handle('./imgs/croped.png', threshold)
        gap = contou()
        if not gap:
            threshold -= 3
            continue
        else:
            break
    # 获取滑块轨迹
    track = getTrack(gap)
    # 移动滑块
    ActionChains(browser).click_and_hold(btn).perform()
    # print(track)
    for x in track:
        y = random.uniform(-3, 3)
        ActionChains(browser).move_by_offset(xoffset=x, yoffset=y).perform()
    ActionChains(browser).release(btn).perform()
    sleep(1.5)
    browser.close()


def cropImg():
    # 剪切图片,去除小图对轮廓识别的影响
    base_crop = 70
    img1 = Image.open('./imgs/1.png')
    box = (base_crop, 0, img1.size[0], img1.size[1])
    img = img1.crop(box)
    img.save('./imgs/croped.png')


if __name__ == "__main__":
    getImg()

整个代码的实现逻辑为:先用selenium访问登录界面,输入账号密码后调出拼图验证码,截取验证码图片,再把包含小滑块的图片部分切割掉,之后处理图片,识别轮廓,根据识别出的坐标生成移动轨迹,最后移动滑块到缺口位置,其中借鉴了一些网友的代码。
下面这张是测试的动图:
识别滑动拼图验证码的新策略
一共测试了6次,成功了4次,失败了两次。
另外也测试了缺口位置较暗的情况,验证码如图4,结果就不再演示了。
识别滑动拼图验证码的新策略

结语

以上只做学习交流使用,感兴趣的同学欢迎尝试,也欢迎各位提出宝贵意见,另转载请注明出处。

本文地址:https://blog.csdn.net/a97d1f4b2/article/details/107188556