使用OCR技术识别图形验证码
文章目录
- 使用OCR技术识别图形验证码
-
- [1. OCR技术](#1. OCR技术)
- [2. 准备工作](#2. 准备工作)
-
- [2.1. tesserocr安装异常](#2.1. tesserocr安装异常)
- [3. 验证码图片爬取](#3. 验证码图片爬取)
- [4. 无障碍识别测试](#4. 无障碍识别测试)
- [5. 错误识别](#5. 错误识别)
- [6. 识别实战:](#6. 识别实战:)
- [7. 参数设置](#7. 参数设置)
图形验证码是最早出现的验证方式,现在依然很常见,一般由4位左右的字母或者数字组成。本章节使用的网站时https://captcha7.scrape.center/,这个网站的验证码相对来说比较平整,没有过多的干扰线和干扰点,文字也没有大幅度的变形和旋转,因此比较好作为案例进行分析,对于这类验证码,可以使用OCR技术识别。参考书籍依然是Python3网络爬虫开发实战(第三版)。
1. OCR技术
OCR,即Optical Character Recognition,中文叫做光学字符识别,是指使用电子设备(例如扫描仪和数码相机)检查打印再纸上的字符,通过检查暗、亮的模式确定字符形状,然后使用字符识别方法将形状转化位计算机文字。现在OCR技术已经广泛应用于生产活动中,如文档识别,证件识别,字幕识别,文档搜索等。当然用来识别本节所述的图形验证码也没有问题。
2. 准备工作
再本节的学习中需要导入tesserocr库,这个库的安装需要参考https://setup.scrape.center/tesserocr.另外,还需要安装Selenium、Pillow、Numpy和retrying库用来模拟登录、处理图像和重试操作,可以使用pip3工具安装这些库。 安装好这些库就可以开始了。
2.1. tesserocr安装异常
如果安装异常的话就换一个,可以参照我的,我用的库不是上面的,而是pytesseract,我觉得两者差别不大
- 打开tesseract下载的网页 tesseract,下载最后一个(应该是)tesseract-ocr-w64-setup-v5.3.0.2.221214这个版本,接着就是安装,安装过程中自己记好自己安装在哪里!!!然后就是选择语言包,建议不要全选会下载很慢。
- 将你记下来的安装路径的整个文件地址给添加到环境变量中去。
- 接着python安装pytesseract,找到pytesseract.py文件,打开并找到tesseract_cmd这个变量(大约在30行左右)将里面的值修改为tesseract.exe文件的地址(这个文件在你一开始记下的文件地址里面,查找文件夹就找到了,不用进其他的文件夹,注意转义字符)。
- 搞定上述之后在cmd窗口运行tesseract --list-langs可以看到你下载的语言包。
- 重启,然后运行你的示例代码就行了,如果还不可以,那你去看其他下载教程
3. 验证码图片爬取
这个网页使用JavaScript渲染出来的,所以我们进行爬取的时候使用selenium自动化测试工具。
python
from selenium import webdriver
from selenium.webdriver.common.by import By
from PIL import Image
from io import BytesIO
import time
def demo():
browser = webdriver.Chrome()
browser.get("https://captcha7.scrape.center")
time.sleep(3)
captcha = browser.find_element(By.CSS_SELECTOR,"#captcha")
image = Image.open(BytesIO(captcha.screenshot_as_png))
image.show()
if __name__ == "__main__":
demo()
这里使用了我很少见的BytesIO,这是一个类,它的功能是读取二进制数据流,而图片就是二进制数据流;还有就是captcha.screenshot_as_png这部分的功能就是将当前页面的内容捕获为一张图像,以bytes二进制数据保存;最后调用image的show方法来显式验证码的图像。
4. 无障碍识别测试
首先我们选用两张图片来进行测试,第一张是有换行和明显空格,第二张是一张验证码。
我们运行下面代码:
python
import pytesseract
from PIL import Image
image1 = Image.open("tesseract_tt1.png")
result1 = pytesseract.image_to_string(image1)
image2 = Image.open("tesseract_tt2.png")
result2 = pytesseract.image_to_string(image2)
print(result1, end= '')
print("=========")
print(result2, end= '')
Demons
Lin
Ss ZzTU
=========
2034
我们可以看到在输出SZTU这部分时候出现了SsZz这样大小写都输出的情况,这是因为pytesseract库在识别大小写字母时候很难准确识别出大小写,你可以采取其他办法来执行,这里就不列出来。
5. 错误识别
我选取到了一张图片,如下所示:
python
import pytesseract
from PIL import Image
image = Image.open("error.png")
result = pytesseract.image_to_string(image)
print(result, end= '')
04-8 d.
可以看到这个输出结果明显不是我们想要的,这是因为OCR识别技术是通过检查暗、亮的模式确定字符形状,不是我们想当然的用脑子来看。所以,我们需要做一些额外处理,把干扰信息去掉,我们观察发现,图片里哪些造成干扰的点,其颜色大多比文本的颜色更浅,因此可以通过颜色将干扰点去掉。首先将保存的图片转化为数组,看一下维度:
python
from PIL import Image
import numpy as np
image = Image.open("error.png")
print(np.array(image).shape)
print(image.mode)
(38, 112, 4)
RGBA
从结果上可以看出,这个图片其实是一个三维数组,38和112代表图片的高和宽,4则是每个像素点的表示向量,那为什么是4呢?因为最后一维是一个长度为4的数组分别表示R(红)G(绿)B(蓝)A(透明度),即一个像素点由4个数字表示。那为什么是RGBA而不是RGB或者其他的呢?因为image.mode是RGBA,即由透明通道的真彩色。
mode属性定义了图片的类型和像素的位宽,一共由9种类型:
- 1:像素用1位表示,Python中表示为True或False,即二值化。
- L:像素用8位表示,取值位0-255,表示灰度图像,数字越小,颜色越黑。
- P:像素用8位表示,即调色板数据。
- RGB:像素用3X8位表示,即真彩色。
- RGBA:像素用4X8位标识,即有透明通道的真彩色。
- CMYK:像素用4X8位表示,即印刷四色模式。
- YCbCr:像素用3X8位表示,即彩色视频格式。
- I:像素用32位整型表示。
- F:像素用32位浮点型表示。
为了方便处理,可以把RGBA转化位更简单的L,即把图片转化位灰度图像。往图片对象的convert方法中传入L即可,代码如下表示:
python
image = image.convert('L')
image.show()
我们选择把图片转化位灰度图像,然后根据阈值删除图片上的干扰点,成功识别出验证码,代码如下:
python
from PIL import Image
import numpy as np
image = Image.open("error.png")
image = image.convert('L')
threshold = 90
array = np.array(image)
array = np.where(array> threshold, 255, 0)
image = Image.fromarray((array.astype('uint8')))
# image.show()
result = pytesseract.image_to_string(image)
print(result)
这里先将变量threshold赋值位50.它代表灰度的阈值。接着将图片转化位Numpy数组,利用Numpy的where方法对数组进行筛选和处理,其中将灰度大于阈值的图片的像素设置为255表示白色,否则为0,表示黑色。
6. 识别实战:
python
import time
import re
import pytesseract
from selenium import webdriver
from io import BytesIO
from PIL import Image
from retrying import retry
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import TimeoutException
from selenium.webdriver.common.by import By
import numpy as np
def preprocess(image):
image = image.convert('L')
array = np.array(image)
array = np.where(array > 105, 255, 0)
image = Image.fromarray(array.astype('uint8'))
return image
@retry(stop_max_attempt_number=10, retry_on_result=lambda x: x is False)
def login():
browser.get('https://captcha7.scrape.center/')
browser.find_element(By.CSS_SELECTOR, '.username input[type="text"]').send_keys('admin')
browser.find_element(By.CSS_SELECTOR, '.password input[type="password"]').send_keys('admin')
captcha = browser.find_element(By.CSS_SELECTOR,'#captcha')
image = Image.open(BytesIO(captcha.screenshot_as_png))
image = preprocess(image)
image.show()
captcha = pytesseract.image_to_string(image)
print(captcha)
captcha = re.sub('[^A-Za-z0-9]', '', captcha)
browser.find_element(By.CSS_SELECTOR, '.captcha input[type="text"]').send_keys(captcha)
browser.find_element(By.CSS_SELECTOR, '.login').click()
try:
WebDriverWait(browser, 10).until(EC.presence_of_element_located((By.XPATH, '//h2[contains(., "登录成功")]')))
time.sleep(5)
browser.close()
return True
except TimeoutException:
return False
if __name__ == '__main__':
browser = webdriver.Chrome()
login()
7. 参数设置
在使用 pytesseract
时,你可以使用以下参数:
lang
: 这个参数用于指定 OCR 使用的语言。默认为 'eng',表示英文。如果你的验证码是英文的,那么你可以保持这个默认值。如果验证码是其他语言的,你需要指定相应的语言代码。例如,中文的语言代码是 'chi_sim'。config
: 这个参数用于指定 tesseract 的配置文件。你可以使用它来调整 OCR 的行为。例如,你可以设置 tesseract 只识别数字和大写字母。nice
: 这个参数用于指定 OCR 的质量。值的范围是 0-3,0 表示最快但质量最低,3 表示最慢但质量最高。默认值是 0。如果你的验证码很难识别,你可能需要将这个值设为 3。
这些参数可以在调用 pytesseract.image_to_string
时通过关键字参数的方式指定。例如:
python
captcha = pytesseract.image_to_string(image, lang='chi_sim', config='--psm 10', nice=3)
另外,你也可以使用 pytesseract.image_to_data
函数,它比 image_to_string
更灵活。image_to_data
函数返回一个包含了 OCR 结果的数据结构,你可以从这个数据结构中提取你需要的信息。例如,你可以提取每个单词的置信度,然后只保留置信度高的单词。
还有其他的识别技巧可以学习,这里给出CSDN博客我觉得挺好的一篇: