记一次Python验证码识别并将模型移植到Java项目的过程

前一段时间需要利用某个学校的教务网的数据来构建一个web应用,后台使用Java。

本来这应该是一个很简单的任务,只需要利用JSoup写好爬虫就可以了,但没想到打开教务网之后发现每次都需要输入验证码,那么一方面为了简化用户操作另一方面也为了练手,我决定识别这个验证码来实现无验证码登录。

能这样做的主要原因是它的验证码长得还是比较规整的,基本上是这个样子:

验证码

但还是不够规整,不能直接用图像处理然后匹配来做,如果长这个样子完全可以匹配像素点然后来识别数字的:

emmm

当时正好在学tensorflow,于是我决定利用tensorflow训练一个简单的NN模型来对验证码进行识别(当时本来想着如果效果不好就改CNN,没想到简单的NN就已经有90多的正确率了),然后再把模型放到Java的爬虫上作为登录的工具。

1.训练模型

1.1.数据获取

由于没有现成的数据集,所以我自己用python写了一个爬虫爬了1000张的验证码图片作为训练数据集:

download.py:

import requests
from PIL import Image 
import time
def getOnePic(code):
    r=requests.get('http://xx.xx.xx.xx/CheckCode.aspx')	#获取验证码页面
    f=open('./oriPic/'+code+'.gif','wb')	#打开文件
    f.write(r.content)	#写出图片
    f.close()	#关闭文件
    time.sleep(1)	#休眠一段时间防止频率过高爆炸
for i in range(1001):	#获取1000张图片
    getOnePic(str(i))	

执行完成之后,我就已经拥有了比较多的数据可以用于训练了:

3

1.2.数据处理

按照以往的经验,把图片黑白化之后,会使得识别效果更好,此外这个验证码每个字符所处的位置还是比较规整的,可以将字符切分之后分别识别,如果不切分的话这个数据量应该是不够的,因为四个字符输出会有 3 6 4 36^{4} 364种可能。

1.2.1.工具文件实现

tool.py:

from PIL import Image
import numpy as np
# 阅读标签列表
def readList(filename):
    li=[]
    with open(filename,'r') as fp:  # 打开文件
        for l in fp:    # 遍历每一行
            strs=l.split(',')
            for w in strs[:-1]:
                li.append(w)
    return li

# 打开图片文件为图片或数组
def openPic(filename,arr=False):
    return  np.array(Image.open(filename)) if arr else Image.open(filename)

#打印一个数组
def printArray(arr,spl=False):
    for i in arr:
        count=-1
        for j in i:
            count+=1
            #if(count%5==0 and count!=0):
            #    print('\033[50m ',end='')
            if(spl and (count==13 or count==26 or count==39)):
                print('\033[40m     ',end='')
            print('\033[42mx' if j>=128 else '\033[46mx',end='')
        print()
# 获取分割后的子图
def getSplitPic(pic):
    res=[]
    for i in range(4):  # 共四个子图
        res.append(pic.crop((3+i*12,0,3+(i+1)*12,20)))
    return res

# 判断一个像素点是否为黑色
def isBlack(dot):
    return dot<128
    
# 清除孤立点
# f=0 上 1 左 2 下 3 右
def check_neiber(arr,y,x,f,w):
    if w==0:    # 如果超过了最大栈 撤回
        return False # 不是要清除的点
    if x>0 and isBlack(arr[y][x-1]) and f!=1: # 如果在左边发现黑点并且不来自左边
        if not check_neiber(arr,y,x-1,3,w-1):   # 递归检查左边,如果不需要清除
            return False    # 返回
    if x<len(arr[0])-1 and isBlack(arr[y][x+1]) and f!=3:    # 检查右边
        if not check_neiber(arr,y,x+1,1,w-1):  
            return False
    if y>0 and isBlack(arr[y-1][x])  and f!=0:    # 检查上边
        if not check_neiber(arr,y-1,x,2,w-1):   # 表示从下边过来的
            return False
    if y<len(arr)-1 and isBlack(arr[y+1][x]) and f!=2:   # 检查下边
        if not check_neiber(arr,y+1,x,0,w-1):
            return False
    arr[y][x]=255   # 清除
    return True
# 黑白化并去噪
def clearPic(arr):
    for i in range(len(arr)):
        for j in range(len(arr[0])):
            arr[i][j]=0 if arr[i][j]<128 else 255   # 首先设置黑白点
            if isBlack(arr[i][j]):  # 如果是黑点
                check_neiber(arr,i,j,-1,2)   # 处理噪音

1.2.2.黑白化并分割字符

dealCheckCode.py:

import sys
sys.path.append("../")
import tool
import numpy as np
from PIL import Image
import readData
#获取处理之后的子数组
def handlePicToPics(pic):
    pic=np.array(pic.crop((4,0,56,20)))
    tool.clearPic(pic)   #黑白化并去噪
    #tool.printArray(pic) #打印看看
    pic=Image.fromarray(pic)    #获取图片
    pics=tool.getSplitPic(pic)   #获取分割后的子图
    arrays=[]
    for i in range(4):
        arrays.append(p
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值