python 识别滑块验证码

# -*- coding: utf-8 -*-

import io
import json
from pathlib import Path
from PIL import Image
import base64
import cv2
import time
import requests
sess = requests.session()

class SlideCrack(object):
    def __init__(self, front, bg, out=None):
        """
        init code
        :param front: 缺口图片
        :param bg: 背景图片
        :param out: 输出图片
        """
        self.front = front
        self.bg = bg
        self.out = out



    @staticmethod
    def clear_white(img):
        # 清除图片的空白区域,这里主要清除滑块的空白
        img = cv2.imread(img)
        rows, cols, channel = img.shape
        min_x = 255
        min_y = 255
        max_x = 0
        max_y = 0
        for x in range(1, rows):
            for y in range(1, cols):
                t = set(img[x, y])
                if len(t) >= 2:
                    if x <= min_x:
                        min_x = x
                    elif x >= max_x:
                        max_x = x
                    if y <= min_y:
                        min_y = y
                    elif y >= max_y:
                        max_y = y
        img1 = img[min_x:max_x, min_y: max_y]
        return img1

    def template_match(self, tpl, target):
        th, tw = tpl.shape[:2]
        result = cv2.matchTemplate(target, tpl, cv2.TM_CCOEFF_NORMED)
        # 寻找矩阵(一维数组当作向量,用Mat定义) 中最小值和最大值的位置
        min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
        tl = max_loc
        br = (tl[0] + tw, tl[1] + th)
        # 绘制矩形边框,将匹配区域标注出来
        # target:目标图像
        # tl:矩形定点
        # br:矩形的宽高
        # (0,0,255):矩形边框颜色
        # 1:矩形边框大小
        cv2.rectangle(target, tl, br, (0, 0, 255), 2)
        return tl[0]

    @staticmethod
    def image_edge_detection(img):
        edges = cv2.Canny(img, 100, 200)
        return edges

    def discern(self):
        img1 = self.clear_white(self.front)
        img1 = cv2.cvtColor(img1, cv2.COLOR_RGB2GRAY)
        slide = self.image_edge_detection(img1)

        back = cv2.imread(self.bg, 0)
        back = self.image_edge_detection(back)

        slide_pic = cv2.cvtColor(slide, cv2.COLOR_GRAY2RGB)
        back_pic = cv2.cvtColor(back, cv2.COLOR_GRAY2RGB)
        x = self.template_match(slide_pic, back_pic)
        # 输出横坐标, 即 滑块在图片上的位置
        return int(x)


def bg_captcha_0(img,bgarray:list):
    """
    滑块背景图还原
    """
    if isinstance(img, (str, Path)):
        _img = Image.open(img)
    elif isinstance(img, bytes):
        _img = Image.open(io.BytesIO(img))
    else:
        return
    # 创建一个新图
    new_img = Image.new('RGB', (300, 200))

    # 图片还原顺序
    __imgx = 300
    __imgy = 200
    _cutX = int(__imgx / 10)
    _cutY = int(__imgy / 2)
    for i in range(len(bgarray)):
        # 从背景图中裁剪出对应位置的小块  Image.crop(左,上,右,下)坐标的4元组
        if i<10:
            crop_tuple = (int(i*_cutX),0,int((i+1)*_cutX),100)
        else:
            crop_tuple = (int((i-10)*_cutX),100,int((i-10+1)*_cutX),200)
        img_cut = _img.crop(crop_tuple)

        # 将小块拼接到新图中,按bgarray数组给的位置来写坐标。
        if bgarray[i]<10:
            new_img.paste(img_cut,(bgarray[i]*30,0))
        else:
            new_img.paste(img_cut, ((bgarray[i]-10)*30,100))

    new_img.save('./image/bg.png')
    return new_img


def indexOf(arr,str_):
    if arr and arr.index:
        return arr.index(str_)
    lenarr = len(arr)
    for i in range(lenarr):
        if arr[i] == str_:
            return i
    return -1

# 生成轨迹
def get_tracks(distance, rate=0.5, t=0.2, v=0):
    """
    将distance分割成小段的距离
    :param distance: 总距离
    :param rate: 加速减速的临界比例
    :param a1: 加速度
    :param a2: 减速度
    :param t: 单位时间
    :param t: 初始速度
    :return: 小段的距离集合
    """
    tracks = []
    # 加速减速的临界值
    mid = rate * distance
    # 当前位移
    s = 0
    # 循环
    while s < distance:
        # 初始速度
        v0 = v
        if s < mid:
            a = 20
        else:
            a = -3
        # 计算当前t时间段走的距离
        s0 = v0 * t + 0.5 * a * t * t
        # 计算当前速度
        v = v0 + a * t
        # 四舍五入距离,因为像素没有小数
        tracks.append(round(s0))
        # 计算当前距离
        s += s0

    datelist = ''
    distance = 0
    for i in tracks[1:]:
        distance += i
        datelist_time = round(time.time() * 1000)
        datelist += str(distance) + ',' + str(datelist_time) + '|'
        time.sleep(0.005)
    return datelist[:-1]


# 注册和校验
def check_request(data):
    headers = {
        'Cookie': 'ASP.NET_SessionId=qnfya5nvtn2b3pkycs5hs1zv; gr_user_id=f0c2c220-c9b3-4d70-96dd-1ee89e08a87f; gr_session_id_81eec0d0f0be2df1=da98e241-8c1a-4496-a734-61ad1907ef90; gr_session_id_81eec0d0f0be2df1_da98e241-8c1a-4496-a734-61ad1907ef90=true; xCloseNew=27; redSpot=false',
        'Host': 'gov.pkulaw.cn',
        'Origin': 'http://gov.pkulaw.cn',
        'Pragma': 'no-cache',
        'Referer': 'http://gov.pkulaw.cn/',
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.67 Safari/537.36',
        'X-Requested-With': 'XMLHttpRequest',
    }
    url = 'http://gov.pkulaw.cn/VerificationCode/GetVerifiCodeResult'
    res = sess.post(url,headers=headers,data=data)
    return res


def process_main():
    register_data = {
        'act': 'getcode',
        'spec': '300*200'
    }
    data = json.loads(json.loads(check_request(register_data).text))
    bgarray = [int(i) for i in data['array'].split(',')]
    # 分割字符串
    normal = data['normal'].split(',')[1]
    small = data['small'].split(',')[1]

    # base64解码
    b64decode = base64.b64decode(normal)
    # 背景还原
    bg_captcha_0(b64decode, bgarray=bgarray)
    # 滑块图片
    hk_image = base64.b64decode(small)
    with open('./image/hk.png', 'wb') as f:
        f.write(hk_image)
    # 获取缺口距离
    sc = SlideCrack('./image/bg.png','./image/hk.png')
    slide_s = sc.discern()
    check_data = {
        'act': 'check',
        'point': str(slide_s),
        'timespan': '452',
        'datelist': get_tracks(slide_s),
    }
    print(check_request(check_data).text)


process_main()

https://www.cnblogs.com/wyh0923/p/16528354.html
posted @ 2022-07-31 23:02  莫贞俊晗  阅读(534)  评论(0编辑  收藏  举报