未加星标

那些年,我爬过的北科(八)――反反爬虫之验证码识别

字体大小 | |
[开发(python) 所属分类 开发(python) | 发布者 店小二05 | 时间 2018 | 作者 红领巾 ] 0人收藏点击收藏

本章将要介绍一下如何识别简单的验证码。会涉及到一些图像的概念以及机器学习的知识。

我们本次识别的验证码来自csdn,长相如下:


那些年,我爬过的北科(八)――反反爬虫之验证码识别

在学习之前,我们先安装本章需要的三个库:图像库Pillow、机器学习库Scikit-Learn、科学计算库Numpy。通过pip命令就可以进行安装。

pip install pillow scikit-learn numpy 源码介绍

本章节的案例稍微复杂,见: USTBCrawlers/lesson8


那些年,我爬过的北科(八)――反反爬虫之验证码识别

这里主要有三个部分:下载器、分割器、与识别器。我们可以先把代码clone下来,然后进入到lesson8这个目录下。

图像基本概念

下面我们先来对图像有个基本的介绍。图像是由一个一个像素点构成的,其内部结构是一个二维的矩阵,或者理解成一个二维数组。

例如,一个 M x N 的图像,可以表示成以下的格式:


那些年,我爬过的北科(八)――反反爬虫之验证码识别

图像的坐标和我们平时学的直角坐标并不相同,直角坐标的原点是在左下角;而图像的坐标 起点是在左上角,如下图所示。


那些年,我爬过的北科(八)――反反爬虫之验证码识别

python中,我们可以使用PIL(Pillow)对图像进行操作。如下,我们打开了我们的验证码,并调用 convert("L") 方法把图片转为灰度图像。

from PIL import Image im = Image.open("csdn.png").convert("L") im.show() # 显示图像
那些年,我爬过的北科(八)――反反爬虫之验证码识别

然而我们真正操作并不是图像对象,而是一个矩阵,或者说是二维数组,我们可以把图像转成numpy数组。


那些年,我爬过的北科(八)――反反爬虫之验证码识别

可以看到我们的验证码是20*48的。

图像直方图

图像是由一个个的像素构成的,像素有灰度值,从0-255,一共256个灰度级。直方图的作用是观察每个灰度级所占像素的多少。

可以调用 Image.histogram() 获取Image对象的直方图。


那些年,我爬过的北科(八)――反反爬虫之验证码识别

比如说对于我们的验证码图片,一共有20*48=960个像素点,其中灰度级为94的像素有754个,而灰度级为255的有129个。

再次观察一下我们灰度化的验证码,可以看到验证码的字母是白色的,也就是灰度级为255。周围的背景是灰色的,灰度级为94。


那些年,我爬过的北科(八)――反反爬虫之验证码识别
图像二值化

在处理验证码的时候,背景很多时候并不是同一个灰度级的,为了减少背景对数据的影响。一般都会讲验证码进行二值化。

所谓二值化,其实就是把灰度图像变成只由纯黑、或纯白两种像素组成的图像。

方法很简单,我们可以设定一个阈值,灰度大于100的像素都变成纯白(255),而灰度小于100的像素都变成纯黑(0)。


那些年,我爬过的北科(八)――反反爬虫之验证码识别
验证码字符分割

因为验证码包含四个数字,所以需要把每个字母分割开。笔者为读者准备的验证码是很好分割的类型,只需要对指定区域进行筛选即可。

这里的图片数字的宽度都是8,起点分别位于5、14、23、32。分割代码如下:

def split_and_save(path): path = "../downloader/captchas/" + path pix = np.array(Image.open(path).convert("L")) # threshold image pix = (pix > 100) * 255 col_ranges = [ [5, 5 + 8], [14, 14 + 8], [23, 23 + 8], [32, 32 + 8] ] # split and save for col_range in col_ranges: letter = pix[:, col_range[0]: col_range[1]] im = Image.fromarray(np.uint8(letter)) save_path = "./letters/" + str(uuid.uuid4()) + ".png" im.save(save_path)

我们每个验证码字符的大小为:20*8。

创建数据集

对验证码分割后,我们就会得到一堆字母的图片了。


那些年,我爬过的北科(八)――反反爬虫之验证码识别

但是这些图片都没有标注,下面我们使用的机器学习算法是数据驱动的,所以需要一些已经标注好的验证码数据。我这里标注的方法比较简单,因为毕竟只有0-9十种字母,我就每种数据标注6个。直接通过文件名称进行标识。


那些年,我爬过的北科(八)――反反爬虫之验证码识别
机器学习之KNN算法 算法描述

K近邻算法的定义十分简单,在百度百科上有这样的解释:如果一个样本在特征空间中的k个最相似(即特征空间中最邻近)的样本中的大多数属于某一个类别,则该样本也属于这个类别。

也就是说,需要找到要识别的字母在训练样本中K个最近的字母,然后找出这K个字母中最多的是某个类的?要识别的图片也就是该类的。

算法举例

上面那么描述可能稍微有点儿晦涩,那我们举个例子。

这里以电影分类作为例子,电影题材可分为爱情片,动作片等。这里假定将电影分为爱情片和动作片两类,直观感受的话,如果一部电影中接吻镜头很多,打斗镜头较少,显然是属于爱情片,反之为动作片。

这里我们的数据有两个特征:一个是接吻镜头的数目,一个是打斗镜头的数目。下面我们有一组已知的数据。


那些年,我爬过的北科(八)――反反爬虫之验证码识别

我们这里的目标是利用已知的四个电影数据,预测未知电影的类型。

我们把爱情电影定义为红色的叉子,动作电影定义为绿色的圆圈,未知电影为问号。这里可以画个图直观感受一下。


那些年,我爬过的北科(八)――反反爬虫之验证码识别

假设我们K取3的话,那么从图中可以很清晰的看到,离未知电影最近的三个电影分别是:爱情电影、爱情电影、动作电影。爱情电影占比大,所以我们未知的电影是爱情片。

scikit-learn中使用KNN

我们将使用scikit-learn来实现KNN,所以不需要关注算法的实现(虽然实现也很简单),只要有数据和标签就好了。我们来看看怎么实现上面的预测电影类型的功能。


那些年,我爬过的北科(八)――反反爬虫之验证码识别

这里X是我们已知类型的四部电影,y是四部电影的标签。0代表爱情电影,1代表动作电影。然后调用scikit-learn中的 KNeighborsClassifier 先通过 fit 拟合数据,再调用 predict 预测就好了。

可以看到我们最后预测出未知电影的标签为0,也就是爱情电影,和想法一致。

验证码识别

了解了以上知识后,我们可以编写验证码识别脚本了。

我们这里首先编写加载数据的函数,加载之前标注好的验证码字母数据。我们验证码字母数据是20*8的,也就是相当于有160个特征。

def load_dataset(): X = [] y = [] for i in range(60): path = "./dataset/%d%d.png" % (i / 6, i % 6 + 1) pix = np.array(Image.open(path).convert("L")) X.append(pix.reshape(8*20)) y.append(i/6) return np.array(X), np.array(y)

然后对数据进行拟合:

X, y = load_dataset() knn = KNeighborsClassifier(n_neighbors=5) knn.fit(X, y.astype('uint8'))

最后先分割图片,再使用拟合好数据的knn进行预测。

def split_letters(path): pix = np.array(Image.open(path).convert("L")) # threshold image pix = (pix > 100) * 255 col_ranges = [ [5, 5 + 8], [14, 14 + 8], [23, 23 + 8], [32, 32 + 8] ] letters = [] for col_range in col_ranges: letter = pix[:, col_range[0]: col_range[1]] letters.append(letter.reshape(8*20)) return letters if __name__ == "__main__": if len(sys.argv) != 2: print("Usage: python recognizer.py <image_filename>") letters = split_letters(sys.argv[1]) print(knn.predict(letters))

我们运行一下,可以看到以下识别的结果,都识别出来了。


那些年,我爬过的北科(八)――反反爬虫之验证码识别
内容补充

以上的验证码识别只是一个基本的操作流程。现在只要有足够多的数据,利用深度学习基本上所有的验证码都能识别出来。

深度学习由于需要读者有数学基础以及相关的背景知识,这里笔者就提供一些我自己写过的验证码相关的资料,如果感兴趣可以自己去学习。

如果读者感兴趣可以进行深入学习。

笔者的验证码相关的一个项目:

https://github.com/nladuo/captcha-break 笔者的验证码相关的博客:

本文开发(python)相关术语:python基础教程 python多线程 web开发工程师 软件开发工程师 软件开发流程

代码区博客精选文章
分页:12
转载请注明
本文标题:那些年,我爬过的北科(八)――反反爬虫之验证码识别
本站链接:https://www.codesec.net/view/620971.html


1.凡CodeSecTeam转载的文章,均出自其它媒体或其他官网介绍,目的在于传递更多的信息,并不代表本站赞同其观点和其真实性负责;
2.转载的文章仅代表原创作者观点,与本站无关。其原创性以及文中陈述文字和内容未经本站证实,本站对该文以及其中全部或者部分内容、文字的真实性、完整性、及时性,不作出任何保证或承若;
3.如本站转载稿涉及版权等问题,请作者及时联系本站,我们会及时处理。
登录后可拥有收藏文章、关注作者等权限...
技术大类 技术大类 | 开发(python) | 评论(0) | 阅读(175)