Python验证码识别实战:从灰度处理到TesserOCR识别全流程

栗子
栗子
发布时间:2026-10-13 | 4人浏览过
原创

本文详细讲解利用Python进行图像类验证码识别的完整流程。从生成测试验证码入手,依次演示灰度处理、二值化及基于邻域算法的降噪技术,并结合TesserOCR库实现自动识别。文章通过对比不同干扰程度下的识别效果,提供了处理简单至中等复杂度验证码的实用代码与策略。

前言

在开发爬虫程序时,验证码是一个无法回避的技术难点。目前的验证码主要可以分为四类:

  1. 图像类
  2. 滑动类
  3. 点击类
  4. 语音类

本文将重点探讨图像类验证码。这类验证码通常由数字、字母混合组成,部分国内网站甚至使用汉字。为了增加识别难度,生成时往往会加入噪点、干扰线、字符变形、重叠以及不同字体颜色等干扰因素。

针对此类验证码的识别,通常遵循以下处理步骤:

  1. 灰度处理
  2. 增加对比度(可选)
  3. 二值化
  4. 降噪处理
  5. 倾斜校正与字符分割
  6. 建立训练库
  7. 最终识别

出于实验目的,本文使用的验证码均由程序自动生成,而非从真实网站批量抓取。这样做的主要优势在于拥有大量已知准确结果的数据集,便于验证算法效果。

若需在真实环境中获取数据进行训练,可结合各大打码平台来构建数据集。

在生成验证码环节,本文使用Claptcha库(本地下载),Captcha库(本地下载)也是不错的替代选择。

为了生成最基础的纯数字且无干扰的验证码,需要对claptcha.py源码第285行的_drawLine函数进行修改,直接使其返回None,随后即可开始生成验证码:

from claptcha import Claptcha

c = Claptcha("8069","/usr/share/fonts/truetype/freefont/FreeMono.ttf")
t,_ = c.write('1.png')

请注意Ubuntu系统的字体路径配置,也可自行下载其他字体文件使用。生成的验证码效果如下:


从图中可见,验证码存在形变。针对这类最简单的验证码,可以直接使用谷歌开源的tesserocr库进行识别。

首先执行安装命令:

apt-get install tesseract-ocr libtesseract-dev libleptonica-dev
pip install tesserocr

接着进行识别测试:

from PIL import Image
import tesserocr

p1 = Image.open('1.png')
tesserocr.image_to_text(p1)

'8069\n\n'

结果显示,对于此类简单验证码,即使不做任何预处理,识别率也已经相当高。有兴趣的读者可以使用更多数据进行测试,此处不再展开。

接下来,我们在验证码背景中添加噪点,观察效果:

c = Claptcha("8069","/usr/share/fonts/truetype/freefont/FreeMono.ttf",noise=0.4)
t,_ = c.write('2.png')

生成的验证码如下:


识别结果:

p2 = Image.open('2.png')
tesserocr.image_to_text(p2)
'8069\n\n'

效果尚可。随后生成一个包含字母和数字组合的验证码:

c2 = Claptcha("A4oO0zZ2","/usr/share/fonts/truetype/freefont/FreeMono.ttf")
t,_ = c2.write('3.png')

生成的验证码如下:

其中第3个字符是小写字母o,第4个是大写字母O,第5个是数字0,第6个是小写字母z,第7个是大写字母Z,最后一个字符是数字2。这种混淆对人眼极具挑战,但大多数现代验证码对大小写并不做严格区分,我们来看看自动识别的效果:

p3 = Image.open('3.png')
tesserocr.image_to_text(p3)
'AMOOZW\n\n'

既然人眼都难以分辨,计算机识别自然更加困难。然而,对于干扰较小、形变不严重的场景,使用tesserocr依然简单有效。接下来,我们将claptcha.py第285行的_drawLine函数还原,观察添加干扰线的情况。

p4 = Image.open('4.png')
tesserocr.image_to_text(p4)
''

一旦加入干扰线,识别率便完全失效。那么,是否有办法去除这些干扰线呢?

尽管图片看起来是黑白的,但仍需进行灰度处理。否则,使用load()函数获取的是像素点的RGB元组而非单一灰度值。处理代码如下:

def binarizing(img,threshold):
 """传入image对象进行灰度、二值处理"""
 img = img.convert("L") # 转灰度
 pixdata = img.load()
 w, h = img.size
 # 遍历所有像素,大于阈值的为黑色
 for y in range(h):
  for x in range(w):
   if pixdata[x, y] < threshold:
    pixdata[x, y] = 0
   else:
    pixdata[x, y] = 255
 return img

处理后的图片效果如下:

可以看出,处理后图像锐度显著提升。接下来尝试去除干扰线,常用方法包括4邻域和8邻域算法。所谓X邻域算法,可参考手机九宫格输入法:以按键5为待判断像素点,4邻域即判断其上下左右四个方向,8邻域则判断周围8个像素点。若这4或8个点中值为255(白色)的数量超过设定阈值,则判定该点为噪声,阈值可根据实际情况调整。

def depoint(img):
 """传入二值化后的图片进行降噪"""
 pixdata = img.load()
 w,h = img.size
 for y in range(1,h-1):
  for x in range(1,w-1):
   count = 0
   if pixdata[x,y-1] > 245:#上
    count = count + 1
   if pixdata[x,y+1] > 245:#下
    count = count + 1
   if pixdata[x-1,y] > 245:#左
    count = count + 1
   if pixdata[x+1,y] > 245:#右
    count = count + 1
   if pixdata[x-1,y-1] > 245:#左上
    count = count + 1
   if pixdata[x-1,y+1] > 245:#左下
    count = count + 1
   if pixdata[x+1,y-1] > 245:#右上
    count = count + 1
   if pixdata[x+1,y+1] > 245:#右下
    count = count + 1
   if count > 4:
    pixdata[x,y] = 255
 return img

处理后的图片如下:


结果似乎……毫无作用?确实如此,因为示例图片中干扰线的宽度与数字宽度一致。对于干扰线与数据像素宽度不同的情况,例如Captcha生成的验证码:

从左至右依次展示原图、二值化后图像及去除干扰线后的效果,总体降噪效果较为明显。此外,降噪操作可多次执行。例如,我对上述降噪后的结果再次执行一次降噪,可获得如下效果:


再次进行识别,得到结果:

p7 = Image.open('7.png')
tesserocr.image_to_text(p7)
'8069 ,,\n\n'

另外,从图片观察,实际数据颜色与噪点及干扰线颜色差异明显,据此可直接剔除噪点,此处不再展开。 作为系列文章的第一篇,本文主要记录如何将图片进行灰度处理、二值化及降噪,并结合tesserocr识别简单验证码。剩余内容将在下一篇文章中与大家分享。

总结

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对本站的支持。

相关标签:

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表