转载自:http://blog.csdn.net/pwiling/article/details/50596982
引入
基本上大家使用每一种网络服务都会遇到验证码,一般是网站为了防止恶意注册、发帖而设置的验证手段。其生成原理是将一串随机产生的数字或符号,生成一幅图片,图片里加上一些干扰象素(防止OCR)。下面就详细讲解如何生成验证码。
所需环境
除了配置好的python环境外,还需要配有python中的PIL库,这是python中专门用来处理图片的库。用传统的pip install 方法或者下载源码 python setup.py install 方法安装该库,很可能会报错(视运行环境不同)。可以采用下面这个方法
<code class="hljs avrasm has-numbering" style="display: block; padding: 0px; background-color: transparent; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background-position: initial initial; background-repeat: initial initial;"><span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1.</span>下载安装包URL:http://www<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.pythonware</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.com</span>/products/pil/index<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.htm</span>,要下载支持全平台的。 <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">2.</span>解压缩: tar –zxv –f Imaging-<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1.1</span><span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">.7</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.tar</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.gz</span> <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">3.</span>进入到解压后的目录: cd Imaging-<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1.1</span><span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">.7</span> <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">4.</span>Bulid pakage:python setup<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.py</span> build_ext –i <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">5.</span>测试:python selftest<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.py</span> <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">6.</span>安装:python setup<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.py</span> install</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li></ul>
代码实现
要生成验证码图片,我们首先要生成一个随机字符串,包含26个字母和10个数字。
<code class="hljs python has-numbering" style="display: block; padding: 0px; background-color: transparent; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background-position: initial initial; background-repeat: initial initial;"><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">#用来随机生成一个字符串</span> <span class="hljs-function" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">def</span> <span class="hljs-title" style="box-sizing: border-box;">gene_text</span><span class="hljs-params" style="color: rgb(102, 0, 102); box-sizing: border-box;">()</span>:</span> source = list(string.letters) <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">for</span> index <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">in</span> range(<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>,<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">10</span>): source.append(str(index)) <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">''</span>.join(random.sample(source,number))<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">#number是生成验证码的位数</span></code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li></ul>
然后我们要创建一个图片,写入字符串,需要说明的这里面的字体是不同系统而定,如果没有找到系统字体路径的话,也可以不设置
<code class="hljs python has-numbering" style="display: block; padding: 0px; background-color: transparent; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background-position: initial initial; background-repeat: initial initial;"><span class="hljs-function" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">def</span> <span class="hljs-title" style="box-sizing: border-box;">gene_code</span><span class="hljs-params" style="color: rgb(102, 0, 102); box-sizing: border-box;">()</span>:</span> width,height = size <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">#宽和高</span> image = Image.new(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">'RGBA'</span>,(width,height),bgcolor) <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">#创建图片</span> font = ImageFont.truetype(font_path,<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">25</span>) <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">#验证码的字体和字体大小</span> draw = ImageDraw.Draw(image) <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">#创建画笔</span> text = gene_text() <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">#生成字符串</span> font_width, font_height = font.getsize(text) draw.text(((width - font_width) / number, (height - font_height) / number),text,\ font= font,fill=fontcolor) <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">#填充字符串</span></code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li></ul>
接下来,我们要在图片上画几条干扰线
<code class="hljs python has-numbering" style="display: block; padding: 0px; background-color: transparent; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background-position: initial initial; background-repeat: initial initial;"><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">#用来绘制干扰线</span> <span class="hljs-function" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">def</span> <span class="hljs-title" style="box-sizing: border-box;">gene_line</span><span class="hljs-params" style="color: rgb(102, 0, 102); box-sizing: border-box;">(draw,width,height)</span>:</span> begin = (random.randint(<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>, width), random.randint(<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>, height)) end = (random.randint(<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>, width), random.randint(<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>, height)) draw.line([begin, end], fill = linecolor)</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li></ul>
最后创建扭曲,加上滤镜,用来增强验证码的效果。
<code class="hljs mel has-numbering" style="display: block; padding: 0px; background-color: transparent; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background-position: initial initial; background-repeat: initial initial;"> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">image</span> = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">image</span>.transform((width+<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">20</span>,height+<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">10</span>), Image.AFFINE, (<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>,-<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0.3</span>,<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>,-<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0.1</span>,<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>,<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>),Image.BILINEAR) #创建扭曲 <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">image</span> = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">image</span>.<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">filter</span>(ImageFilter.EDGE_ENHANCE_MORE) #滤镜,边界加强 <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">image</span>.save(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">'idencode.png'</span>) #保存验证码图片 </code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li></ul>
下面是用上述程序生成的一个验证码
下面是完整的代码:
<code class="hljs python has-numbering" style="display: block; padding: 0px; background-color: transparent; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background-position: initial initial; background-repeat: initial initial;"><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">#coding=utf-8</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> random <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> string <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> sys <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> math <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">from</span> PIL <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> Image,ImageDraw,ImageFont,ImageFilter <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">#字体的位置,不同版本的系统会有不同</span> font_path = <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">'/Library/Fonts/Arial.ttf'</span> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">#生成几位数的验证码</span> number = <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">4</span> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">#生成验证码图片的高度和宽度</span> size = (<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">100</span>,<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">30</span>) <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">#背景颜色,默认为白色</span> bgcolor = (<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">255</span>,<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">255</span>,<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">255</span>) <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">#字体颜色,默认为蓝色</span> fontcolor = (<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>,<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>,<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">255</span>) <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">#干扰线颜色。默认为红色</span> linecolor = (<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">255</span>,<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>,<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>) <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">#是否要加入干扰线</span> draw_line = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">True</span> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">#加入干扰线条数的上下限</span> line_number = (<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>,<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">5</span>) <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">#用来随机生成一个字符串</span> <span class="hljs-function" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">def</span> <span class="hljs-title" style="box-sizing: border-box;">gene_text</span><span class="hljs-params" style="color: rgb(102, 0, 102); box-sizing: border-box;">()</span>:</span> source = list(string.letters) <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">for</span> index <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">in</span> range(<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>,<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">10</span>): source.append(str(index)) <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">''</span>.join(random.sample(source,number))<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">#number是生成验证码的位数</span> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">#用来绘制干扰线</span> <span class="hljs-function" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">def</span> <span class="hljs-title" style="box-sizing: border-box;">gene_line</span><span class="hljs-params" style="color: rgb(102, 0, 102); box-sizing: border-box;">(draw,width,height)</span>:</span> begin = (random.randint(<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>, width), random.randint(<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>, height)) end = (random.randint(<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>, width), random.randint(<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>, height)) draw.line([begin, end], fill = linecolor) <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">#生成验证码</span> <span class="hljs-function" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">def</span> <span class="hljs-title" style="box-sizing: border-box;">gene_code</span><span class="hljs-params" style="color: rgb(102, 0, 102); box-sizing: border-box;">()</span>:</span> width,height = size <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">#宽和高</span> image = Image.new(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">'RGBA'</span>,(width,height),bgcolor) <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">#创建图片</span> font = ImageFont.truetype(font_path,<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">25</span>) <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">#验证码的字体</span> draw = ImageDraw.Draw(image) <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">#创建画笔</span> text = gene_text() <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">#生成字符串</span> font_width, font_height = font.getsize(text) draw.text(((width - font_width) / number, (height - font_height) / number),text,\ font= font,fill=fontcolor) <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">#填充字符串</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> draw_line: gene_line(draw,width,height) <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"># image = image.transform((width+30,height+10), Image.AFFINE, (1,-0.3,0,-0.1,1,0),Image.BILINEAR) #创建扭曲</span> image = image.transform((width+<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">20</span>,height+<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">10</span>), Image.AFFINE, (<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>,-<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0.3</span>,<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>,-<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0.1</span>,<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>,<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>),Image.BILINEAR) <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">#创建扭曲</span> image = image.filter(ImageFilter.EDGE_ENHANCE_MORE) <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">#滤镜,边界加强</span> image.save(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">'idencode.png'</span>) <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">#保存验证码图片</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> __name__ == <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"__main__"</span>: gene_code()</code>