问题来源

拼图验证码属于比较常见的一种验证码了,大部分情况下使用官方的验证码拼图/或者验证码免费版都能处理掉。

小白遇到的这个属于较为特殊的一种。页面上是canvas标签而非常见的div标签,如图所示

image-969f4368d631eede1e8c5db0ee1b

这一类验证码最大的问题在于元素不好捕获,canvas是一个矩形区域的画布,可以用 JavaScript 在上面绘画。

缺口图片和背景图片直接捕获的话,都是这个画布的区域(比如小白展示的这个网站),此前社区也有个类似的问题,小白测试了下的速度不是特别快。

目前也测试过了现有的几个指令,效果不是很好就是了,就打算自己折腾下

突破点

因为验证码肯定是有图片来源的,小白测试下来发现,点击登陆/刷新验证码的时候都有发送网络交互,交互的结果就是下载这两张图片。

那么剩下的就是很简单了,获取这网络交互的结果,将监听到的数据转化为图片,在做缺口识别。

image-99d3b3bc1277a38943d2711850c4

图片转化部分

image-4e82d7017a2fc1ec6d987cb56454

监听到的数据是这样的字典信息,可以通过字典的type的值判断是否为图片(image类型)

图片的转化

输入监听的数据的body信息,设置保存路径,可以将图片保存到指定位置

def new_img(img_data,save_path):
    # 去掉数据头部的描述信息
    image_data = img_data.replace('image/png;base64,', '')

    # 解码base64数据
    decoded_data = base64.b64decode(image_data)

    # 将数据保存成图片文件
    with open(save_path, 'wb') as file:
        file.write(decoded_data)

下载图片了之后,剩下的做法就比较多了,归根结底就是计算缺口的位置信息(从图片的左侧到做缺口的位置,也就是横坐标)

注意!这边小白默认是没有计算缩放比例的,有的时候canvas标签的大小和图片的大小对不上的话,需要使用指令获取图片大小,然后和canvas画布的大小计算缩放比,最后得出的横坐标再乘以这个缩放比

指令:

image-c8282cd88e9b8b8043a757499db4

处理图片获取坐标的话,目前是几个方法,小白也都介绍下吧

1.使用OpenCV库

介绍及案例可以参考社区小伙伴的帖子:

https://www.yingdao.com/community/detaildiscuss?id=6f5ac7d1-6d58-41cf-82c1-2054464bb3c4

注意点:

如果使用这个指令或者OpenCV库处理图片的时,提示类似这样乱码的情况或者提示,就需要检查截图或者本机的文件路径了。

OpenCV库暂不支持直接读取含有中文字符的路径的图片。

image-2231aa389142fa2c704e2b9c8fc8

image-431298cb7fa93df873d816685665

关于这个问题,小白也尝试过将文件路径重新编码,然后去读取。最后发现还是没办法正常执行。

后面发现严格来讲,imread并不是不支持中文字符路径,而是不支持Non-ascii文件路径。

那么方向就比较清楚了,先用其他支持中文字符路径的扩展库读取图片数据,再用OpenCV库从内存读取图片信息。(此处以numpy为例子)

numpy、OpenCV读取中文字符路径图片参考:

import io
import numpy as np
import cv2

# 读取图片路径
image_path = "假设这边是中文字符路径图片"

# 使用BytesIO对象读取图片
with open(image_path, "rb") as f:
   img_bytes = f.read()

# 将字节数据转换为NumPy数组
img_array = np.frombuffer(img_bytes, np.uint8)

# 将NumPy数组转换为OpenCV图像
img = cv2.imdecode(img_array, cv2.IMREAD_COLOR)

完整的计算缺口过程

import cv2
import numpy as np

img_file_bg=r'C:\Users\未白\Desktop\test11.png'
img_file_tp=r'C:\Users\未白\Desktop\test22.png'

# np.frombuffer()函数可以接受二进制字节数据,但也可以接受其他类型的数据,例如图片路径。
bg_img=cv2.imdecode(np.fromfile(img_file_bg,dtype=np.uint8),-1)
tp_img=cv2.imdecode(np.fromfile(img_file_tp,dtype=np.uint8),-1)

bg_edge = cv2.Canny(bg_img, 100, 200)
tp_edge = cv2.Canny(tp_img, 100, 200)

bg_pic = cv2.cvtColor(bg_edge, cv2.COLOR_GRAY2RGB)
tp_pic = cv2.cvtColor(tp_edge, cv2.COLOR_GRAY2RGB)

res = cv2.matchTemplate(bg_pic, tp_pic, cv2.TM_CCOEFF_NORMED)
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res) # 寻找最优匹配
X = max_loc[0]

print("缺口的横坐标:", X)

2.使用ddddocr扩展库

真不愧是带带弟弟,连小白也能轻松使用这个库。(主要还是免费,而且经小白实际测试下来,准确度还是不错的)具体使用方式可以参考这个链接:https://www.qinglite.cn/doc/8820647771fe47749

这边只介绍验证码识别的这部分

import ddddocr

img_file_bg=r'C:\Users\未白\Desktop\test11.png'
img_file_tp=r'C:\Users\未白\Desktop\test22.png'

det = ddddocr.DdddOcr(det=False, ocr=False)

#读取背景图和缺口图
with open(img_file_tp, 'rb') as f:
        target_bytes = f.read()
          
with open(img_file_bg, 'rb') as f:
    background_bytes = f.read()

res = det.slide_match(target_bytes, background_bytes, simple_target=True)
print(res)

3.使用影刀自带的验证码处理指令

如果真的觉得麻烦,不想去写的话,也可以用影刀自带的验证码处理指令,也支持本地验证码识别。

比如这个验证码识别指令,也能获取到缺口的横坐标信息。

image-3553cf526e5950975d6b0a8d1cac

得到横坐标信息之后就好办了,一般情况,如果图片没有做缩放,直接拿着这个缺口的横坐标信息,去拖动滑块就行了。小白目前实测下来,准确度还是比较高的,基本上一两次就过了。

效果如下:

image-20839ead845e67fcbacfd8cdaed0

作者:未白 来源:影刀社区