利用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读取图片EXIF信息类库介绍和使用实例
Jul 10 Python
python smtplib模块发送SSL/TLS安全邮件实例
Apr 08 Python
Django的信号机制详解
May 05 Python
Python八大常见排序算法定义、实现及时间消耗效率分析
Apr 27 Python
Python爬虫信息输入及页面的切换方法
May 11 Python
Django分页查询并返回jsons数据(中文乱码解决方法)
Aug 02 Python
Python实现的矩阵转置与矩阵相乘运算示例
Mar 26 Python
使用PyCharm进行远程开发和调试的实现
Nov 04 Python
Pytorch 高效使用GPU的操作
Jun 27 Python
Python实现PS滤镜中的USM锐化效果
Dec 04 Python
Python3中的tuple函数知识点讲解
Jan 03 Python
Python的property属性详细讲解
Apr 11 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
Thinkphp中数据按分类嵌套循环实现方法
2014/10/30 PHP
PHP中4种常用的抓取网络数据方法
2015/06/04 PHP
PHP中的switch语句的用法实例详解
2015/10/21 PHP
微信支付开发维权通知实例
2016/07/12 PHP
php7 图形用户界面GUI 开发示例
2020/02/22 PHP
不错的新闻标题颜色效果
2006/12/10 Javascript
json的前台操作和后台操作实现代码
2012/01/20 Javascript
微信JSAPI支付操作需要注意的细节
2017/01/10 Javascript
AngularJS中的路由使用及实现代码
2017/10/09 Javascript
利用jQuery+localStorage实现一个简易的计时器示例代码
2017/12/25 jQuery
简述vue状态管理模式之vuex
2018/08/29 Javascript
js删除对象/数组中null、undefined、空对象及空数组方法示例
2018/11/14 Javascript
Element 默认勾选表格 toggleRowSelection的实现
2019/09/04 Javascript
vue配置nprogress实现页面顶部进度条
2019/09/21 Javascript
vue 使用插槽分发内容操作示例【单个插槽、具名插槽、作用域插槽】
2020/03/06 Javascript
JavaScript实现横版菜单栏
2020/03/17 Javascript
Eclipse + Python 的安装与配置流程
2013/03/05 Python
Python中函数的多种格式和使用实例及小技巧
2015/04/13 Python
Python 字典与字符串的互转实例
2017/01/13 Python
python日期与时间戳的各种转换示例
2020/02/12 Python
Python网页解析器使用实例详解
2020/05/30 Python
HTML5 Canvas 实现K线图的示例代码
2019/12/23 HTML / CSS
怀俄明州飞钓:Platte River Fly Shop
2017/12/28 全球购物
英国手机零售商:Metrofone
2019/03/18 全球购物
如何进行Linux分区优化
2013/02/12 面试题
JSP和EJB可以共享HttpSession么?EJB里面可以改变session里面的内容
2013/06/05 面试题
建筑专业毕业生推荐信
2013/11/21 职场文书
车间班长岗位职责
2013/11/30 职场文书
《北大荒的秋天》教学反思
2014/04/14 职场文书
感恩教育月活动总结
2014/07/07 职场文书
环境保护与污染治理求职信
2014/07/16 职场文书
2016护理专业求职自荐书
2016/01/28 职场文书
2016优秀教师先进个人事迹材料
2016/02/25 职场文书
css3实现背景图片颜色修改的多种方式
2021/04/13 HTML / CSS
Python基础之字符串格式化详解
2021/04/21 Python
Hive HQL支持2种查询语句风格
2022/06/25 数据库