深度解析:滑动验证码识别
在滑动验证码的应用中,极验和网易易盾是比较有代表性的服务商。验证码的效果如下图所示:极验网易易盾验证码通常会有一个滑动条和一个提示文字「拖动滑块完成拼图」。用户需要按住滑块并将其拖拽至右侧,当验证码左侧的滑块移动至右侧缺口并匹配成功时,验证
在滑动验证码的应用中,极验和网易易盾是比较有代表性的服务商。验证码的效果如下图所示:
极验
网易易盾
验证码通常会有一个滑动条和一个提示文字「拖动滑块完成拼图」。用户需要按住滑块并将其拖拽至右侧,当验证码左侧的滑块移动至右侧缺口并匹配成功时,验证即告通过。验证成功的效果如下图所示:
如果我们希望用爬虫自动化完成这一流程,关键步骤有两个:
识别目标缺口的位置
将滑块移动到目标位置
第二步可以通过模拟工具如 Selenium 完成,但这种方法效率较低。另一种方法是逆向分析验证码的 JavaScript 逻辑,将缺口信息传给 JavaScript 代码以获取“密钥”,并用这些“密钥”进行后续操作。出于安全考虑,本节只介绍如何识别目标缺口位置。
基本原理
我们将介绍利用 OpenCV 进行缺口识别的方法。输入一张带有缺口的验证码图片,输出缺口的位置(通常为缺口左侧的横坐标)。
示例图片
输入的验证码图片如下:
输出的识别结果如下:
实现步骤
利用 OpenCV 进行图像处理的主要步骤包括:
高斯模糊滤波处理,消除部分噪声干扰 更多内容联系1436423940
边缘检测算法识别滑块边缘
对轮廓信息进行筛选,确定缺口位置
准备工作
请确保已安装 python-opencv 库:
bash
pip3 install python-opencv
准备一张滑动验证码图片。样例图片下载地址:验证码图片。
基础知识
高斯滤波
高斯滤波用于去除图像中的噪声,使图像模糊化,为边缘检测做好准备。
OpenCV 提供了 GaussianBlur 方法:
python
def GaussianBlur(src, ksize, sigmaX, dst=None, sigmaY=None, borderType=None)
src:待处理的图像。
ksize:高斯内核大小。
sigmaX:高斯核函数在 X 方向的标准偏差。
示例:
python
image_gaussian_blur = cv2.GaussianBlur(image, (5, 5), 0)
边缘检测
边缘检测常用 Canny 算法,OpenCV 提供了 Canny 方法:
python
def Canny(image, threshold1, threshold2, edges=None, apertureSize=None, L2gradient=None)
image:待处理的图像。
threshold1、threshold2:两个阈值。
示例:
python
image_canny = cv2.Canny(image_gaussian_blur, 200, 450)
轮廓提取
使用 findContours 方法提取轮廓:
python
def findContours(image, mode, method, contours=None, hierarchy=None, offset=None)
image:待处理的图像。
mode:轮廓检索模式。
method:轮廓近似方法。
示例:
python
contours, _ = cv2.findContours(image_canny, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_SIMPLE)
外接矩形
计算轮廓的外接矩形,使用 boundingRect 方法:
python
def boundingRect(array)
示例:
python
x, y, w, h = cv2.boundingRect(contour)
轮廓面积和周长
计算轮廓面积使用 contourArea 方法:
python
def contourArea(contour, oriented=None)
计算轮廓周长使用 arcLength 方法:
python
def arcLength(curve, closed)
缺口识别
定义处理方法
python
import cv2
GAUSSIAN_BLUR_KERNEL_SIZE = (5, 5)
GAUSSIAN_BLUR_SIGMA_X = 0
CANNY_THRESHOLD1 = 200
CANNY_THRESHOLD2 = 450
def get_gaussian_blur_image(image):
return cv2.GaussianBlur(image, GAUSSIAN_BLUR_KERNEL_SIZE, GAUSSIAN_BLUR_SIGMA_X)
def get_canny_image(image):
return cv2.Canny(image, CANNY_THRESHOLD1, CANNY_THRESHOLD2)
def get_contours(image):
contours, _ = cv2.findContours(image, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_SIMPLE)
return contours
处理验证码图片
python
image_raw = cv2.imread('captcha.png')
image_height, image_width, _ = image_raw.shape
image_gaussian_blur = get_gaussian_blur_image(image_raw)
image_canny = get_canny_image(image_gaussian_blur)
contours = get_contours(image_canny)
轮廓筛选条件
python
def get_contour_area_threshold(image_width, image_height):
contour_area_min = (image_width * 0.15) * (image_height * 0.25) * 0.8
contour_area_max = (image_width * 0.15) * (image_height * 0.25) * 1.2
return contour_area_min, contour_area_max
def get_arc_length_threshold(image_width, image_height):
arc_length_min = ((image_width * 0.15) + (image_height * 0.25)) * 2 * 0.8
arc_length_max = ((image_width * 0.15) + (image_height * 0.25)) * 2 * 1.2
return arc_length_min, arc_length_max
def get_offset_threshold(image_width):
offset_min = 0.2 * image_width
offset_max = 0.85 * image_width
return offset_min, offset_max
轮廓筛选和结果标注
python
contour_area_min, contour_area_max = get_contour_area_threshold(image_width, image_height)
arc_length_min, arc_length_max = get_arc_length_threshold(image_width, image_height)
offset_min, offset_max = get_offset_threshold(image_width)
offset = None
for contour in contours:
x, y, w, h = cv2.boundingRect(contour)
if contour_area_min < cv2.contourArea(contour) < contour_area_max and \
arc_length_min < cv2.arcLength(contour, True) < arc_length_max and \
offset_min < x < offset_max:
cv2.rectangle(image_raw, (x, y), (x + w, y + h), (0, 0, 255), 2)
offset = x
cv2.imwrite('image_label.png', image_raw)
print('offset', offset)