使用SCF快速部署验证码识别接口
一、本文介绍
验证码识别是搞爬虫
实现自动化脚本避不开的一个问题,
通常验证码识别程序要么部署在本地,
要么部署在服务器端,
如果部署在服务器端就需要
自己去搭建配置网络环境并编写调用接口,
这是一个极其繁琐耗时的过程。
但是现在我们通过无服务器云函数(SCF),
就可以快速将本地验证码识别程序发布上线,
极大地提高了开发效率。
废话少说,上图 ↓
可以看到,识别效果还是蛮好的,
甚至超过了肉眼识别率。
二、操作步骤
传统的验证码识别流程是
图像预处理(灰化,去噪,切割,二值化,去干扰线等)
验证码字符特征提取(SVM,CNN等)
验证码识别
下面我就带大家一起来创建、编写
并发布上线一个验证识别云函数
第一步:新建python云函数
参见我之前的系列文章
《万物皆可Serverless之使用SCF+COS快速开发全栈应用》
第二步:编写验证识别云函数
Life is short, show me the code.
这里我就以一个
最简单的验证码识别程序为例,
直接上代码
import io
import os
import time
from PIL import Image as image
import json
#字符特征
chars = {
'1': [1, 1, 1, 0, 1, ...],
'2': [1, 0, 0, 1, 0, ...],
'3': [0, 1, 0, 0, 1, ...],
# 其他字符特征...
}
# 灰度处理
def covergrey(img):
return img.convert('L')
# 去除验证码边框
def clearedge(img):
for y in range(img.size[1]):
img.putpixel((0, y), 255)
img.putpixel((1, y), 255)
img.putpixel((2, y), 255)
img.putpixel((img.size[0]-1, y), 255)
img.putpixel((img.size[0]-2, y), 255)
img.putpixel((img.size[0]-3, y), 255)
for x in range(img.size[0]):
img.putpixel((x, 0), 255)
img.putpixel((x, 1), 255)
img.putpixel((x, 2), 255)
img.putpixel((x, img.size[1]-1), 255)
img.putpixel((x, img.size[1]-2), 255)
img.putpixel((x, img.size[1]-3), 255)
return img
# 去除干扰线并转换为黑白照片
def clearline(img):
for y in range(img.size[1]):
for x in range(img.size[0]):
if int(img.getpixel((x, y))) >= 110:
img.putpixel((x, y), 0xff)
else:
img.putpixel((x, y), 0x0)
return img
# 去噪/pnum-去噪效率
def del_noise(im, pnum=3):
w, h = im.size
white = 255
black = 0
for i in range(0, w):
im.putpixel((i, 0), white)
im.putpixel((i, h - 1), white)
for i in range(0, h):
im.putpixel((0, i), white)
im.putpixel((w - 1, i), white)
for i in range(1, w - 1):
for j in range(1, h - 1):
val = im.getpixel((i, j))
if val == black:
cnt = 0
for ii in range(-1, 2):
for jj in range(-1, 2):
if im.getpixel((i + ii, j + jj)) == black:
cnt += 1
if cnt < pnum:
im.putpixel((i, j), white)
else:
cnt = 0
for ii in range(-1, 2):
for jj in range(-1, 2):
if im.getpixel((i + ii, j + jj)) == black:
cnt += 1
if cnt >= 7:
im.putpixel((i, j), black)
return im
# 图片数据二值化
def two_value(code_data):
table = []
for i in code_data:
if i < 140: # 二值化分界线140
table.append(0)
else:
table.append(1)
return table
# 图片预处理
def pre_img(img):
img = covergrey(img) # 去色
img = clearedge(img) # 去边
img = clearline(img) # 去线
img = del_noise(img) # 去噪
return img
# 处理图片数据
def data_img(img):
code_data = [] # 验证码数据列表
for i in range(4): # 切割验证码
x = 5 + i * 18 # 可用PS确定图片切割位置
code_data.append(img.crop((x, 9, x + 18, 33)).getdata())
code_data[i] = two_value(code_data[i]) # 二值化数据
return code_data
# 验证码识别
def identify(data):
code = ['']*4 # 验证码字符列表
diff_min = [432]*4 # 初始化最小距离--不符合的数据点个数(共120数据点)
for char in chars: # 遍历验证码字符(每个字符比较一次4个验证码)
diff = [0]*4 # 各验证码差距值(每个字符判断前重置此距离)
for i in range(4): # 计算四个验证码
for j in range(432): # 逐个像素比较验证码特征
if data[i][j] != chars[char][j]:
diff[i] += 1 # 距离+1
for i in range(4):
if diff[i] < diff_min[i]: # 比已有距离还要小(更加符合)
diff_min[i] = diff[i] # 刷新最小距离
code[i] = char # 刷新最佳验证码
return ''.join(code) # 输出结果
def predict(imgs):
code = ''
img = imgs.read()
img = image.open(io.BytesIO(img))
img = pre_img(img) # 预处理图片
data = data_img(img) # 获取图片数据
code = identify(data) # 识别验证码
return code
def apiReply(reply, code=200):
return {
"isBase64Encoded": False,
"statusCode": code,
"headers": {'Content-Type': 'application/json', "Access-Control-Allow-Origin": "*"},
"body": json.dumps(reply, ensure_ascii=False)
}
def main_handler(event, context):
main_start = time.time()
flag = True if 'image' in event['queryString'] else False
code = predict(event['queryString']['image']) if 'image' in event['queryString'] else '无效的请求'
return apiReply({
'ok': flag,
'code': code,
'spendTime': str(time.time()-main_start)
})
老规矩,
先捋一下整个云函数的流程。
def main_handler ( event , context ) :
main_start = time . time ( )
flag = True if 'image' in event [ 'queryString' ] else False
code = predict ( event [ 'queryString' ] [ 'image' ] ) if 'image' in event [ 'queryString' ] else '无效的请求'
return apiReply ( {
'ok' : flag ,
'code' : code ,
'spendTime' : str ( time . time ( ) - main_start )
} )
首先,我们通过event事件
拿到api请求的验证码image数据,
然后判断一下image参数是否存在,
若不存在就返回请求无效的提示
def predict ( imgs ) :
code = ''
img = imgs . read ( )
img = image . open ( io . BytesIO ( img ) )
img = pre_img ( img ) # 预处理图片
data = data_img ( img ) # 获取图片数据
code = identify ( data ) # 识别验证码
return code
如果image请求参数存在
就调用 predict 函数解析识别验证码,
流程如下
读取验证码图像
验证码图像预处理
识别处理后的验证码
# 图片预处理
def pre_img ( img ) :
img = covergrey ( img ) # 去色
img = clearedge ( img ) # 去边
img = clearline ( img ) # 去线
img = del_noise ( img ) # 去噪
return img
我们来看一下图像预处理过程
将验证码去色,转为灰度图
去除验证码黑色边框
去除验证码干扰线
去除验证码噪点
#字符特征
chars = {
'1' : [ 1 , 1 , 1 , 0 , 1 , ... ] ,
'2' : [ 1 , 0 , 0 , 1 , 0 , ... ] ,
'3' : [ 0 , 1 , 0 , 0 , 1 , ... ] ,
# 其他字符特征 ... }
# 验证码识别
def identify ( data ) :
code = [ '' ] * 4 # 验证码字符列表
diff_min = [ 432 ] * 4 # 初始化最小距离 -- 不符合的数据点个数(共 120 数据点)
for char in chars : # 遍历验证码字符(每个字符比较一次 4 个验证码)
diff = [ 0 ] * 4 # 各验证码差距值 ( 每个字符判断前重置此距离 )
for i in range ( 4 ) : # 计算四个验证码
for j in range ( 432 ) : # 逐个像素比较验证码特征
if data [ i ] [ j ] != chars [ char ] [ j ] :
diff [ i ] += 1 # 距离 + 1
for i in range ( 4 ) :
if diff [ i ] < diff_min [ i ] : # 比已有距离还要小(更加符合)
diff_min [ i ] = diff [ i ] # 刷新最小距离
code [ i ] = char # 刷新最佳验证码
return '' . join ( code ) # 输出结果
PS:
文章中的字符特征 chars 并不完整,
你可能需要自行提取所有特征
最后来看一下验证码的识别过程
这里我们直接简单粗暴地
取处理后图像数据的所有像素点
作为字符的特征
所谓大道至简,哈哈哈
然后将每个待识别字符处理后图像的数据
与所有字符的特征逐个比较,
取最相似的那个字符作为识别结果
嗯,没什么问题的话,
你就可以得到正确的识别结果了。
第三步:上线发布云函数、添加Api网关触发器
参见我之前的系列文章
《万物皆可Serverless之使用SCF+COS快速开发全栈应用》
三、文章最后
当然,
以上只是以一个简单的验证码识别为例,
对于一些比较复杂的验证码,
你也可以使用Tensorflow,Pytorch等
深度学习计算框架搭建、训练模型,
然后将训练好的模型借助无服务器云函数
快速上线发布使用。
如果你已经有本地的验证码识别算法,
不妨尝试将它上线发布成一个云函数
方便随时调用。
以上,Have fun.