PIL应用之生成验证码图片
现在的网页中,为了防止机器人提交表单,图片验证码是很常见的应对手段之一。这里就不详细介绍了,相信大家都遇到过。
现在就给出用Python的PIL库实现验证码图片的代码。代码中有详细注释。
#!/usr/bin/env python #coding=utf-8 import random from PIL import Image, ImageDraw, ImageFont, ImageFilter _letter_cases = "abcdefghjkmnpqrstuvwxy" # 小写字母,去除可能干扰的i,l,o,z _upper_cases = _letter_cases.upper() # 大写字母 _numbers = ''.join(map(str, range(3, 10))) # 数字 init_chars = ''.join((_letter_cases, _upper_cases, _numbers)) def create_validate_code(size=(120, 30), chars=init_chars, img_type="GIF", mode="RGB", bg_color=(255, 255, 255), fg_color=(0, 0, 255), font_size=18, font_type="ae_AlArabiya.ttf", length=4, draw_lines=True, n_line=(1, 2), draw_points=True, point_chance = 2): ''' @todo: 生成验证码图片 @param size: 图片的大小,格式(宽,高),默认为(120, 30) @param chars: 允许的字符集合,格式字符串 @param img_type: 图片保存的格式,默认为GIF,可选的为GIF,JPEG,TIFF,PNG @param mode: 图片模式,默认为RGB @param bg_color: 背景颜色,默认为白色 @param fg_color: 前景色,验证码字符颜色,默认为蓝色#0000FF @param font_size: 验证码字体大小 @param font_type: 验证码字体,默认为 ae_AlArabiya.ttf @param length: 验证码字符个数 @param draw_lines: 是否划干扰线 @param n_lines: 干扰线的条数范围,格式元组,默认为(1, 2),只有draw_lines为True时有效 @param draw_points: 是否画干扰点 @param point_chance: 干扰点出现的概率,大小范围[0, 100] @return: [0]: PIL Image实例 @return: [1]: 验证码图片中的字符串 ''' width, height = size # 宽, 高 img = Image.new(mode, size, bg_color) # 创建图形 draw = ImageDraw.Draw(img) # 创建画笔 def get_chars(): '''生成给定长度的字符串,返回列表格式''' return random.sample(chars, length) def create_lines(): '''绘制干扰线''' line_num = random.randint(*n_line) # 干扰线条数 for i in range(line_num): # 起始点 begin = (random.randint(0, size[0]), random.randint(0, size[1])) #结束点 end = (random.randint(0, size[0]), random.randint(0, size[1])) draw.line([begin, end], fill=(0, 0, 0)) def create_points(): '''绘制干扰点''' chance = min(100, max(0, int(point_chance))) # 大小限制在[0, 100] for w in xrange(width): for h in xrange(height): tmp = random.randint(0, 100) if tmp > 100 - chance: draw.point((w, h), fill=(0, 0, 0)) def create_strs(): '''绘制验证码字符''' c_chars = get_chars() strs = ' %s ' % ' '.join(c_chars) # 每个字符前后以空格隔开 font = ImageFont.truetype(font_type, font_size) font_width, font_height = font.getsize(strs) draw.text(((width - font_width) / 3, (height - font_height) / 3), strs, font=font, fill=fg_color) return ''.join(c_chars) if draw_lines: create_lines() if draw_points: create_points() strs = create_strs() # 图形扭曲参数 params = [1 - float(random.randint(1, 2)) / 100, 0, 0, 0, 1 - float(random.randint(1, 10)) / 100, float(random.randint(1, 2)) / 500, 0.001, float(random.randint(1, 2)) / 500 ] img = img.transform(size, Image.PERSPECTIVE, params) # 创建扭曲 img = img.filter(ImageFilter.EDGE_ENHANCE_MORE) # 滤镜,边界加强(阈值更大) return img, strs if __name__ == "__main__": code_img = create_validate_code() code_img.save("validate.gif", "GIF")
最后结果返回一个元组,第一个返回值是Image类的实例,第二个参数是图片中的字符串(比较是否正确的作用)。
需要提醒的是,如果在生成ImageFont.truetype实例的时候抛出IOError异常,有可能是运行代码的电脑没有包含指定的字体,需要下载安装。
生成的验证码图片效果:
这时候,细心的同学可能要问,如果每次生成验证码,都要先保存生成的图片,再显示到页面。这么做让人太不能接受了。这个时候,我们需要使用python内置的StringIO模块,它有着类似file对象的行为,但是它操作的是内存文件。于是,我们可以这么写代码:
try: import cStringIO as StringIO except ImportError: import StringIO mstream = StringIO.StringIO() img = create_validate_code()[0] img.save(mstream, "GIF")
这样,我们需要输出的图片的时候只要使用“mstream.getvalue()”即可。比如在Django里,我们首先定义这样的url:
from django.conf.urls.defaults import * urlpatterns = patterns('example.views', url(r'^validate/$', 'validate', name='validate'), )
在views中,我们把正确的字符串保存在session中,这样当用户提交表单的时候,就可以和session中的正确字符串进行比较。
from django.shortcuts import HttpResponse from validate import create_validate_code def validate(request): mstream = StringIO.StringIO() validate_code = create_validate_code() img = validate_code[0] img.save(mstream, "GIF") request.session['validate'] = validate_code[1] return HttpResponse(mstream.getvalue(), "image/gif")
想请教一下这个验证码的使用问题:就是最后一步那个内存中生成的图片文件怎么样传递到模板当中,显示为一个图片?我想了半天也没想出来。能否指点一下?
呃,我上面不是写了一个URL吗,你直接在页面中写<img src="/validate/" />就可以了呀。
楼主您好,django本身不是有了CSRF机制吗,CSRF是否可以在一定程度上防止垃圾信息提交呢?!还是说CSRF根本就不能防止重复提交这类问题?如果可以,请您回复的同时也通过email提醒我一声,谢谢楼主!
CSRF和垃圾信息防范似乎没有什么联系,不过据说开启CSRF能够阻止一些简单的自动脚本。
开启了CSRF还是会收到很多垃圾消息,如果想要阻止重复提交和spam消息,还是要在业务逻辑中实现吧。这只是我的个人意见。
ps,我的博客是有邮件回复功能的。
哈哈,牛啊,网站做的挺智能的,大赞一个!哦,知道了,看来CSRF还是没有什么用,是吧~还得靠我们的验证码、ip识别限制提交次数等方法。看了你的好文,很实战,总谈到一些开发中真实要用到的功能,期待更多好文了……我想我得订阅你的博客了:)
需要验证码,IP地址判断之类的。
恩,谢谢哈,以后可以多交流:)
嗯呐,好!一定!!
秦,为何你的代码总会用到 url(r'^validate/$', 'validate', name='validate'), 的 name='validate'?? 你用的django是啥版本呀,为何我一用这个name属性就挂掉呢
我的是win7系统测试,应该不会name只能在ubuntu里有效吧~~
当时应该是django1.2吧,报什么错呢?
OK知道了,我的是1.3.1,看来dj可能不再兼容这个name属性了……老秦留意了,呵呵~~
不对呀,我看了我博客代码,是基于1.3的,也还是这么用的
刚刚看了一下官方文档,看这一段,https://docs.djangoproject.com/en/1.4/topics/http/urls/#url
老秦,你整的这个字体 font_type="ae_AlArabiya.ttf", 太罕见了;我报错弄了接近1个小时才发现,原来不是最可疑的IO、机器配置问题(报错说no resource还以为是要装什么插件,还是IO调用不到资源等呢~),而是字体缺少的问题……前人已摔下悬崖,后人请借鉴…呵呵~~~ 老秦找一个通用一点的字体放上去吧,可能你的是Ubuntu系统的字体,我这个老Win就摔下去了……
呵呵,么事,别人看到你的评论就明白啦
我刚学没两天 想问个问题: 现在已经做到生成验证码图片了,但是在后台该怎样判断用户输入的验证码是否正确呢?
我是先 verifycode = request.POST['code'] 获取用户输入的验证码
然后对比 if verifycode == request.session('identify_code'):
但是这样会报错啊
请问该怎么写呢?
PS:生成验证码图片的代码中,已经用
request.session['identify_code'] = rand_str
将验证码放到session里面了
报的错可以看一下吗?
理论上应该是对的。
我的为什么会报:IndexError: list index out of range
具体出错在哪句?详细的出错信息呢?
报的错误
MOD_PYTHON ERROR
ProcessId: 15809
Interpreter: 'localhost'
ServerName: 'localhost'
DocumentRoot: '/home/aldrich/workspace'
URI: '/validate/'
Location: '/'
Directory: None
Filename: '/home/aldrich/workspace/validate'
PathInfo: '/'
Phase: 'PythonHandler'
Handler: 'django.core.handlers.modpython'
Traceback (most recent call last):
File "/usr/lib/python2.7/dist-packages/mod_python/importer.py", line 1537, in HandlerDispatch
default=default_handler, arg=req, silent=hlist.silent)
File "/usr/lib/python2.7/dist-packages/mod_python/importer.py", line 1229, in _process_target
result = _execute_target(config, req, object, arg)
File "/usr/lib/python2.7/dist-packages/mod_python/importer.py", line 1128, in _execute_target
result = object(arg)
File "/usr/local/lib/python2.7/dist-packages/django/core/handlers/modpython.py", line 180, in handler
return ModPythonHandler()(req)
File "/usr/local/lib/python2.7/dist-packages/django/core/handlers/modpython.py", line 158, in __call__
response = self.get_response(request)
File "/usr/local/lib/python2.7/dist-packages/django/core/handlers/base.py", line 179, in get_response
response = self.handle_uncaught_exception(request, resolver, sys.exc_info())
File "/usr/local/lib/python2.7/dist-packages/django/core/handlers/base.py", line 221, in handle_uncaught_exception
return debug.technical_500_response(request, *exc_info)
File "/usr/local/lib/python2.7/dist-packages/django/views/debug.py", line 66, in technical_500_response
html = reporter.get_traceback_html()
File "/usr/local/lib/python2.7/dist-packages/django/views/debug.py", line 286, in get_traceback_html
c = Context(self.get_traceback_data())
File "/usr/local/lib/python2.7/dist-packages/django/views/debug.py", line 244, in get_traceback_data
frames = self.get_traceback_frames()
File "/usr/local/lib/python2.7/dist-packages/django/views/debug.py", line 390, in get_traceback_frames
pre_context_lineno, pre_context, context_line, post_context = self._get_lines_from_file(filename, lineno, 7, loader, module_name)
File "/usr/local/lib/python2.7/dist-packages/django/views/debug.py", line 371, in _get_lines_from_file
context_line = source[lineno].strip('\n')
IndexError: list index out of range
这个……是django报的错。
stackoverflow上有人说到这个:http://stackoverflow.com/questions/3257414/django-in-debug-py-list-index-out-of-range
大意是清一下pyc文件,重启Apache等等。然后再看看报什么错……
非常感谢,重庆apache之后就可以了
你好,现在出现cannot open resource,看到你的帖子说是缺少字体的原因,但是我将改字体放在文件所在目录时还是有这个错误
字体文件直接放到目录里肯定不行的啊。要安装的。
No module named Image
这个是什么原因呢?
有没有安装PIL?
您好,我是在用你的ChineBlog代码试着去运行,但是没有成功,原因是No module named Image
第三方库包都安装好了的
求解答。。谢谢!
现在单纯的运行验证码,可以得到想要的图片,现在使用的并没有什么框架 只是用了mod_python 和Cheetah 实验了好多次 都不能在前台得到该验证码 不知这种情况下 是如何返回验证码图片让前台能够访问 以及显示!
Hi,大神,你好,根据你的代码修改为windows下的字体文件后,运行报这个错误,求解惑
F:\MyPython>yanzhengmatupian.py
Traceback (most recent call last):
File "F:\MyPython\yanzhengmatupian.py", line 110, in <module>
code_img.save("validate.gif", "GIF ")
AttributeError: 'tuple' object has no attribute 'save'
just curious how it works.
File "E:\plugins\PyDev\plugins\org.python.pydev_2.8.2.2013090511\pysrc\pydevd.py", line 1446, in <module>
debugger.run(setup['file'], None, None)
File "E:\plugins\PyDev\plugins\org.python.pydev_2.8.2.2013090511\pysrc\pydevd.py", line 1092, in run
pydev_imports.execfile(file, globals, locals) #execute the script
File "D:\SGM\trunk\Src\cms\base\generateCode.py", line 112, in <module>
code_img = create_validate_code()
File "D:\SGM\trunk\Src\cms\base\generateCode.py", line 93, in create_validate_code
strs = create_strs()
File "D:\SGM\trunk\Src\cms\base\generateCode.py", line 81, in create_strs
font = ImageFont.truetype(font_type, font_size)
File "E:\python27\lib\site-packages\PIL\ImageFont.py", line 218, in truetype
return FreeTypeFont(filename, size, index, encoding)
File "E:\python27\lib\site-packages\PIL\ImageFont.py", line 134, in __init__
self.font = core.getfont(file, size, index, encoding)
File "E:\python27\lib\site-packages\PIL\ImageFont.py", line 34, in __getattr__
raise ImportError("The _imagingft C module is not installed")
ImportError: The _imagingft C module is not installed
PIL的问题,Google一搜很多结果
今天刚在python开发者大会上听了大佬的分享,哈哈,我还是个py初学者,虽然还听不懂,但是真的讲的不错呢,印象深刻,赞赞赞!
给作者留言
关于作者
残阳似血(@秦续业),程序猿一枚,把梦想揣进口袋的挨踢工作者。现加入阿里云,研究僧毕业于上海交通大学软件学院ADC实验室。熟悉分布式数据分析(DataFrame并行化框架)、基于图模型的分布式数据库和并行计算、Dpark/Spark以及Python web开发(Django、tornado)等。
博客分类
搜索
点击排行
标签云
扫描访问
主题
残阳似血的微博
登录
最新评论
yosong 在文章“Django mptt介绍以及使用”下评论
。
yosong 在文章“Django mptt介绍以及使用”下评论
这个多级评论咋做呀,啊啊啊啊啊啊
热搜 在文章“PyOdps 0.4版本发布,从一个故事说起”下评论
文章不错交个朋友
啊啊 在文章“Django mptt介绍以及使用”下评论
console.log('helloworld')
admin22 在文章“Django mptt介绍以及使用”下评论
111111
关于作者
残阳似血(@秦续业),程序猿一枚,把梦想揣进口袋的挨踢工作者。现加入阿里云,研究僧毕业于上海交通大学软件学院ADC实验室。熟悉分布式数据分析(DataFrame并行化框架)、基于图模型的分布式数据库和并行计算、Dpark/Spark以及Python web开发(Django、tornado)等。
所有分类
友情链接
联系信息
地址
上海闵行区东川路800号
上海交通大学
200240
Mail
chinekingseu@gmail.com
QQ
344861256