[验证码识别] 某团图例点选识别

写在前面

最近笔者在抓取大众点评数据时,遇到了各种各样的验证码,如:滑块,滑块拼图,图片,图例验证码,不知道还有没有其他的验证码(心累)。但总体美团的验证码识别起来时还是比较简单的,没有那种花里胡哨的验证,就是服务器检验比较严格。

数据特点

某团的点选和其他点选差不多,按顺序点击。同时背景图的图例和图例图的图例方向和大小是一样的。

 

获取背景图和图例图

这里获取背景图有两种方式:

  1. 通过api 获取背景和图例。
  2. 通过自动化工具获取背景和图例。

方式1 获取的背景图会发现图片经过切割的,并不是完整的图片。

 

因此,我们选择方式2,通过selemuim 获取背景和图例,同时这里的背景和图例都是canvas 绘制直接找图片url 肯定不行,我们可以用 js 获取图片的 base64,再将 base 64 转为图片。  

r = brower.execute_script('return document.getElementsByClassName("img-answer")["0"].src')  # 图例图 base64
k = brower.execute_script('return document.getElementsByClassName("yoda-inference-question")[0].toDataURL()') # 背景图 base64

测试图片

识别思路

一个验证码是由两张图组成的,一个的背景图,一个是图标图。 首先在识别之前,看看要解决哪些问题。

  • 从图标图中按顺序(按顺序点击的依据)抠出4个图标并处理图例图片
  • 将扣出来的图例图和背景图中的图例配对

按顺序抠出图例

这里我们使用OpenCV模块扣取图例,因为这里的图例比较简单背景也没有特别的颜色,所以我们简单处理一下就行

image = cv2.imread(fg)  # fg 是图例图
gray_img = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)  # 灰度图
_, threshold_img = cv2.threshold(gray_img, 0, 255, cv2.THRESH_OTSU)  # 二值化
kernel = np.ones([3, 3], np.uint8)
dialte_img = cv2.dilate(threshold_img, kernel, 2) # 图例膨胀
dst = cv2.bitwise_not(dialte_img)  # 像素取反

结果就是这样

 有了上面的结果后,按顺序抠图就简单了。从左到右遍历下康康纵向的像素和是不是0就ok了

roi_image = []

i = 0
while i < image.shape[1]:
     if(np.sum(dialte_img[:,i]) > 0):
          start_col = i
          while np.sum(dialte_img[:,i]) > 0:
              i += 1
          end_col = i
          # 抠图
          roi_image.append(image[:,start_col:end_col])
     else:
          i += 1

扣图效果

优化: 扣出来的图例左右已经没有多余的背景,但是上下还有一定的的多余的背景,测试之后发现会影响后面匹配所有我们这里直接处理。

img = cv2.imread(tuli) # tuli: 已经扣出来的图例
rows, cols, channel = img.shape
min_x = rows
min_y = cols
max_x = 0
max_y = 0
count = 0

for x in range(1, rows):
   for y in range(1, cols):
      if list(set(img[x, y]))[0] == 0:
          if not max_x:
             max_x = count
             count = 0
             break
   else:
        count += 1
else:
    min_x = min_x - count
img1 = img[max_x:min_x, max_y:min_y]

 最终效果

缩小图例和背景

因为背景图和图例图实际大小并不是与浏览器的大小一致,因此我们还需要对图片进行缩小,方便后续匹配。

# 背景图缩小
bg = cv2.imread(bg)
y, x = bg.shape[0:2]
new_bg = cv2.resize(bg, (int(x - 80), int(y - 54)))
# 图例缩小
fg = cv2.imread(fg)
_y, _x = fg.shape[0:2]
new_fg = cv2.resize(fg, (int(_x / 2), int(_y / 2)))

定位背景图图例

 因为这里图例比较简单,同时没有什么旋转,缩放等花里胡哨的操作,我们可以直接使用OpenCV中的模板匹配来定位图例。

result = cv2.matchTemplate(bg, tuli, cv2.TM_CCOEFF_NORMED)  # 模板匹配
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result) 
br = (max_loc[0] + tw, max_loc[1] + th)
# 画框框
cv2.rectangle(bg, max_loc, br, (0, 0, 255), cv2.WINDOW_NORMAL)
cv2.imshow(f'tuli.png', bg)
cv2.waitKey(0)

识别结果

 

 最后经过测试,正确率大概80、90% 的样子,还是挺高的。

优化点

1. 我没考虑那种隔得很开的图标,比如下图中的点赞标识本来是一个图例,它分成了部分扣取。

 参考文章

Python识别验证码----数美图标点选 - 哔哩哔哩

前面提到过,美团验证码识别比较简单,但是后端验证很严格(心累),后续再分析图例验证的算法。

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值