本文主要使用python + selenium来破解旋转突破的验证码;其中用到numpy和OpenCV(CV2)来进行图片拼接,转换,遮罩,识别等,共分为三个部分:
旋转突破最难的在于如何计算旋转角度,我们不可能用人工智能的方式来自动识别图片摆正的位置,那么我们只能遍历可能出现的图片并保存。通常遍历有两种可能:
图片1的方法很简单,比如51fapiao开出的发票url,当需要下载或浏览发票时,会有一个验证码,这个验证随机出现滑动验证码或者旋转验证码,但检查其出现验证码的html时,发现它保存有所有正确验证码图片的url列表
# 使用正则表达式取出所有的正确图像的url
html = driver.page_source
url_list = re.findall(
"\'(http[^\']+?\d+?\.(?:jpg|png))\'", html, re.S)
# 旋转图片通常是一个正方形,假设宽高分别为w,h(通常w = h)
# 为了识别的方便,需要将所有图像拼接到一起,形成一行图像(或1列图像)
n = len(url_list)
img_all = np.zeros((h, w*n), dtype=np.uint8)
n = 0
for img_url in url_list:
try:
# 下载图像并载入
r = requests.get(img_url)
img_tmp = cv2.imdecode(np.asarray(
bytearray(r.content), dtype=np.uint8), cv2.IMREAD_COLOR)
except:
continue
# 调整大小,并且启用mask,转为灰度图像
img_tmp = cv2.resize(img_tmp, (w, h))
# 将图片用圆形遮罩住(这个稍后讲解)
img_tmp = cv2.add(img_tmp, np.zeros(
img_tmp.shape, dtype=np.uint8), mask=mask)
# 转换为灰度图像
img_tmp = cv2.cvtColor(
np.asarray(img_tmp), cv2.COLOR_BGR2GRAY)
# show(img_tmp)
# 横向拼为大图
img_all[:, n*w:(n+1)*w] = img_tmp[:, :]
n += 1
获取得到的所有图片如下(拼接,缩放为正方形,遮罩并转为灰度)
刷新屏幕,找到验证码图片的那个元素,然后对图片进行处理:
来谈一谈遮罩
验证码通常会有一个背景,然后核心的图像在一个圆形中,这是需要将圆周围的背景图片遮罩住,只比较圆形中心的图片,我们使用OpenCV来进行遮罩的操作
def get_chaptcha_image(driver):
png_data = driver.find_element_by_tag_name(
'canvas').screenshot_as_png
img = cv2.imdecode(np.frombuffer(png_data, dtype=np.uint8),
cv2.IMREAD_COLOR)
h, w = img.shape[:2] # 第一种方法的验证码的高,宽就是从这儿来的
# 制作圆形mask(仅中间圆形部分通过,其它屏蔽)
mask = np.zeros((h, w), dtype=np.uint8)
(centerX, centerY) = (mask.shape[1] // 2, mask.shape[0] // 2)
white = (255, 255, 255)
cv2.circle(mask, (centerX, centerY), w//2-1, white, -1)
# 启用mask并转换图像为灰度
img = cv2.add(img, np.zeros(
img.shape, dtype=np.uint8), mask=mask)
img = cv2.cvtColor(np.asarray(img), cv2.COLOR_BGR2GRAY)
return img
通过上述操作,我们已经找到了一个验证码图片,然后我们进行下列操作,就可以遍历所有的图像
核心代码如下
def add_to_img_all(img_all, img):
'''
将img拼接到img_all中,返回新的img_all
'''
h_all, w_all = img_all.shape[:2]
h, w = img_all.shape[:2]
w_new = w_all + w
img_all_new = np.zeros((h, w_new), dtype=np.uint8)
img_all_new[:,:w_all,:]=img_all[:,:,:]
img_all_new[:,w_all:w_all+w,:] = img[:,:,:]
return img_all_new
def is_existed(img_all, img, semilar = 0.95):
'''
在img_all中寻找img,当相似度到达semilar时停止
参数:
img_all:
img:
semilar:相似度阀值
返回: (maxValue, maxLoc, Angle)
maxValue: 相似度- 0:不相识,1:全相似
maxLoc:最相似的位置(x,y)
Angle:转换到最相似的旋转角度
'''
img = get_chaptcha_image(driver)
img_all = img
h, w = img.shape[:2]
while True:
driver.refresh()
img = get_chaptcha_image(driver)
maxValue, maxLoc, Angle = is_existed(img_all, img
if maxValue>0.9: # 相似度大于0.9认为它已经存在
times +=1
if times>=20:
break
else:
continue
else:
times = 0
img_all = add_to_img_all(img_all, img)