利用Python破解验证码实例详解


Posted in Python onDecember 08, 2016

一、前言

本实验将通过一个简单的例子来讲解破解验证码的原理,将学习和实践以下知识点:

      Python基本知识

      PIL模块的使用

二、实例详解

安装 pillow(PIL)库:

$ sudo apt-get update

$ sudo apt-get install python-dev

$ sudo apt-get install libtiff5-dev libjpeg8-dev zlib1g-dev \
libfreetype6-dev liblcms2-dev libwebp-dev tcl8.6-dev tk8.6-dev python-tk

$ sudo pip install pillow

下载实验用的文件:

$ wget http://labfile.oss.aliyuncs.com/courses/364/python_captcha.zip
$ unzip python_captcha.zip
$ cd python_captcha

这是我们实验使用的验证码 captcha.gif

利用Python破解验证码实例详解

提取文本图片

在工作目录下新建 crack.py 文件,进行编辑。

#-*- coding:utf8 -*-
from PIL import Image

im = Image.open("captcha.gif")
#(将图片转换为8位像素模式)
im = im.convert("P")

#打印颜色直方图
print im.histogram()

输出:

[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 , 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 2, 1, 0, 0, 0, 2, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0 , 1, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 0, 0, 1, 2, 0, 1, 0, 0, 1, 0, 2, 0, 0, 1, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 3, 1, 3, 3, 0, 0, 0, 0, 0, 0, 1, 0, 3, 2, 132, 1, 1, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 0, 0, 15, 0 , 1, 0, 1, 0, 0, 8, 1, 0, 0, 0, 0, 1, 6, 0, 2, 0, 0, 0, 0, 18, 1, 1, 1, 1, 1, 2, 365, 115, 0, 1, 0, 0, 0, 135, 186, 0, 0, 1, 0, 0, 0, 116, 3, 0, 0, 0, 0, 0, 21, 1, 1, 0, 0, 0, 2, 10, 2, 0, 0, 0, 0, 2, 10, 0, 0, 0, 0, 1, 0, 625]

颜色直方图的每一位数字都代表了在图片中含有对应位的颜色的像素的数量。

每个像素点可表现256种颜色,你会发现白点是最多(白色序号255的位置,也就是最后一位,可以看到,有625个白色像素)。红像素在序号200左右,我们可以通过排序,得到有用的颜色。

his = im.histogram()
values = {}

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

for j,k in sorted(values.items(),key=lambda x:x[1],reverse = True)[:10]:
 print j,k

输出:

255 625
212 365
220 186
219 135
169 132
227 116
213 115
234 21
205 18
184 15

我们得到了图片中最多的10种颜色,其中 220 与 227 才是我们需要的红色和灰色,可以通过这一讯息构造一种黑白二值图片。

#-*- coding:utf8 -*-
from PIL import Image

im = Image.open("captcha.gif")
im = im.convert("P")
im2 = Image.new("P",im.size,255)


for x in range(im.size[1]):
 for y in range(im.size[0]):
  pix = im.getpixel((y,x))
  if pix == 220 or pix == 227: # these are the numbers to get
   im2.putpixel((y,x),0)

im2.show()

得到的结果:

利用Python破解验证码实例详解

提取单个字符图片

接下来的工作是要得到单个字符的像素集合,由于例子比较简单,我们对其进行纵向切割:

inletter = False
foundletter=False
start = 0
end = 0

letters = []

for y in range(im2.size[0]): 
 for x in range(im2.size[1]):
  pix = im2.getpixel((y,x))
  if pix != 255:
   inletter = True
 if foundletter == False and inletter == True:
  foundletter = True
  start = y

 if foundletter == True and inletter == False:
  foundletter = False
  end = y
  letters.append((start,end))

 inletter=False
print letters

输出:

[(6, 14), (15, 25), (27, 35), (37, 46), (48, 56), (57, 67)]

得到每个字符开始和结束的列序号。

import hashlib
import time

count = 0
for letter in letters:
 m = hashlib.md5()
 im3 = im2.crop(( letter[0] , 0, letter[1],im2.size[1] ))
 m.update("%s%s"%(time.time(),count))
 im3.save("./%s.gif"%(m.hexdigest()))
 count += 1

(接上面的代码)

对图片进行切割,得到每个字符所在的那部分图片。

AI 与向量空间图像识别

在这里我们使用向量空间搜索引擎来做字符识别,它具有很多优点:

  1. 不需要大量的训练迭代
  2. 不会训练过度
  3. 你可以随时加入/移除错误的数据查看效果
  4. 很容易理解和编写成代码
  5. 提供分级结果,你可以查看最接近的多个匹配
  6. 对于无法识别的东西只要加入到搜索引擎中,马上就能识别了。

当然它也有缺点,例如分类的速度比神经网络慢很多,它不能找到自己的方法解决问题等等。

向量空间搜索引擎名字听上去很高大上其实原理很简单。拿文章里的例子来说:

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

用 Python 类实现向量空间:

import math

class VectorCompare:
 #计算矢量大小
 def magnitude(self,concordance):
  total = 0
  for word,count in concordance.iteritems():
   total += count ** 2
  return math.sqrt(total)

 #计算矢量之间的 cos 值
 def relation(self,concordance1, concordance2):
  relevance = 0
  topvalue = 0
  for word, count in concordance1.iteritems():
   if concordance2.has_key(word):
    topvalue += count * concordance2[word]
  return topvalue / (self.magnitude(concordance1) * self.magnitude(concordance2))

它会比较两个 python 字典类型并输出它们的相似度(用 0~1 的数字表示)

将之前的内容放在一起

还有取大量验证码提取单个字符图片作为训练集合的工作,但只要是有好好读上文的同学就一定知道这些工作要怎么做,在这里就略去了。可以直接使用提供的训练集合来进行下面的操作。

iconset目录下放的是我们的训练集。

最后追加的内容:

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

v = VectorCompare()

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})


count = 0
#对验证码图片进行切割
for letter in letters:
 m = hashlib.md5()
 im3 = im2.crop(( letter[0] , 0, letter[1],im2.size[1] ))

 guess = []

 #将切割得到的验证码小片段与每个训练片段进行比较
 for image in imageset:
  for x,y in image.iteritems():
   if len(y) != 0:
    guess.append( ( v.relation(y[0],buildvector(im3)),x) )

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

得到结果

一切准备就绪,运行我们的代码试试:

python crack.py

输出

(0.96376811594202894, '7')
(0.96234028545977002, 's')
(0.9286884286888929, '9')
(0.98350370609844473, 't')
(0.96751165072506273, '9')
(0.96989711688772628, 'j')

是正解,干得漂亮。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流。

Python 相关文章推荐
Python open读写文件实现脚本
Sep 06 Python
Python入门篇之正则表达式
Oct 20 Python
python实现音乐下载器
Apr 15 Python
python使用suds调用webservice接口的方法
Jan 03 Python
新年快乐! python实现绚烂的烟花绽放效果
Jan 30 Python
对Python中class和instance以及self的用法详解
Jun 26 Python
opencv3/C++ 平面对象识别&透视变换方式
Dec 11 Python
python使用openCV遍历文件夹里所有视频文件并保存成图片
Jan 14 Python
Python PyQt5模块实现窗口GUI界面代码实例
May 12 Python
Pycharm修改python路径过程图解
May 22 Python
解决pycharm不能自动保存在远程linux中的问题
Feb 06 Python
python-for x in range的用法(注意要点、细节)
May 10 Python
详解使用python crontab设置linux定时任务
Dec 08 #Python
Python 正则表达式入门(中级篇)
Dec 07 #Python
Python 正则表达式入门(初级篇)
Dec 07 #Python
Python标准库06之子进程 (subprocess包) 详解
Dec 07 #Python
利用 Monkey 命令操作屏幕快速滑动
Dec 07 #Python
Python深入06——python的内存管理详解
Dec 07 #Python
Python制作钉钉加密/解密工具
Dec 07 #Python
You might like
从零开始学YII2框架(四)扩展插件yii2-kartikgii
2014/08/20 PHP
摘自织梦CMS中的图片处理类
2015/08/08 PHP
在TP5数据库中四个字段实现无限分类的示例
2019/10/18 PHP
Jquery常用技巧收集整理篇
2010/11/14 Javascript
JavaScript之编码规范 推荐
2012/05/23 Javascript
javascript游戏开发之《三国志曹操传》零部件开发(三)情景对话中仿打字机输出文字
2013/01/23 Javascript
jquery插件lazyload.js延迟加载图片的使用方法
2014/02/19 Javascript
jQuery UI库中dialog对话框功能使用全解析
2016/04/23 Javascript
AngularJS实现星星等级评分功能
2016/09/24 Javascript
基于 Vue 的树形选择组件的示例代码
2017/08/18 Javascript
Angular中的$watch方法详解
2017/09/18 Javascript
详解vscode中vue代码颜色插件
2018/10/11 Javascript
webpack4实现不同的导出类型
2019/04/09 Javascript
React如何实现浏览器打印部分内容详析
2019/05/19 Javascript
python实现微信自动回复功能
2018/04/11 Python
python 读入多行数据的实例
2018/04/19 Python
python 读取文本文件的行数据,文件.splitlines()的方法
2018/07/12 Python
对python 生成拼接xml报文的示例详解
2018/12/28 Python
python sort、sort_index方法代码实例
2019/03/28 Python
从0开始的Python学习014面向对象编程(推荐)
2019/04/02 Python
python切片(获取一个子列表(数组))详解
2019/08/09 Python
初次部署django+gunicorn+nginx的方法步骤
2019/09/11 Python
Python爬虫爬取煎蛋网图片代码实例
2019/12/16 Python
关于ResNeXt网络的pytorch实现
2020/01/14 Python
Python版中国省市经纬度
2020/02/11 Python
Pycharm安装第三方库失败解决方案
2020/11/17 Python
css3强大的动画效果animate使用说明及浏览器兼容介绍
2013/01/09 HTML / CSS
使用HTML5和CSS3制作一个模态框的示例
2018/03/07 HTML / CSS
Canvas中设置width与height的问题浅析
2018/11/01 HTML / CSS
中国央视网签名寄语
2014/01/18 职场文书
小学教师办公室制度
2014/02/03 职场文书
《会变的花树叶》教学反思
2014/02/10 职场文书
学习优秀共产党员先进事迹思想报告
2014/09/17 职场文书
庆祝教师节标语
2014/10/09 职场文书
python3中apply函数和lambda函数的使用详解
2022/02/28 Python
vue实现Toast组件轻提示
2022/04/10 Vue.js