做网站的时候,你肯定遇到过这样的场景:用户注册、登录要输验证码,防止机器人乱刷;评论、发帖要验证码,避免垃圾内容。以前我以为做验证码很复杂,得自己画干扰线、调字体,直到发现了 captcha
这个库 —— 一行命令安装,几行代码就能生成自定义验证码,还支持改尺寸、换字体、多格式输出,简直是开发神器!今天就带你来实操,从基础到进阶,把验证码生成器玩明白。
可能有人会问:“市面上不是有很多现成的验证码服务吗?为啥还要自己用 Python 写?” 其实有几个场景特别适合自己做:
而 captcha
库就是干这个的 —— 轻量、好上手、功能够用,咱们先从安装开始。
不管你用 Windows、Mac 还是 Linux,安装步骤都一样:打开终端(Windows 是命令提示符或 PowerShell,Mac/Linux 是终端),输入一行命令:
pip install captcha
sudo
(Mac/Linux)或用管理员身份打开终端(Windows);pip install captcha -i https://pypi.doubanio.com/simple/
安装完后,咱们先写个最简单的例子,看看验证码长啥样。
先从 “生成固定文本的验证码” 入手 —— 比如我想生成一个写着 “PY386” 的验证码,保存成图片文件。代码特别简单,咱们一步步看:
# 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
的图片,打开后是这样的:
ImageCaptcha()
:这是 captcha 库的核心类,所有验证码的配置(尺寸、字体等)都靠它;generate_image(text)
:生成验证码图像的方法,参数 text
就是你想在验证码里显示的内容(比如字母、数字、汉字,不过汉字要注意字体支持);save("文件名")
:把生成的图像保存成文件,支持 png、jpg 等常见格式。基础版虽然能跑,但实际用的时候肯定要改 —— 比如想让验证码宽一点、用自己喜欢的字体、调大文字大小。这部分是重点,咱们分模块讲,还会用表格整理参数,方便你记。
创建 ImageCaptcha
实例时,能传 3 个常用参数,咱们用表格说清楚:
参数名 | 作用 | 默认值 | 取值范围 |
---|---|---|---|
| 验证码图片的宽度(像素) | 160 | 正整数(比如 200、300) |
| 验证码图片的高度(像素) | 60 | 正整数(比如 80、100) |
| 可选字体列表(路径) | 库自带字体 | 字体文件路径(.ttf/.ttc) |
| 可选字体大小列表(像素) | 42, 50, 56 | 正整数列表(比如 36,48) |
简单说:你想改尺寸,就传 width
和 height
;想换字体,就传 fonts
;想调文字大小,就传 font_sizes
。
比如咱们想生成一个更宽更高的验证码,代码这样写:
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
,会发现图片比之前大一圈,文字也跟着适配了尺寸。
很多人改字体时会踩 “字体找不到” 的坑,因为 fonts
参数需要传字体文件的完整路径,而且不同系统的字体路径不一样。咱们分系统教你找字体路径:
C:WindowsFonts
里,比如 “黑体” 是 simhei.ttf
,完整路径是 C:WindowsFontssimhei.ttf
;/Library/Fonts/
或 ~/Library/Fonts/
,比如 “苹方” 是 PingFang.ttc
,完整路径是 /Library/Fonts/PingFang.ttc
;/usr/share/fonts/
,比如 “文泉驿正黑” 是 wqy-zenhei.ttc
,路径是 /usr/share/fonts/wqy-zenhei/wqy-zenhei.ttc
。咱们以 Windows 的 “黑体” 和 Mac 的 “苹方” 为例,写两个版本的代码(你根据自己的系统选一个):
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'
;如果觉得验证码文字太小或太大,可以用 font_sizes
参数自定义。比如咱们想让文字大小在 36 到 48 之间(库会随机选一个大小,增加多样性):
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 项目里,用户访问页面时,服务器要直接返回验证码图片(不是让用户下载文件)。这时候就需要 “字节流” 输出,而 captcha
库刚好支持。
咱们讲两种常用的输出方式:字节流保存到内存(适合本地测试)和 Web 接口返回(适合实际项目)。
字节流就是把图片数据存在内存里,不用写进硬盘,适合临时使用。这里要用到 PIL
库(Python 图像处理库,captcha
已经依赖它了,不用额外安装):
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” 的验证码,同时终端会打印字节流的大小(比如几千字节)。这种方式适合不想生成一堆文件的场景。
实际项目中,比如你用 Flask 写网站,用户访问 /captcha
路径就能获取验证码。咱们写个极简的 Flask 示例:
pip install flask
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
,就能看到随机生成的验证码;text
存到用户的 session
里(Flask/Django 都有 session 功能),用户提交输入后,和 session 里的文本对比,判断是否正确;debug=True
(生产环境下打开有安全风险);实际用的时候,验证码要 “既好认(用户能看清),又难破(机器人识别不了)”。咱们讲两个进阶技巧:生成随机文本(前面 Web 示例里提过,这里再细化)和 理解 captcha 的干扰机制。
前面的 generate_random_text
函数已经做了优化,这里再升级一下,支持自定义长度和字符类型:
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"
很多人会问:“要不要自己加干扰线、噪点?” 其实 captcha
库已经帮你做好了,生成的验证码默认包含两种干扰:
如果你觉得干扰太少或太多,captcha
库本身不支持直接调整干扰强度,但可以用 PIL
库二次处理(比如自己加几条线)。不过对大部分项目来说,默认的干扰已经够用了,不用画蛇添足。
我整理了大家用 captcha
库时最常遇到的 5 个问题,每个都讲清楚 “现象→原因→解决办法”,帮你避坑。
pip list | findstr captcha
(Windows)或 pip list | grep captcha
(Mac/Linux),能看到 captcha 版本就说明装了;pip install captcha
。�PNGrnx1anx00x00x00rIHDRx00x00...
);Content-Type: image/png
,浏览器不知道这是图片,就按文本显示了;response.headers['Content-Type'] = 'image/png'
(参考前面的 Web 示例)。width=200, height=80
;font_sizes=[40, 48, 56]
;geetest
),captcha
只支持图像验证码。如果你面试 Python 开发(尤其是 Web 方向),面试官可能会问验证码相关的问题,这里整理了 5 个高频题,帮你提前准备。
答:用过,主要用 captcha
库。选它是因为:
如果是特别复杂的需求(比如滑动验证码),会考虑第三方服务(比如极验),但小项目用 captcha
完全够了。
答:干扰线和噪点的核心作用是 “防止机器识别”—— 比如早期的机器人会用 “图像分割” 算法,把文字从背景里提取出来,再用 OCR(光学字符识别)识别文本。干扰线和噪点能破坏文字的完整性,让算法难以准确分割和识别。
一般不建议去掉,除非是内部系统(比如只有员工用,不需要防机器人),否则去掉后验证码会很容易被破解。如果用户觉得干扰太多看不清,可以调大尺寸和字体大小,而不是去掉干扰。
答:核心是 “服务器端存验证码文本,用户提交后对比”,步骤如下:
还要注意:验证码要加时效性(比如 5 分钟过期),过期后清除 session;每次获取新验证码时,要更新 session 里的文本。
答:因为这些字符视觉上太像了,用户容易混淆,比如:
排除这些字符能减少用户的输入错误,提升体验,同时不影响安全性(剩下的字符足够多,机器人还是难识别)。
答:会用 PIL
库(或 OpenCV
)自己写,核心步骤如下:
PIL.Image.new()
生成指定尺寸的图片(比如 RGB 模式,白色背景);PIL.ImageFont.truetype()
加载支持中文的字体;random
和 string
生成包含汉字、字母、数字的随机文本(汉字需要自己准备一个汉字库列表);PIL.ImageDraw.Draw()
在图片上画文本,随机调整每个字符的位置和角度(增加干扰);draw.arc()
或 draw.line()
)、加随机噪点(用 random
生成像素点,改变颜色);自己实现的好处是灵活,能完全自定义干扰方式和样式,但缺点是代码量多,需要懂一点图像处理的基础知识。
今天咱们从 “安装 captcha 库” 到 “生成自定义验证码”,再到 “Web 集成” 和 “解决常见问题”,把 Python 验证码生成器的核心用法都讲透了。其实核心就三点:
captcha
库很简单,关键是掌握 ImageCaptcha
的参数(width、height、fonts、font_sizes);现在你可以动手试试:比如修改代码里的文本、字体、尺寸,生成自己的验证码;或者把 Web 示例里的 Flask 接口,集成到你自己的小项目里。遇到问题别慌,回头看看 “常见问题” 部分,大部分坑都能解决。
如果想进一步提升,可以试试用 Django
集成验证码,或者用 PIL
自己加更复杂的干扰(比如旋转文字、渐变背景)。编程就是这样,多动手多试,慢慢就熟练了!
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。