一、推荐 python 图片验证码程序simpel_captcha

    有需求要做一个图片验证码,不想自己造轮子,就到网上找一些代码或者项目,找了一些代码都不大好使用还需要调试,git上的一些项目要么累赘,要么不好用,最后找到了这个simpel_captcha项目,简单轻巧又好用。我这里使用的是FastAPI框架,这套程序也非常方便地结合 FastAPI 或者 starlette 中的StreamingResponse返回图片验证码,分享一下:

GITEE地址:https://toscode.gitee.com/antonizhu/simpel_captcha

    里面就是captcha和img_captcha两相模块,captcha生成文字验证码,接受一个num参数,关系到生成的验证码位数,img_captcha生成图片验证码, 返回数据为元组 (Image| BytesIO, captcha)。

#调用img_captcha生成图片验证码
from fastapi import FastAPI
from fastapi.responses import StreamingResponse
from simpel_captcha import img_captcha
app = FastAPI()

@app.get("/captcha", summary='返回图片验证码')
def image_captcha():
	#img_captcha可以传递width,height,font_size控制图片验证码和大小和字体大小
    image, text = img_captcha(byte_stream=True)
	print(f'图片对象: {image}')
	print(f'验证码: {text}'))
    #将验证码缓存到Redis中 
    return StreamingResponse(content=image, media_type='image/jpeg')

    补充一下,在使用的时候,建议把其源码中的ascii_lowercase和ascii_uppercase字母源里的小写的l、o和大写的O字母去掉了,这两个字线和数字0,1会不好区分。这个源码中是没有删掉的。

二、 webpy框架进行python开发的验证码图片生成显示实现

    项目需要,需要使用python生成图片验证码,python生成图片到硬盘的代码到是很容易get到,但是生成验证码图片后要在浏览器里直接展示可真是费了不少功夫,百度上搜索真没有搜索到webpy直接能用的东西,有看到一些差不多的文章,但用的是Tornado,Django之类框架,拿来没法用,一步步查找试探,终于实现了这个功能。

    我这里使用的webpy框架,下面是URL的规则设置及执行方法的代码

#url设置时uri为code就调用Code类来实现。
urls = (
    '/code', 'Code',
)

web.config.debug = conf.web_debug
app = web.application(urls, globals())

#定义code类并完善GET方法如下
class Code:
    def GET(self):
        web.header('Content-Type', 'image/gif')
        codechar = code.getcode()
        content = code.makeimg(codechar)
        return content

    上面的Code类中会调用code模块里的getcode方法和makeimg方法,Jokerman_Regular.woff.ttf字体文件我就不提供了,可以到网上搜索下载即可,code模块代码如下:

from PIL import Image, ImageDraw, ImageFont
import io
import StringIO

def randomColor():
    r = random.randint(0, 256)
    g = random.randint(0, 256)
    b = random.randint(0, 256)
    return (r, g, b)

def colorDifference(bg_color, text_color):
    d = 0
    for i in range(0, 3):
        d += (text_color[i] - bg_color[i]) ^ 2
    return d

def getcode():
    ans = ""
    for i in range(0, 4):
        ans += random.choice('0123456789')
    return ans

def makeimg(codechar):
    bg_color = randomColor()
    bgImg = Image.new('RGB', (185, 90), bg_color) # 新建一个图片对象, 背景颜色随机
    # bgImg.show()
    canvas = ImageDraw.Draw(bgImg)

    font = ImageFont.truetype(r'libs/Jokerman_Regular.woff.ttf', 60) 
    #font = ImageFont.truetype(r'./Jokerman_Regular.woff.ttf', 60)
    text_color = randomColor()
    while(colorDifference(bg_color, text_color) < 100): 
        text_color = randomColor()

    canvas.text((15, 0), codechar, text_color, font)
    buf = StringIO.StringIO()
    bgImg.save(buf, "gif")
    contents = buf.getvalue()
    buf.close()
    return contents

    一开始各种调试问题,后来是响应状态200正常了,但是图片就是不出来,最后发现有一句代码buf = StringIO.StringIO() 在其它的地方都是这样写的 buf = StringIO() 不知道为什么这里需要写成buf = StringIO.StringIO(),最后是google里这篇文章解决到了这个点

https://stackoverflow.com/questions/14957599/correct-way-to-serve-a-dynamic-image-in-python-web-py-using-pil?rq=1

看看生成的图片验证码效果吧.

fastapi保存登录用户的session fastapi 登录验证_图片验证码

三、Fastapi框架对YmdHis格式的时间日期验证

    Fastapi框架带了不少的验证,除了支持int、str、float、bool数据类型作为参数外,还可以使用其他一些数据类型。比如UUID,Decimal。在时间日期方面支持datetime,有

datetime.datetime:python内置时间类型datetime.datetime,2008-09-15T15:53:00+05:00
datetime.date:python内置日期类型datetime.date,2008-09-15
datetime.time:python内置时间类型datetime.time,14:23:55.003
datetime.timedelta:python内置时间间隔类型datetime.timedelta,秒为单位,显示为float

    但是就是没有一个“2008-09-15 15:53:00”格式。现在我的需求就是在请求接口中某个可重复字典里存在一个时间字段,要求格式就是这个格式,我希望通过fastapi的pydantic模块就把不合格的数据挡在外面,但使用时就发现并没有我期望的验证类来做为判断标准。但也有两种解决办法。

1. 使用Query方法加上正则表达式判断验证,

比如:

time: str = Query(..., regex="^20\d{2}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])\s+(20|21|22|23|[0-1]\d):[0-5]\d:[0-5]\d$")

2. 可以使用datetime.datetime格式

        也可以使用datetime.datetime格式,只是不好的一点是在fastapi的接口文档中RequestBody中的示例将会是下面这种格式,并不完全符合预期。

"time": "2020-12-10T06:05:35.273Z"

    不过上面的时间格式显示不是很好,但是不影响使用,照样可以通过YmdHis格式传递时间值。在程序处理中将得到的time值使用time.strftime("%Y-%m-%d %H:%M:%S")转换一下即可变成YmdHis格式

3. 使用自定义验证

定义一个检验时间格式的Model,然后各个地方都可以使用。

from pydantic import validator,ValidationError

#自定义一个格式
class Ymdhis(BaseModel):
    ymdhis: str
    @validator('ymdhis')
    def check_ymdhis(cls, t):
        try:
			datetime.strptime(t, '%Y-%m-%d %H:%M:%S')
            return t
        raise ValidationError('时间日期格式有误')
		
#调用示例
class User(BaseModel):
	time:Ymdhis

    第3个方法实际是一个很好的方法,因为可以在一个地方设置好,然后全局都能通用,但我并没有试验成功,总是报错:__init__() takes exactly 3 positional arguments (2 given);。未再尝试。我最终还是使用datetime格式吧。