在开发爬虫的过程中会遇到一种常见的反爬措施,验证码。验证码(CAPTCHA)是“Completely Automated Public Turing test to tell Computers and Humans Apart”(全自动区分计算机和人类的图灵测试)的缩写,是一种区分用户是计算机还是人的公共全自动程序。
验证码自面世以来,不断发展,出现了以下几种验证码
图形验证码:这类验证码大多是计算机随机产生一个字符串,在把字符串增加噪点、干扰线、变形、重叠、不同颜色、扭曲组成一张图片来增加识别难度。
滑动验证码:也叫行为验证码,比较流行的一种验证码,通过用户的操作行为来完成验证,其中最出名的就是极验。
滑动验证码的原理就是使用机器学习中的深度学习技术,根据一些特征来区分是否为正常用户。通过记录用户的滑动速度,还有每一小段时间的瞬时速度,用户鼠标点击情况,以及滑动后的匹配程度来识别。而且,不是说滑动到正确位置就是验证通过,而是根据特征识别来区分是否为真用户,滑到正确位置只是一个必要条件。
点触验证码:点击类验证码都是给出一张包含文字的图片,通过文字提醒用户点击图中相同字的位置进行验证。
今天我们主要讨论如何通过程序处理图像验证码。
官方文档:https://pillow.readthedocs.io/en/latest/installation.html
像素点是最小的图片单元,一张图片由很多像素点构成,一个像素点的颜色是由RGB三个值来表现的,所以一个像素点对应三个颜色向量矩阵,我们对图像的处理就是对这个像素点的操作。
图片的灰度化,就是让像素点矩阵中的每一个像素点满足 R=G=B,此时这个值叫做灰度值,白色为255,黑色为0
灰度转化一般公式为:
R=G=B = 处理前的 RX0.3 + GX0.59 + B*0.11
from PIL import Image
image = Image.open('code.jpg')
im = image.convert('L')
图像的二值化,就是将图像的像素点矩阵中的每个像素点的灰度值设置为0(黑色)或255(白色),从而实现二值化,将整个图像呈现出明显的只有黑和白的视觉效果。
二值化原理是利用设定的一个阈值来判断图像像素是0还是255, 一般小于阈值的像素点变为0, 大于的变成255
这个临界灰度值就被称为阈值,阈值的设置很重要,阈值过大或过小都会对图片造成损坏
选择阈值的原则是:既要尽可能保存图片信息,又要尽可能减少背景和噪声的干扰
常用阈值选择的方法是:
灰度平局值法: 取127 (0~255的中数, (0+255)/2 = 127)
平均值法:
计算像素点矩阵中的所有像素点的灰度值的平均值avg
迭代法:
选择一个近似阈值作为估计值的初始值,然后进行分割图像,根据产生的子图像的特征来选取新的阈值,在利用新的阈值分割图像,经过多次循环,使得错误分割的图像像素点降到最小。
from PIL import Image
img = Image.open('captcha.jpg')
# 增强对比度
img = img.point(lambda x: 1.2*x)
# 获取尺寸
w, h = img.size
# 灰度
img = img.convert('L')
# 获取像素
pixes = img.load()
total = []
for i in range(w):
for j in range(h):
total.append(pixes[i,j])
# 计算平均值
avg = sum(total)//len(total)
img.show()
# 二值化
out = img.point(lambda x: 0 if x< avg else 255)
经过了二值化处理,整个图片像素就被分为了两个值0和255, 如果一个像素点是图片或者干扰因素的一部分,那么她的灰度值一定是0(黑色),如果一个点是背景,其灰度值应该是255,白色
所以对于孤立的噪点,他的周围应该都是白色,或者大多数点都是白色的,所以在判断的时候条件应该放宽,一个点是黑色并且相邻的点为白色的点的个数大于一个固定的值,那么这个点就是噪点。
from PIL import Image
def noise_reduction(img):
w, h = img.size
pixes = img.load()
# 先处理4条边
# 顶边
for i in range(w):
if pixes[i,0] == 0:
if pixes[i, 1] == 255:
pixes[i, 0] = 255
# 底边
for i in range(w):
if pixes[i,h-1] == 0:
if pixes[i, h-2] == 255:
pixes[i, h-1] = 255
# 左边
for i in range(h):
if pixes[0, i] == 0:
if [1, i] == 255:
pixes[0, i] = 255
# 右边
for i in range(h):
if pixes[w-1, i] == 0:
if [w-2, i] == 255:
pixes[w-1, i] = 255
# 处理其他的点
for i in range(1, w-1):
for j in range(1, h-1):
if pixes[i, j] == 0:
sum = pixes[i+1, j] + pixes[i, j+1] + pixes[i-1, j] + pixes[i, j-1] + pixes[i-1, j-1] + pixes[i+1, j-1] + pixes[i+1, j+1] + pixes[i-1, j+1]
if sum // 255 > 4:
pixes[i, j] = 255
return img
字符识别我们使用的是谷歌开源项目,Tesseract OCR
。
github地址:https://github.com/tesseract-ocr/tesseract/wiki
windows
下载地址:https://github.com/UB-Mannheim/tesseract/wiki
ubuntu安装
Python -Tesseract是一种用于Python的光学字符识别(OCR)工具。也就是说,它将识别和“读取”图像中嵌入的文本。Python-tesseract是对谷歌Tesseract OCR
引擎的python封装。它还可用作Tesseract的独立调用脚本,因为它可以读取Pillow和Leptonica图像库支持的所有图像类型,包括jpeg、png、gif、bmp、tiff等。
官方文档:https://github.com/madmaze/pytesseract
pip install pytesseract
try:
from PIL import Image
except ImportError:
import Image
import pytesseract
# If you don't have tesseract executable in your PATH, include the following:
pytesseract.pytesseract.tesseract_cmd = r''
# Example tesseract_cmd = r'C:\Program Files (x86)\Tesseract-OCR\tesseract'
# Simple image to string
print(pytesseract.image_to_string(Image.open('test.png')))
# French text image to string
print(pytesseract.image_to_string(Image.open('test-european.jpg'), lang='fra'))
import pytesseract
from PIL import Image
class Captcha:
def __init__(self, img=None):
if img:
self.open(img)
else:
self.img = img
def open(self, img):
self.img = Image.open(img)
def convert_tow_value(self):
# 增强对比度
self.img = self.img.point(lambda x: 1.2 * x)
# 获取尺寸
w, h = self.img.size
# 灰度
self.img = self.img.convert('L')
# 获取像素
pixes = self.img.load()
total = []
for i in range(w):
for j in range(h):
total.append(pixes[i, j])
# 计算平均值
avg = sum(total) // len(total)
# 二值化
self.img = self.img.point(lambda x: 0 if x < avg - 10 else 255)
def noise_reduction(self):
w, h = self.img.size
pixes = self.img.load()
# 先处理4条边
# 顶边
for i in range(w):
if pixes[i, 0] == 0:
if pixes[i, 1] == 255:
pixes[i, 0] = 255
# 底边
for i in range(w):
if pixes[i, h - 1] == 0:
if pixes[i, h - 2] == 255:
pixes[i, h - 1] = 255
# 左边
for i in range(h):
if pixes[0, i] == 0:
if [1, i] == 255:
pixes[0, i] = 255
# 右边
for i in range(h):
if pixes[w - 1, i] == 0:
if [w - 2, i] == 255:
pixes[w - 1, i] = 255
# 处理其他的点
for i in range(1, w - 1):
for j in range(1, h - 1):
if pixes[i, j] == 0:
sum = pixes[i + 1, j] + pixes[i, j + 1] + pixes[i - 1, j] + pixes[i, j - 1] + pixes[i - 1, j - 1] + \
pixes[i + 1, j - 1] + pixes[i + 1, j + 1] + pixes[i - 1, j + 1]
if sum // 255 > 4:
pixes[i, j] = 255
def image_to_string(self):
self.convert_tow_value()
self.noise_reduction()
pytesseract.pytesseract.tesseract_cmd = r'D:\Program Files\Tesseract-OCR\tesseract.exe'
return pytesseract.image_to_string(self.img)
if __name__ == '__main__':
captcha = Captcha('captcha.jpg')
res = captcha.image_to_string()
print(res)