Python用KNN算法实现验证码识别作为一名爬虫爱好者,把互联网作为数据库的同时总会遇到很多坑,其中一个就是验证码,设置验证码一大理由就是为了限制你乱爬,不过对于很多简单的验证码,破解还是相当容易的。比如类似下面这种。最简单的办法是直接用pytesseract库,具体方法请自行搜索。当然如果不想用它,自己来写一个识别算法也并不难。可以用机器学习里面比较基础的KNN算法。先来介绍一下这个KNN算法,他全称叫K最近邻(kNN,k-NearestNeighbor),所谓K最近邻,就是离谁最近,是谁的可能性就更大。什么意思,举个例子就明白了。我现在已经统计了一组手机数据,方便起见,假设只有高端机和低端机两个分类,如下:此时如果有一个新手机T(1500元,15小时),该怎么判断是什么手机呢?我把它们放到同一个坐标系中去比较一下。它并不能很好地跟已知数据完全重合,但不要紧,可以算一下它与其他手机的距离。经过计算,与T的距离D就是这么简单。当然了,这里面只是举个例子所以数据量比较小,当数据量足够大时,比如有200台低端机和300台高端机,选出最接近的200个数据,其中哪种机器多就归到哪种,得出的结论就会比较准确。这就是最简单的KNN算法,离哪个近,就把它归类为哪个种类。这是二维,还可以再加个“像素”属性,变为三维,仍然可以求最近距离。拓展到N个属性也是一样的道理。基本原理就是这样,下面来介绍如何识别验证码,以下代码基于Python3还是这张验证码,我们把它放大一点可以看到,其实他就是一个一个的像素点构成的。里面颜色太多不好操作,首先把这张图变成黑白的fromPILimportImage#引入Image类im=Image.open('图片路径')#打开图片im=im.convert('1')#原来图片是彩色的,这里把图片转换成模式'1',即黑白的im.show()#显示图片于是我们就得到了这么一张图可以把每个点的颜色转变成0和1,并打印出来foriinrange(im.size[1]):temp=[]forjinrange(im.size[0]):pos=(j,i)col=im.getpixel(pos)#获取某坐标的点的颜色,黑色为0,白色为255,为了显示规程,把它转变成1了ifcol==255:col=1temp.append(col)print(temp)可以从打印出来的字符中隐约看到这几个数字然后处理一下这串字符,把不连续的点,即上下左右都没有同色的点去掉im2=imforiinrange(im.size[1]):forjinrange(im.size[0]):#pos=(j,i)#print(pos)ifi==0orj==0ori==im.size[1]-1orj==im.size[0]-1:im2.putpixel((j,i),255)#图片最外面一周都换成白点elifim.getpixel((j,i))!=im.getpixel((j-1,i))andim.getpixel((j,i))!=im.getpixel((j+1,i))andim.getpixel((j,i-1))!=im.getpixel((j,i))andim.getpixel((j,i+1))!=im.getpixel((j,i)):im2.putpixel((j,i),255)#不连续点也都换成白点然后开始切割图片inletter=Falsefoundletter=Falsestart=0end=0letters=[]#用来记录每个数字的起始位置forxinrange(im2.size[0]):foryinrange(im2.size[1]):pix=im2.getpixel((x,y))ifpix!=255:inletter=True#如果不是白色,这说明已经开始接触到数字了iffoundletter==Falseandinletter==True:foundletter=Truestart=x#数字的起始坐标iffoundletter==Trueandinletter==False:foundletter=Falseend=x-1#数字的结束位置letters.append((start,end))inletter=Falseletters的值为[(7,14),(20,27),(33,40),(46,54)],就是每个数字的起始X坐标,同样道理获得每个数字的起始Y坐标,然后切割开来。region=(起始X值,起始Y值,结束X值,结束Y值)#裁切图片im3=im2.crop(region)#im3就是这个区域内的图形,也就是提取的数字#im3.show()于是得出了四个切割好的数字这个时候怎么做呢,我们把第一个数字9用0和1来打印看看。[1,1,1,1,0,1,1,1,1,0,0,0,0,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,0,0,1,1,1,1,0,0,0,1,1,1,0,0,0,0,1,1,1,0,1,1,0,0,0,0,0,0,1,1,0,0,0,1,0,1,1,1,1,1,1,0,1,1,1,1,1,1,0,0,0,1,1,0,0,1,1,0,0,0,0,0,0,1,1,0,0,1,0,1]总大小7*14,总共98个点到这里差不多就出来了,把每个点看成一个属性,问题就转化成了:已知一个数有98个属性,也知道他的种类是9,同样,还有很多很多这样的数,知道属性+种类。然后要做的,就是拿N个样本作为标准集合,有新的数要识别的时候,选出最接近的K个,这K个里面,数量最多的就是这个数的值。