在Python web中实现验证码图片代码分享


Posted in Python onNovember 09, 2017

系统版本: CentOS 7.4
Python版本: Python 3.6.1

在现在的WEB中,为了防止爬虫类程序提交表单,图片验证码是最常见也是最简单的应对方法之一。

1.验证码图片的生成

  在python中,图片验证码一般用PIL或者Pillow库实现,下面就是利用Pillow生成图片验证码的代码:

#!/usr/bin/env python3

#- * -coding: utf - 8 - * -#@Author: Yang#@ Time: 2017 / 11 / 06 1: 04
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(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 = (230, 230, 230),
    fg_color = (18, 18, 18),
    font_size = 20,
    font_type = ‘/usr/share / fonts / dejavu / DejaVuSans - Bold.ttf',
    length = 4,
    draw_lines = True,
    n_line = (1, 2),
    draw_points = True,
    point_chance = 1):
  ''
'@
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 range(width):
  for h in range(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__':
  img, str = create_validate_code()
img.save('./test.gif', 'gif')

  最后的结果会返回一个元组,第一个返回值为一个Image类的实例,第二个返回值为验证码图片中的字符串,可以用于比对验证码是否正确。

生成的验证码图片效果:

在Python web中实现验证码图片代码分享

 但是需要注意一点,以上代码需要依赖于系统字体,如果 font_type设置不正确,就会抛出 OSError 异常。

在Python web中实现验证码图片代码分享

对于CenOS系统,字体文件一般在 /usr/share/fonts/dejavu/ 下, 如CentOS 7.4:

在Python web中实现验证码图片代码分享

从中随意选取一个即可。windows 下同理,只需将 font_type 设置成正确的字体路径即可, 如

font_type=r"C:\Windows\Fonts\Arial.ttf"

在Python web中实现验证码图片代码分享

2.如何在网页中显示验证码

  在上述代码中,验证码都是以文件的方式保存。如果要在web中使用验证码,不可能每次都先生成验证码图片,先保存到磁盘,再返回给前端 web。这样会增加磁盘的开销,另外频繁产生的验证码也会占用大量的磁盘空间。这时,可以使用 BytesIO 模块,使验证码图片的读写直接在内存中进行,并直接返回给前端。同时将正确验证码字符串存在session中,当用户提交表单时,就可以和session中的正确字符串作比较了。

  以Flask为例,以下为在Flask中使用验证码的完整 Demo:

#!/usr/bin/env python3
# -*- coding: utf-8 -*- 
# @Author : Yang
# @Time  : 2017/11/08 15:35 
import random
from PIL import Image, ImageDraw, ImageFont, ImageFilter
from io import BytesIO
from flask import Flask, session, request
_letter_cases = "abcdefghjkmnpqrstuvwxy" # 小写字母,去除可能干扰的i,l,o,z
_upper_cases = _letter_cases.upper() # 大写字母
_numbers = ''.join(map(str, range(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=(230, 230, 230),
             fg_color=(18, 18, 18),
             font_size=20,
             font_type='/usr/share/fonts/dejavu/DejaVuSans-Bold.ttf',
             length=4,
             draw_lines=True,
             n_line=(1, 2),
             draw_points=True,
             point_chance=1):
  '''
  @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 range(width):
      for h in range(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
app = Flask(__name__)
app.config.update(
  DEBUG=True,
  SECRET_KEY='...'
)
@app.route('/')
def index():
  return 'test'
@app.route('/code')
def get_code():
  # 把strs发给前端,或者在后台使用session保存
  code_img, strs = create_validate_code()
  buf = BytesIO()
  code_img.save(buf, 'jpeg')
  buf_str = buf.getvalue()
  response = app.make_response(buf_str)
  response.headers['Content-Type'] = 'image/gif'
  session['img'] = strs.upper()
  return response
@app.route("/login", methods=["POST", "GET"])
def login():
  if request.method == 'POST':
    if session.get('img') == request.form.get('img').upper():
      return 'OK'
    return 'Error'
  return """
  <form action="" method="post">
    <p>Name:<input type=text name=username>
    <p>Password:<input type=text name=password>
    <p>CAPTCHA:<input type=text name=img>
    <img id="verficode" src="./code" onclick="this.src='./code?'+Math.random()">    # onclick事件用于每次
点击时获取一个新的验证码
    <p><input type=submit value=Login>
  </form>
  """
if __name__ == "__main__":
  app.run(host="0.0.0.0", port=18888, debug=True)

最终效果:

在Python web中实现验证码图片代码分享

总结

以上就是本文关于在Python web中实现验证码图片代码分享的全部内容,希望对大家有所帮助。感兴趣的朋友可以继续参阅本站:python实现人脸识别代码、Python爬虫实例爬取网站搞笑段子、Python入门之三角函数全解【收藏】等,有什么问题可以随时留言,小编会及时回复大家的。感谢朋友们对本站的支持!

Python 相关文章推荐
Python库urllib与urllib2主要区别分析
Jul 13 Python
使用beaker让Facebook的Bottle框架支持session功能
Apr 23 Python
Python使用metaclass实现Singleton模式的方法
May 05 Python
python如何实现内容写在图片上
Mar 23 Python
Selenium 模拟浏览器动态加载页面的实现方法
May 16 Python
Python实现的爬取百度贴吧图片功能完整示例
May 10 Python
Python列表对象实现原理详解
Jul 01 Python
python requests证书问题解决
Sep 05 Python
使用django和vue进行数据交互的方法步骤
Nov 11 Python
python读取ini配置的类封装代码实例
Jan 08 Python
django日志默认打印request请求信息的方法示例
May 17 Python
15个Pythonic的代码示例(值得收藏)
Oct 29 Python
Python模糊查询本地文件夹去除文件后缀的实例(7行代码)
Nov 09 #Python
Python3.6 Schedule模块定时任务(实例讲解)
Nov 09 #Python
Python中scatter函数参数及用法详解
Nov 08 #Python
python实现人脸识别代码
Nov 08 #Python
python生成随机图形验证码详解
Nov 08 #Python
Python爬虫实例爬取网站搞笑段子
Nov 08 #Python
python执行使用shell命令方法分享
Nov 08 #Python
You might like
phpphp图片采集后按原路径保存图片示例
2014/02/18 PHP
PHP中$this和$that指针使用实例
2015/01/06 PHP
php使用wordwrap格式化文本段落的方法
2015/03/17 PHP
PHP实现批量修改文件名的方法示例
2019/09/18 PHP
prototype与jquery下Ajax实现的差别
2009/09/13 Javascript
extJs 常用到的增,删,改,查操作代码
2009/12/28 Javascript
javascript 图片上一张下一张链接效果代码
2010/03/12 Javascript
javascript实现网站加入收藏功能
2015/12/16 Javascript
基于javascript显示当前时间以及倒计时功能
2016/03/18 Javascript
基于BootStrap Metronic开发框架经验小结【八】框架功能总体界面介绍
2016/05/12 Javascript
微信小程序 request接口的封装实例代码
2017/04/26 Javascript
Vue渲染函数详解
2017/09/15 Javascript
vue.js中父组件调用子组件的内部方法示例
2017/10/22 Javascript
vue项目tween方法实现返回顶部的示例代码
2018/03/02 Javascript
解决vue2中使用axios http请求出现的问题
2018/03/05 Javascript
Vue实现点击时间获取时间段查询功能
2020/08/21 Javascript
JavaScript ES6箭头函数使用指南
2018/12/30 Javascript
[01:02:54]完美世界DOTA2联赛PWL S2 FTD vs GXR 第一场 11.22
2020/11/26 DOTA
Python2.x和3.x下maketrans与translate函数使用上的不同
2015/04/13 Python
Python学习笔记之if语句的使用示例
2017/10/23 Python
Python处理文本换行符实例代码
2018/02/03 Python
解决Tensorflow安装成功,但在导入时报错的问题
2018/06/13 Python
centos7之Python3.74安装教程
2019/08/15 Python
Django import export实现数据库导入导出方式
2020/04/03 Python
Python局部变量与全局变量区别原理解析
2020/07/14 Python
call在Python中改进数列的实例讲解
2020/12/09 Python
深入解读CSS3中transform变换模型的渲染
2016/05/27 HTML / CSS
英国DVD和蓝光碟片购买网站:Zoom.co.uk(电影和电视)
2019/09/23 全球购物
团委书记的竞聘演讲稿
2014/04/24 职场文书
房展策划方案
2014/06/07 职场文书
禁止酒驾标语
2014/06/25 职场文书
领导班子作风建设年个人整改措施
2014/09/29 职场文书
判缓刑人员个人思想汇报
2014/10/10 职场文书
小兵张嘎观后感300字
2015/06/03 职场文书
离婚财产分割协议书
2015/08/11 职场文书
Win11 PC上的Outlook搜索错误怎么办?
2022/07/15 数码科技