验证码破解之----传统破解方式

前言
      最近在爬取数据的过程当中遇到验证码问题,正好遇到的是比较容易的,是字母和数字的验证码。本文就描述一下常规破解的方式。
主要内容包括:
图片二值化
图片降噪
图片切割
矢量计算
验证码获取
要破解验证码,第一步需要将我们看到的验证码下载下来。
我们点击我们要破解的验证码,然后按下F12,看一下浏览器提交的请求,可以看到验证码的图片。

然后我们写一个captchaDownload.py的程序:
--------------------------------------------------------------------------------
/* ---示例代码----*/
 
import urllib.request
from PIL import Image
import io
 
imagePath = 'D:/Yonyou/验证码yy/'
 
for count in range(0,1000):
    checkCodeUrl = 'https://euc.yonyoucloud.com/images/getValiImage?ts=1502786693000289491'
    
    headers = {'Referer':'https://euc.yonyoucloud.com/usercenter/emailMOD'}
    headers['Host'] = 'euc.yonyoucloud.com'
    headers['User-Agent'] = 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36'
    headers['Connection'] = 'keep-alive'
    
    request = urllib.request.Request(checkCodeUrl)
    res = urllib.request.urlopen(request).read()
    
    #print(res)
    
    image = Image.open(io.BytesIO(res))
    fullName = imagePath+'CheckCode'+str(count)+'.jpg'
    image.save(fullName)
    count += 1
 
/* ---示例代码----*/
--------------------------------------------------------------------------------
运行以上代码以后,在验证码yy文件夹下就下载了100个验证码图片。如下图:

这样,验证码图片就准备好了。
降噪及切割
可以看到字母和数字的背后有很多噪音,首先要考虑去除这些噪音。
可以参考prepareCaptcha.py文件。
--------------------------------------------------------------------------------
/* ---示例代码----*/
 
from PIL import Image
 
 
imagePath = 'D:/验证码yy/'
 
count = 0
for i in range(339,984):
    imageFile = 'CheckCode'+str(i)+'.jpg'
    image = Image.open(str(imagePath+imageFile))
    imageGray = image.convert('L') #转化为灰度
    #降低噪音,同时转化成二进制
    threshold = 75
    table = []
    for i in range(256):
        if i < threshold:
            table.append(0)
        else:
            table.append(1)
            
    out = imageGray.point(table,'1')
    #转回RGB格式
    img2 = out.convert('L')
    
    #截取字符
    inletter = False
    foundletter = False
    start = 0
    end = 0
    letters = []
    
    for x in range(0,img2.width):
        for y in range(0,img2.height):
            pix = img2.getpixel((x,y))
            if pix != 255:
                inletter = True
                
        if foundletter == False and inletter == True:
            foundletter = True
            start = x
            
        if foundletter == True and inletter == False:
            foundletter = False
            end = x
            letters.append((start,end))
            
        inletter = False
    
    #只保留切割后最大的四个图片
    lens = []
    if len(letters)>4:
        for letter in letters:
            temp = letter[1]-letter[0]
            lens.append(temp)
        #对宽度进行排序
        lens.sort(reverse=True)
        #小于第四个宽度的则删除
        for letter in letters:
            if (letter[1]-letter[0])                letters.remove(letter)
            
    savePath = 'D:/验证码yy/切割后图片/'
    for letter in letters:
        tmpImg = img2.crop((letter[0],0,letter[1],img2.height))
        fullName = 'D:/验证码yy/切割后图片/'+str(count)+'.jpg'
        tmpImg.save(fullName)
        count += 1
        print(count)      
/* ---示例代码----*/
--------------------------------------------------------------------------------
程序运行后,可以看到,切割好的图片。


字符匹配
因为这些字符采用都是标准字符,没有变形,所以我们可以将标准的文字库与截取的图片进行对比,对比的方式就是将截取图片和标准图片处理成两个向量,然后计算两个向量的相似度。计算的方式就是将每个图片与标准库中的每个图片计算cos值,最小的就是最接近的。
使用的是 AI与向量空间图像识别 将标准图片转换成向量坐标a,需要识别的图片字段为向量坐标b,cos(a,b)值越大说明夹角越小,越接近重合。
空间向量的计算公式:

核心代码如下:
--------------------------------------------------------------------------------
/* ---示例代码----*/
# 夹角公式
class VectorCompare:
    # 计算矢量大小
    # 计算平方和
    def magnitude(self, concordance):
        total = 0
        for word, count in concordance.items():
            total += count ** 2
        return math.sqrt(total)
 
    # 计算矢量之间的 cos 值
    def relation(self, concordance1, concordance2):
        topvalue = 0
        for word, count in concordance1.items():
            if word in concordance2:
                # 计算相乘的和
                topvalue += count * concordance2[word]
        return topvalue / (self.magnitude(concordance1) * self.magnitude(concordance2))
 
 
# 将图片转换为矢量
def buildvector(im):
    d1 = {}
    count = 0
    for i in im.getdata():
        d1[count] = i
        count += 1
    return d1
/* ---示例代码----*/
--------------------------------------------------------------------------------

你可能感兴趣的:(验证码破解之----传统破解方式)