Python 实现验证码生成器!自定义文本 + 多样字体样式做网站的时候,你肯定遇到过这样的场景:用户注册、登录要输验证码,防止机器人乱刷;评论、发帖要验证码,避免垃圾内容。以前我以为做验证码很复杂,得自己画干扰线、调字体,直到发现了 captcha 这个库 —— 一行命令安装,几行代码就能生成自定义验证码,还支持改尺寸、换字体、多格式输出,简直是开发神器!今天就带你来实操,从基础到进阶,把验证码生成器玩明白。
先搞懂:为啥要自己做验证码?用现成的不行吗?可能有人会问:“市面上不是有很多现成的验证码服务吗?为啥还要自己用 Python 写?” 其实有几个场景特别适合自己做:
小项目快速落地:比如你做个个人博客、小型工具站,没必要对接第三方服务,自己写几行代码就能搞定,省得麻烦;自定义需求强:比如想让验证码里带品牌名(比如 “我的网站 2024”)、用特定字体(比如公司 logo 字体),第三方服务不一定支持;学习实用技能:验证码涉及 “图像生成”“参数配置”“Web 集成”,学会了能举一反三,比如后面做图片水印、简单海报生成也能用得上。而 captcha 库就是干这个的 —— 轻量、好上手、功能够用,咱们先从安装开始。
第一步:安装 captcha 库,5 秒钟搞定不管你用 Windows、Mac 还是 Linux,安装步骤都一样:打开终端(Windows 是命令提示符或 PowerShell,Mac/Linux 是终端),输入一行命令:
代码语言:python复制pip install captcha安装时可能遇到的小问题(提前避坑):问题 1:提示 “Permission denied”(权限不够)—— 在命令前面加 sudo(Mac/Linux)或用管理员身份打开终端(Windows);问题 2:下载慢或超时 —— 换国内镜像源,比如用豆瓣源:代码语言:python复制pip install captcha -i https://pypi.doubanio.com/simple/问题 3:提示 “Python 不是内部命令”—— 先检查 Python 是否装好了,或者把 Python 的安装路径添加到系统环境变量里。安装完后,咱们先写个最简单的例子,看看验证码长啥样。
基础用法:3 行代码生成固定文本验证码先从 “生成固定文本的验证码” 入手 —— 比如我想生成一个写着 “PY386” 的验证码,保存成图片文件。代码特别简单,咱们一步步看:
可运行代码(基础版)代码语言:python复制# 1. 从captcha库导入生成图像验证码的类
from captcha.image import ImageCaptcha
# 2. 创建验证码生成器实例(这里先用默认配置,后面再自定义)
image = ImageCaptcha()
# 3. 生成验证码:text是要显示的文本,output是保存的文件名
image.generate_image(text="PY386").save("basic_captcha.png")
print("验证码已保存为 basic_captcha.png!")运行后啥效果?你运行代码后,会在当前文件夹里看到一个叫 basic_captcha.png 的图片,打开后是这样的:
背景是白色或浅灰色,文字是 “PY386”;自动带了干扰线和小噪点(防止机器人识别);默认尺寸是 160x60 像素(宽 x 高)。这 3 行代码里的关键知识点:ImageCaptcha():这是 captcha 库的核心类,所有验证码的配置(尺寸、字体等)都靠它;generate_image(text):生成验证码图像的方法,参数 text 就是你想在验证码里显示的内容(比如字母、数字、汉字,不过汉字要注意字体支持);save("文件名"):把生成的图像保存成文件,支持 png、jpg 等常见格式。核心技能:自定义验证码!改尺寸、换字体、调大小基础版虽然能跑,但实际用的时候肯定要改 —— 比如想让验证码宽一点、用自己喜欢的字体、调大文字大小。这部分是重点,咱们分模块讲,还会用表格整理参数,方便你记。
先看:ImageCaptcha 支持哪些自定义参数?创建 ImageCaptcha 实例时,能传 3 个常用参数,咱们用表格说清楚:
参数名
作用
默认值
取值范围
width
验证码图片的宽度(像素)
160
正整数(比如 200、300)
height
验证码图片的高度(像素)
60
正整数(比如 80、100)
fonts
可选字体列表(路径)
库自带字体
字体文件路径(.ttf/.ttc)
font_sizes
可选字体大小列表(像素)
42, 50, 56
正整数列表(比如 36,48)
简单说:你想改尺寸,就传 width 和 height;想换字体,就传 fonts;想调文字大小,就传 font_sizes。
实战 1:修改验证码尺寸(宽 200,高 80)比如咱们想生成一个更宽更高的验证码,代码这样写:
代码语言:python复制from captcha.image import ImageCaptcha
# 自定义宽度200,高度80
image = ImageCaptcha(width=200, height=80)
# 生成文本为“验证码666”的图片,保存为 big_captcha.png
image.generate_image(text="验证码666").save("big_captcha.png")
print("大尺寸验证码已保存!")运行后打开 big_captcha.png,会发现图片比之前大一圈,文字也跟着适配了尺寸。
实战 2:换字体!用自己系统的字体(重点避坑)很多人改字体时会踩 “字体找不到” 的坑,因为 fonts 参数需要传字体文件的完整路径,而且不同系统的字体路径不一样。咱们分系统教你找字体路径:
第一步:找到自己系统的字体路径Windows 系统:
字体默认存在 C:WindowsFonts 里,比如 “黑体” 是 simhei.ttf,完整路径是 C:WindowsFontssimhei.ttf;Mac 系统:
字体默认存在 /Library/Fonts/ 或 ~/Library/Fonts/,比如 “苹方” 是 PingFang.ttc,完整路径是 /Library/Fonts/PingFang.ttc;Linux 系统:
字体默认存在 /usr/share/fonts/,比如 “文泉驿正黑” 是 wqy-zenhei.ttc,路径是 /usr/share/fonts/wqy-zenhei/wqy-zenhei.ttc。第二步:用自定义字体生成验证码(可运行代码)咱们以 Windows 的 “黑体” 和 Mac 的 “苹方” 为例,写两个版本的代码(你根据自己的系统选一个):
代码语言:python复制from captcha.image import ImageCaptcha
# ------------------- Windows 版本(用黑体)-------------------
# 字体路径:注意路径里的反斜杠要写成两个(),或者用正斜杠(/)
windows_font = "C:WindowsFontssimhei.ttf"
# 创建实例时传 fonts 参数,用列表包起来(支持传多个字体,随机选)
image_windows = ImageCaptcha(fonts=[windows_font])
# 生成验证码,文本用“Python验证码”
image_windows.generate_image(text="Python验证码").save("windows_font_captcha.png")
# ------------------- Mac 版本(用苹方)-------------------
mac_font = "/Library/Fonts/PingFang.ttc"
image_mac = ImageCaptcha(fonts=[mac_font])
image_mac.generate_image(text="Python验证码").save("mac_font_captcha.png")
print("自定义字体验证码已保存!")字体找不到的解决办法:现象:运行报错 FileNotFoundError: [Errno 2] No such file or directory: 'xxx.ttf';原因:路径写错了,或者字体文件不存在;解决:① 打开对应的字体文件夹,复制正确的文件名;② 右键字体文件→“属性”(Windows)或 “显示简介”(Mac),复制完整路径。实战 3:调字体大小!让文字更清晰如果觉得验证码文字太小或太大,可以用 font_sizes 参数自定义。比如咱们想让文字大小在 36 到 48 之间(库会随机选一个大小,增加多样性):
代码语言:python复制from captcha.image import ImageCaptcha
# Windows 字体路径(你换成自己的)
font_path = "C:WindowsFontssimhei.ttf"
# 自定义字体大小:传一个列表,比如 [36, 40, 48]
image = ImageCaptcha(
width=220,
height=80,
fonts=[font_path],
font_sizes=[36, 40, 48] # 文字大小随机选这三个值
)
# 生成10个不同的验证码(测试字体大小变化)
for i in range(10):
text = f"TEST{i+1}" # 文本分别是 TEST1 到 TEST10
image.generate_image(text=text).save(f"font_size_captcha_{i+1}.png")
print("10个不同字体大小的验证码已保存!")运行后会生成 10 个验证码,打开看会发现每个的文字大小不一样,这样能减少机器人识别的概率(固定大小更容易被破解)。
实用功能:多种输出方式!不止保存文件,还能对接 Web实际开发中,验证码很少只保存成文件 —— 比如 Web 项目里,用户访问页面时,服务器要直接返回验证码图片(不是让用户下载文件)。这时候就需要 “字节流” 输出,而 captcha 库刚好支持。
咱们讲两种常用的输出方式:字节流保存到内存(适合本地测试)和 Web 接口返回(适合实际项目)。
方式 1:生成字节流,不用保存文件(用 PIL 显示)字节流就是把图片数据存在内存里,不用写进硬盘,适合临时使用。这里要用到 PIL 库(Python 图像处理库,captcha 已经依赖它了,不用额外安装):
代码语言:python复制from captcha.image import ImageCaptcha
from io import BytesIO # 用来在内存中存储字节流
from PIL import Image # 用来显示图片
# 创建验证码实例
image = ImageCaptcha(width=180, height=60)
# 1. 生成字节流:用 BytesIO 接收
byte_stream = BytesIO()
# 生成图像后,不是 save 到文件,而是 save 到字节流
image.generate_image(text="BYTE123").save(byte_stream, format="PNG")
# 2. 从字节流读取图片并显示
byte_stream.seek(0) # 把指针移到字节流开头(不然读不到数据)
img = Image.open(byte_stream)
img.show() # 打开系统默认图片查看器显示验证码
# 3. 如果需要把字节流转成二进制数据(比如存数据库)
binary_data = byte_stream.getvalue()
print(f"字节流大小:{len(binary_data)} 字节")
# 注意:用完字节流要关闭
byte_stream.close()运行后,会自动弹出图片查看器,显示 “BYTE123” 的验证码,同时终端会打印字节流的大小(比如几千字节)。这种方式适合不想生成一堆文件的场景。
方式 2:Web 集成!用 Flask 写一个验证码接口实际项目中,比如你用 Flask 写网站,用户访问 /captcha 路径就能获取验证码。咱们写个极简的 Flask 示例:
第一步:安装 Flask(如果没装的话)代码语言:python复制pip install flask第二步:Web 接口代码(可运行)代码语言:python复制from flask import Flask, make_response # Flask 核心库
from captcha.image import ImageCaptcha
from io import BytesIO
import random
import string # 用来生成随机字符串
# 1. 初始化 Flask 应用
app = Flask(__name__)
# 2. 生成随机验证码文本的函数(实际用这个,比固定文本安全)
def generate_random_text(length=4):
# 包含大小写字母和数字(排除容易混淆的 O、0、I、1)
chars = string.ascii_letters + string.digits.replace("O", "").replace("0", "").replace("I", "").replace("1", "")
# 随机选 length 个字符
return ''.join(random.choice(chars) for _ in range(length))
# 3. 定义验证码接口:访问 http://127.0.0.1:5000/captcha 就能获取
@app.route('/captcha')
def get_captcha():
# 生成随机文本(4位)
text = generate_random_text()
print(f"当前验证码文本:{text}") # 实际项目中要存到 session 里,用来验证用户输入
# 生成验证码图像字节流
image = ImageCaptcha(width=180, height=60)
byte_stream = BytesIO()
image.generate_image(text=text).save(byte_stream, format="PNG")
# 4. 构造响应:返回图片字节流,设置正确的 Content-Type
byte_stream.seek(0)
response = make_response(byte_stream.getvalue())
response.headers['Content-Type'] = 'image/png' # 关键!告诉浏览器这是图片
return response
# 5. 运行 Flask 应用
if __name__ == '__main__':
app.run(debug=True) # debug=True 方便开发时调试怎么测试这个接口?运行代码后,终端会显示 Running on ``http://127.0.0.1:5000/;打开浏览器,输入 http://127.0.0.1:5000/captcha,就能看到随机生成的验证码;刷新页面,验证码会变(因为文本是随机的),终端会打印当前验证码的文本(比如 “a3F7”)。实际项目的注意点:要把生成的 text 存到用户的 session 里(Flask/Django 都有 session 功能),用户提交输入后,和 session 里的文本对比,判断是否正确;关闭 debug=True(生产环境下打开有安全风险);可以加限流(比如每分钟只能获取 10 次验证码),防止恶意请求。进阶:生成更安全的验证码!增加干扰 + 随机文本实际用的时候,验证码要 “既好认(用户能看清),又难破(机器人识别不了)”。咱们讲两个进阶技巧:生成随机文本(前面 Web 示例里提过,这里再细化)和 理解 captcha 的干扰机制。
技巧 1:生成更安全的随机文本前面的 generate_random_text 函数已经做了优化,这里再升级一下,支持自定义长度和字符类型:
代码语言:python复制import random
import string
def generate_secure_text(
length=4, # 验证码长度,默认4位
include_upper=True, # 包含大写字母
include_lower=True, # 包含小写字母
include_digit=True # 包含数字
):
# 初始化字符池
chars = ""
if include_upper:
chars += string.ascii_uppercase.replace("O", "").replace("I", "") # 排除易混淆字符
if include_lower:
chars += string.ascii_lowercase.replace("o", "").replace("l", "") # 排除 o 和 l(像 0 和 1)
if include_digit:
chars += string.digits.replace("0", "").replace("1", "") # 排除 0 和 1
# 防止没选任何字符类型
if not chars:
raise ValueError("至少要包含一种字符类型(大写、小写、数字)")
# 生成随机文本
return ''.join(random.choice(chars) for _ in range(length))
# 测试:生成6位,只包含大写字母和数字
text1 = generate_secure_text(length=6, include_lower=False)
print("6位大写+数字:", text1) # 比如 "A3F7Z9"
# 测试:生成5位,只包含小写字母
text2 = generate_secure_text(length=5, include_upper=False, include_digit=False)
print("5位小写字母:", text2) # 比如 "abcde"技巧 2:理解 captcha 的干扰机制(不用自己加!)很多人会问:“要不要自己加干扰线、噪点?” 其实 captcha 库已经帮你做好了,生成的验证码默认包含两种干扰:
干扰线:随机的曲线或直线,穿插在文字周围,防止机器人用 “文字分割” 算法提取文字;噪点:随机的小黑点或小白点,破坏文字的完整性,增加识别难度。如果你觉得干扰太少或太多,captcha 库本身不支持直接调整干扰强度,但可以用 PIL 库二次处理(比如自己加几条线)。不过对大部分项目来说,默认的干扰已经够用了,不用画蛇添足。
常见问题(FAQ):遇到问题别慌,这里有解决方案我整理了大家用 captcha 库时最常遇到的 5 个问题,每个都讲清楚 “现象→原因→解决办法”,帮你避坑。
Q1:运行代码报错 “ModuleNotFoundError: No module named 'captcha'”现象:导入 captcha 时提示找不到模块;原因:要么没安装 captcha 库,要么安装了但没在当前 Python 环境里;解决:先检查是否安装:终端输入 pip list | findstr captcha(Windows)或 pip list | grep captcha(Mac/Linux),能看到 captcha 版本就说明装了;如果装了还报错,可能是用了虚拟环境,先激活虚拟环境再运行代码;没装的话,重新执行 pip install captcha。Q2:生成汉字验证码时,文字显示成乱码或方框现象:text 传 “汉字”,生成的验证码里是方框(□)或乱码;原因:用的字体不支持汉字(比如有些英文字体没有汉字编码);解决:换支持汉字的字体,比如 Windows 的 “黑体(simhei.ttf)”、Mac 的 “苹方(PingFang.ttc)”、Linux 的 “文泉驿正黑(wqy-zenhei.ttc)”,参考前面 “换字体” 的步骤。Q3:Web 接口返回的验证码是乱码,不是图片现象:访问 Flask/Django 接口时,页面显示一堆乱码(比如 �PNGrnx1anx00x00x00rIHDRx00x00...);原因:没设置响应头 Content-Type: image/png,浏览器不知道这是图片,就按文本显示了;解决:在响应里加 headers,比如 Flask 里的 response.headers['Content-Type'] = 'image/png'(参考前面的 Web 示例)。Q4:生成的验证码太模糊,用户看不清现象:文字边缘模糊,干扰线太多,用户容易输错;原因:要么尺寸太小(比如 width<150、height<50),要么字体大小太小(比如 font_sizes<36);解决:增大验证码尺寸,比如 width=200, height=80;调大字体大小,比如 font_sizes=[40, 48, 56];换更清晰的字体(比如无衬线字体比衬线字体清晰)。Q5:验证码能被机器人识别,不安全现象:用自动化工具(比如爬虫)能轻松识别验证码,垃圾注册还是很多;原因:验证码太简单(比如固定长度、无干扰、字符太清晰);解决:用随机文本(长度 4-6 位,包含大小写和数字,排除易混淆字符);增大干扰(如果默认干扰不够,用 PIL 自己加几条随机曲线);加时效性(比如验证码 5 分钟内有效,过期失效);进阶:用 “行为验证码”(比如滑动拼图、点选文字),但这种需要更复杂的库(比如 geetest),captcha 只支持图像验证码。面试常考:关于 Python 验证码的 5 个问题如果你面试 Python 开发(尤其是 Web 方向),面试官可能会问验证码相关的问题,这里整理了 5 个高频题,帮你提前准备。
面试题 1:你用 Python 做过验证码吗?用的什么库?为什么选这个库?答:用过,主要用 captcha 库。选它是因为:
轻量,安装简单(一行 pip 命令),不用依赖复杂的图像处理库(它自己依赖 PIL,但 PIL 也很好装);API 直观,几行代码就能生成验证码,支持自定义尺寸、字体、字体大小;支持多种输出方式,既能保存文件,也能生成字节流,方便对接 Web 项目;默认带干扰线和噪点,安全性够用,不用自己写干扰逻辑。如果是特别复杂的需求(比如滑动验证码),会考虑第三方服务(比如极验),但小项目用 captcha 完全够了。
面试题 2:验证码里的干扰线和噪点有什么用?能不能去掉?答:干扰线和噪点的核心作用是 “防止机器识别”—— 比如早期的机器人会用 “图像分割” 算法,把文字从背景里提取出来,再用 OCR(光学字符识别)识别文本。干扰线和噪点能破坏文字的完整性,让算法难以准确分割和识别。
一般不建议去掉,除非是内部系统(比如只有员工用,不需要防机器人),否则去掉后验证码会很容易被破解。如果用户觉得干扰太多看不清,可以调大尺寸和字体大小,而不是去掉干扰。
面试题 3:在 Web 项目里,怎么验证用户输入的验证码是否正确?答:核心是 “服务器端存验证码文本,用户提交后对比”,步骤如下:
服务器生成验证码时,把文本(比如 “a3F7”)存到当前用户的 session 里(因为 session 是用户级别的,每个用户的 session 独立);服务器把验证码图片返回给用户,用户看到后输入文本并提交;服务器接收用户输入的文本,和 session 里存的文本对比(注意大小写是否敏感,一般建议不敏感,比如都转成小写再比);对比成功就允许下一步操作(比如登录、注册),失败就提示 “验证码错误”,并清除 session 里的旧验证码(防止重复使用)。还要注意:验证码要加时效性(比如 5 分钟过期),过期后清除 session;每次获取新验证码时,要更新 session 里的文本。
面试题 4:为什么生成随机验证码时,要排除 O、0、I、1 这些字符?答:因为这些字符视觉上太像了,用户容易混淆,比如:
“O”(大写字母 O)和 “0”(数字 0)几乎一样,用户可能把 “O” 输成 “0”;“I”(大写字母 I)和 “1”(数字 1)、“l”(小写字母 l)也很像,容易输错。排除这些字符能减少用户的输入错误,提升体验,同时不影响安全性(剩下的字符足够多,机器人还是难识别)。
面试题 5:如果 captcha 库满足不了需求(比如要生成带中文的复杂验证码),你会怎么自己实现?答:会用 PIL 库(或 OpenCV)自己写,核心步骤如下:
创建空白图片:用 PIL.Image.new() 生成指定尺寸的图片(比如 RGB 模式,白色背景);准备字体:用 PIL.ImageFont.truetype() 加载支持中文的字体;生成随机文本:用 random 和 string 生成包含汉字、字母、数字的随机文本(汉字需要自己准备一个汉字库列表);绘制文本:用 PIL.ImageDraw.Draw() 在图片上画文本,随机调整每个字符的位置和角度(增加干扰);加干扰:画几条随机曲线(用 draw.arc() 或 draw.line())、加随机噪点(用 random 生成像素点,改变颜色);输出图片:要么保存成文件,要么生成字节流返回给 Web 端。自己实现的好处是灵活,能完全自定义干扰方式和样式,但缺点是代码量多,需要懂一点图像处理的基础知识。
总结:动手试试,你也能做验证码!今天咱们从 “安装 captcha 库” 到 “生成自定义验证码”,再到 “Web 集成” 和 “解决常见问题”,把 Python 验证码生成器的核心用法都讲透了。其实核心就三点:
captcha 库很简单,关键是掌握 ImageCaptcha 的参数(width、height、fonts、font_sizes);输出方式分两种:保存文件(适合本地)和字节流(适合 Web);安全性靠 “随机文本 + 干扰元素”,实用性靠 “合理尺寸 + 清晰字体”。现在你可以动手试试:比如修改代码里的文本、字体、尺寸,生成自己的验证码;或者把 Web 示例里的 Flask 接口,集成到你自己的小项目里。遇到问题别慌,回头看看 “常见问题” 部分,大部分坑都能解决。
如果想进一步提升,可以试试用 Django 集成验证码,或者用 PIL 自己加更复杂的干扰(比如旋转文字、渐变背景)。编程就是这样,多动手多试,慢慢就熟练了!