python破解简单验证码

此破解的验证码只适合,字符颜色单一,且字符分割较明显的验证码

破解验证码属于机器学习分支的内容, 破解验证码方式有多重,这里使用向量空间搜索的方法

数据集来自于实验楼。由版权原因请自行到https://www.shiyanlou.com/courses/364下载,读者可配合实验内容进行理解

图片二值化即,将图片变成只有黑白两种颜色, 其他大部分在代码中已解释


#coding:utf-8

"""
    此程序只适合红色220跟227画出来的字符,其他颜色不支持
    2018年5月2日21:55:57
    
"""


from PIL import Image
import hashlib
import time
import math
import os

class VectorCompare:
    """
    2 篇文档所使用的相同的单词越多,那这两篇文章就越相似!但是这单词太多怎么办,就由我们来选择几个关键单词,选择的单词又被称作特征,
    每一个特征就好比空间中的一个维度(x,y,z 等),一组特征就是一个矢量,每一个文档我们都能得到这么一个矢量,只要计算矢量之间的夹角就能得到文章的相似度了。


    """
    #计算矢量大小, 即二范数
    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):
        relevance = 0
        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 sort_his():
    """将图片排序, 方便选取颜色,去除噪声"""
    his = im.histogram()
    values = {}

    for i in range(256):
        values[i] = his[i]

    hisSort = sorted(values.items(), key = lambda x: x[1], reverse = True)
    #获取前10的颜色以及数量
    for j, k in hisSort[:10]:
        print(j, k)



def binaryzation(im):
    """将图片二值化"""
    #获得一张全白的背景,即与im同样大小,模式为P,像素全为255的图片
    im2 = Image.new("P", im.size, 255)

    #获取图像矩阵的行列
    for y in range(im.size[1]):
        for x in range(im.size[0]):
            #获取像素的RGB值
            pix = im.getpixel((x, y))
        #   if pix == 219:#用219画出来不是字符的轮廓
            #220跟227是红色,即想要的颜色(通过尝试前面多种颜色的效果,对比输入输出图片)
            if pix == 220 or  pix == 227:
                #im2的白色背景上画像素,颜色为0(黑色)
                im2.putpixel((x, y), 0)

    return im2

def cutImg(im2):
    """将图片进行纵向切割,得到每个字的图片"""
    inletter = False
    foundletter = False
    start = 0   #每个字符的横坐标开始位置
    end = 0     #每个字符的横坐标结束位置

    letters = []

    for x in range(im2.size[0]):
        for y in range(im2.size[1]):
            pix = im2.getpixel((x, y))
            #每列从左往右扫找到第一个不是白色像素的位置
            if pix != 255:
                inletter = True
        #如果找到了不是白色像素的位置,记录每行的x
        if  foundletter == False and inletter == True:
            foundletter = True
            start = x
        #z找到一列都为白色像素的为止,作为字符结尾
        if foundletter == True and inletter == False:
            foundletter = False
            end = x
            letters.append((start, end))

        inletter = False

    count = 0
    imgList = []
    for xStart, xEnd in letters:          #letter里存放每个字符的开始与结束的横坐标
        #crop参数为(Xstart,Ystart,Xend,Yend),然后对图片进行切割
        imgList.append(im2.crop(( xStart, 0, xEnd, im2.size[1])))
    """
    测试代码:
    count = 0
    for xStart, xEnd in letters:
        m = hashlib.md5()
        im3 = im2.crop(( xStart, 0, xEnd, im2.size[1]))
        m.update("%s%s"%(time.time(), count))
        im3.save("./%s.gif"%(m.hexdigest()))
        count += 1
    """
    return imgList

#将图片转换为矢量
def buildvector(im):
    d1 = {}
    count = 0
    for i in im.getdata():
        d1[count] = i
        count += 1
    return d1

def loadSet():
    """加载训练集"""
    #答案
    iconset = ['0','1','2','3','4','5','6','7','8','9','0','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z']
    #加载训练集
    imageset = []
    for letter in iconset:
        for img in os.listdir('./iconset/%s/' % (letter)):
            temp = []
            if img != "Thumbs.db" and img != ".DS_Store":
                temp.append(buildvector(Image.open("./iconset/%s/%s" % (letter,img))))
            imageset.append({letter:temp})

    return imageset

def testImg():
    #创建一个向量类
    v = VectorCompare()
    imageset = loadSet()
    #打开测试图片
    im = Image.open("captcha.gif")
    #将图片转换为8位像素模式,即平常RGB(0, 255)
    im.convert("P") 
    '''
                            测试: 打印图的颜色直方图(每位对应像素出现多少次)
                            print(im.histogram())
                            sort_his()
    '''
    #将图片二值化
    im2 = binaryzation(im)
    #将二值化的图片进行切割,得到单个字符
    imgList = cutImg(im2)

    for img in imgList:
        m = hashlib.md5()
        guess = []
        #遍历列表里的所有字典
        for image in imageset:
            #获取字典的键值,
            for x, y in image.items():
                if len(y) != 0:
                    guess.append( ( v.relation(y[0], buildvector(img)), x) )

        guess.sort(reverse=True)
        print("",guess[0])


if __name__ == '__main__':
    testImg()



你可能感兴趣的:(python破解简单验证码)