当前位置: 移动技术网 > 科技>操作系统>windows > 识别滑动拼图验证码的新策略

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

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

背景

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

原理

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

图3:二值化处理后的图1

代码及测试

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

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

结语

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

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

如您对本文有疑问或者有任何想说的,请点击进行留言回复,万千网友为您解惑!

相关文章:

验证码:
移动技术网