PIL应用之生成验证码图片
位于分类 我爱Python
现在的网页中,为了防止机器人提交表单,图片验证码是很常见的应对手段之一。这里就不详细介绍了,相信大家都遇到过。
现在就给出用Python的PIL库实现验证码图片的代码。代码中有详细注释。
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110#!/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对象的行为,但是它操作的是内存文件。于是,我们可以这么写代码:
123456789try
:
import
cStringIO as StringIO
except
ImportError:
import
StringIO
mstream
=
StringIO.StringIO()
img
=
create_validate_code()[
0
]
img.save(mstream,
"GIF"
)
这样,我们需要输出的图片的时候只要使用“mstream.getvalue()”即可。比如在Django里,我们首先定义这样的url:
12345from
django.conf.urls.defaults
import
*
urlpatterns
=
patterns(
'example.views'
,
url(r
'^validate/
,
'validate'
, name
=
'validate'
),
)
在views中,我们把正确的字符串保存在session中,这样当用户提交表单的时候,就可以和session中的正确字符串进行比较。
1234567891011121314from
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"
)