思路
验证码读取一直是我很感兴趣的模块,今天就拿湖南大学的登录验证码练练手!
验证码识别
思路一:使用Python
的pytesseract
库进行验证码识别
对于验证码的识别,Python
库pytesseract
,然而在使用该库前,一定要先安装tesserocr软件(血的教训OVO)
在进行爬虫爬取时,发现系统报错requests.exceptions.SSLError: HTTPSConnectionPool(host='fangkong.hnu.edu.cn', port=443): Max retries exceeded with url: /api/v1/account/getimgvcode (Caused by SSLError("Can't connect to HTTPS URL because the SSL module is not available."))
,经过一番查询后发现需要下载对应自己系统的OpenSSL的EXE安装包,在安装该安装包后问题解决。
首先咱从打卡系统抓一千张验证码图片下来测试一下正确率:
# 引入程序需要的相应包(类似C语言中的# include<stdio.h> 、include<math.h>等)
import os
from bs4 import BeautifulSoup
import time
import requests
# 定义下载函数
def download(n):
## 设置爬取token的网址
url = "https://fangkong.hnu.edu.cn/api/v1/account/getimgvcode"
## 重定义请求头,防止被网页发现是爬虫,从而进行反爬操作
headers = {
"Cookie": "arccount62298=c; arccount62019=c",
"User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.93 Safari/537.36"
}
## 图片名称初始化
num = 0
## 创建文件夹
try:
os.mkdir('./Verification Code')
except:
pass
## 转换工作路径
os.chdir('./Verification Code')
for i in range(n):
## 爬取token模块
html = requests.get(url, headers=headers)# 这里是使用requests函数用之前重定义的请求头读取网页信息
### 测试代码
print(html.text)# <class 'str'>
token=html.text[27:59]
#print(token)
## 得到验证码的链接
urltoken = "https://fangkong.hnu.edu.cn/imagevcode?token="+token
#print(url)
## 下载图片
img = requests.get(urltoken, headers=headers).content
name = str(num) + '.jpg'
with open(name, 'wb') as f:
f.write(img)
num += 1
print("第{}张验证码,链接为{}".format(num,urltoken))
time.sleep(2)
download(1000)
接下来就是使用pytesseract
库进行验证码识别
from PIL import Image
import pytesseract
import os
path = "E:/Pythonworks/自动打卡/Verification Code"
for root,dirs,filename in os.walk(path):
print("根目录:",root)
print("文件夹:",dirs)
print("文件名:",filename)
for path1 in filename:
print(path1)
path2 = "E:/Pythonworks/自动打卡/Verification Code/" + path1
text = pytesseract.image_to_string(Image.open(path2))
print(text)
然而,pytesseract
库的识别正确率有点令人堪忧,这是识别出的一些结果,可以看到有很高的错误率
1.jpg
1861
10.jpg
a3io(识别错误)
100.jpg
未识别出
101.jpg
未识别出
102.jpg
4328
103.jpg
未识别出
104.jpg
277 F-(识别错误)
105.jpg
未识别出
106.jpg
2392.—(识别错误)
107.jpg
2660. —(识别错误)
108.jpg
096 1-—(识别错误)
109.jpg
6615
后续还会补充,未完待续!
--------------------------------------------------------------------------------------------------------------------
1月31日更新:
为了提高验证码的识别率,我们使用将彩色图片转换为灰度图的方法
灰度图:
&&&
from PIL import Image
def main():
image = Image.open('0.jpg')
imgry = image.convert('L')
imgry.save('0_gray.png')
if __name__ == '__main__':
main()
https://heart-of-engine-picture-bed-1.oss-cn-beijing.aliyuncs.com/img/loading1.gif" data-original="
我们先将图片转移到新文件夹内进行识别
from PIL import Image
import pytesseract
import os
## 定义原图像来源
path = "E:/Pythonworks/自动打卡/Verification Code"
## 转换工作路径
os.chdir('./Verification Code_Gray')
## 转灰度图
def main():
for data in os.walk(path):
for path1 in data[2]:# data[2]即为filename列表,详见https://www.cnblogs.com/poloyy/p/12349230.html
num = path1.replace(".jpg", "")# 图片的序号,replace方法详见https://www.runoob.com/python/att-string-replace.html
path2 = "E:/Pythonworks/自动打卡/Verification Code/" + path1
image = Image.open(path2)
imgry = image.convert('L')# 转为灰度图
imgry.save('{}_gray.jpg'.format(num))
path3 = "E:/Pythonworks/自动打卡/Verification Code_Gray/" + num + "_gray.jpg"# 灰度图的绝对路径
text = pytesseract.image_to_string(Image.open(path3))
print(path1)
print(text)
if __name__ == '__main__':
main()
识别效果
1.jpg
1861
10.jpg
232(识别错误)
100.jpg
未识别出
101.jpg
9627
102.jpg
4328
103.jpg
1124
104.jpg
ELT E(识别错误)
105.jpg
f3 77(识别错误)
106.jpg
2322
107.jpg
2660
108.jpg
0961
109.jpg
6615
可以看到,相较直接上手识别,这次的识别率大幅上升,不过我们还不满意,希望能达到更高的识别成功率
那么,我们将灰度图二值化
二值化:
&&&
from PIL import Image
def main():
image = Image.open('0.jpg')
imgry = image.convert('L')
table = get_bin_table()
binary = imgry.point(table, '1')
binary.save('0_binary.png')
def get_bin_table(threshold=115):
table = []
for i in range(256):
if i < threshold:
table.append(0)
else:
table.append(1)
return table
if __name__ == '__main__':
main()
我们先将图片转移到新文件夹进行识别:
from PIL import Image
import pytesseract
import os
## 定义原图像来源
path = "E:/Pythonworks/自动打卡/Verification Code"
## 转换工作路径
os.chdir('./Verification Code_Binary')
## 转灰度图
def main():
for data in os.walk(path):
for path1 in data[2]:# data[2]即为filename列表,详见https://www.cnblogs.com/poloyy/p/12349230.html
num = path1.replace(".jpg", "")# 图片的序号,replace方法详见https://www.runoob.com/python/att-string-replace.html,需要注意replace不会改变原字符串的内容
path2 = "E:/Pythonworks/自动打卡/Verification Code/" + path1
image = Image.open(path2)
imgry = image.convert('L')# 转为灰度图
table = get_bin_table()
binary = imgry.point(table, '1')# 转为黑白图
binary.save('{}_binary.jpg'.format(num))
path4 = "E:/Pythonworks/自动打卡/Verification Code_Binary/" + num + "_binary.jpg"
text = pytesseract.image_to_string(Image.open(path4))
print(path1)
print(text)
def get_bin_table(threshold=115):
table = []
for i in range(256):
if i < threshold:
table.append(0)
else:
table.append(1)
return table
if __name__ == '__main__':
main()
代码运行后却发生了诡异的一幕:
图片&&
识别结果:
1.jpg
未识别出
10.jpg
3:(识别错误)
100.jpg
未识别出
101.jpg
96 7(识别错误)
102.jpg
4323
103.jpg
1124
104.jpg
未识别出
105.jpg
未识别出
106.jpg
未识别出
107.jpg
未识别出
108.jpg
未识别出
109.jpg
未识别出
这是由于我们将图片二值化的思路就是设立一个阈值threshold
,将大于阈值的像素变为白色,小于阈值的像素变为黑色,以此把图片的像素(灰度值)划分为两部分:0和1,例如0代表黑色,1代表白色,然后我们就可以用一串0和1组成的数字来表示一张图片。
然而,我们的阈值设置得太低了,由于大量的像素高于阈值,导致图片中的很多位置变成了白色,我们适当提高阈值。
意外收获
在使用爬虫进行爬取时,发现了一个构建随机请求头的Python库fake-useragent
学业繁忙,本栏目暂时停更
参考链接
如何实现校园疫情防控自动打卡(更新中)
F12查看headers的含义-CSDN
学习Python爬虫(四):模拟浏览器向服务器提交请求-CSDN
conda SSL错误 SSLError(“Can't connect to HTTPS URL because the SSL module is not available.解决办法-CSDN
GitHub 上有哪些比较好的验证码识别库?-知乎
[Python3 网络爬虫开发实战] 1.3.4-tesserocr 的安装
Python爬虫构建随机请求头headers
Python实例:利用pytesseract库进行图片文字识别(二)
使用python PIL库实现简单验证码的去噪
字符型图片验证码识别完整过程及Python实现