在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求两个文本文件以行为单位的交集、并集与差集的方法
Jun 17 Python
利用Python实现微信找房机器人实例教程
Mar 10 Python
python定时检测无响应进程并重启的实例代码
Apr 22 Python
PyQt4实时显示文本内容GUI的示例
Jun 14 Python
Python发展史及网络爬虫
Jun 19 Python
Python Django的安装配置教程图文详解
Jul 17 Python
pytorch自定义初始化权重的方法
Aug 17 Python
Pytorch 抽取vgg各层并进行定制化处理的方法
Aug 20 Python
Anaconda配置pytorch-gpu虚拟环境的图文教程
Apr 16 Python
python中使用input()函数获取用户输入值方式
May 03 Python
java关于string最常出现的面试题整理
Jan 18 Python
Django实现WebSocket在线聊天室功能(channels库)
Sep 25 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
php 连接mssql数据库 初学php笔记
2010/03/01 PHP
PHP析构函数destruct与垃圾回收机制的讲解
2019/03/22 PHP
php设计模式之策略模式应用案例详解
2019/06/17 PHP
基于PHP实现微信小程序客服消息功能
2019/08/12 PHP
使用javascript访问XML数据的实例
2006/12/27 Javascript
在html页面上拖放移动标签
2010/01/08 Javascript
一个简单的全屏图片上下打开显示网页效果示例
2014/07/08 Javascript
原生javascript实现解析XML文档与字符串
2016/03/01 Javascript
详解为Angular.js内置$http服务添加拦截器的方法
2016/12/20 Javascript
js模拟百度模糊搜索的实例
2017/08/04 Javascript
深入理解Node.js中通用基础设计模式
2017/09/19 Javascript
使用jquery模拟a标签的click事件无法实现跳转的解决
2018/12/04 jQuery
Vue动态组件与异步组件实例详解
2019/02/23 Javascript
详解Js里的for…in和for…of的用法
2019/03/28 Javascript
[53:52]EG vs VGJ.T 2018国际邀请赛小组赛BO2 第一场 8.16
2018/08/17 DOTA
详解Python网络爬虫功能的基本写法
2016/01/28 Python
使用Python的PIL模块来进行图片对比
2016/02/18 Python
python批量获取html内body内容的实例
2019/01/02 Python
Python模块的加载讲解
2019/01/15 Python
django与小程序实现登录验证功能的示例代码
2019/02/19 Python
Python匿名函数及应用示例
2019/04/09 Python
python的pytest框架之命令行参数详解(上)
2019/06/27 Python
Python如何把字典写入到CSV文件的方法示例
2020/08/23 Python
如何通过安装HomeBrew来安装Python3
2020/12/23 Python
Farfetch美国:奢侈品牌时尚购物平台
2019/05/02 全球购物
土木工程个人自荐信范文
2013/11/30 职场文书
大学应届生的自我评价
2014/03/06 职场文书
入党积极分子批评与自我批评思想汇报
2014/09/14 职场文书
征用土地赔偿协议书
2014/09/26 职场文书
党的群众路线教育实践活动教师自我剖析材料
2014/10/09 职场文书
销售经理工作失职检讨书
2014/10/24 职场文书
2014年维修电工工作总结
2014/11/20 职场文书
公司庆典欢迎词
2015/01/26 职场文书
毕业典礼主持词
2015/06/29 职场文书
2016年大学迎新晚会工作总结
2015/10/15 职场文书
图文详解Nginx版本平滑升级方案
2021/09/15 Servers