猿人学web端爬虫攻防大赛赛题解析_第八题:验证码 图文点选

一、前言

第八题跟以往的几道混淆题有很大区别,不需要你去很费劲扣代码逻辑,找加密参数什么的,但是难就难在验证码图片太难识别了,花了一两个星期尝试看看有没比较好的解决方案,最终还是选择最稳妥的人工识别法😂,深度学习识别什么的以后再慢慢研究,不过解题过程中还是涨了不少姿势。

二、解析过程

2.1、点选逻辑分析

网站给出的目标是按顺序点击这四个字
在这里插入图片描述

通过点击观察请求后,发现其操作逻辑是先选择正确的文字坐标,点击完成后,携带正确的坐标信息请求数据页,最后得到正确的数值相应。

坐标信息附带在数据页api的请求参数上,就是这个answer,由四个数值构成的汉字对应坐标信息:

/api/match/8?page=1&answer=735%7C776%7C145%7C724%7C

在源代码里请求观察可以发现,四个坐标会通过“|”符号连接:
在这里插入图片描述
每次点击验证码中的一个位置,就会选中该汉字对应的div 索引:
在这里插入图片描述

整个验证码图片共计对应有900个div标签,对应九个字的话,就是每个字对应100个div,点击某个字体区域的任何一个div位置,都可以算成功选中这个字,经过测试,div是横向排列的。以下图为例,位置1234的位置索引分别是:“0|29|270|299|”。

在这里插入图片描述

所以实际上整个验证码实际上是一幅30*30像素的图片,最上面的三个字对应的所有div位置如下表所示,其余字的坐标也是按这个规则推算。

行数 左边的字 中间的字 右边的字
第一行 0-9 10-19 20-29
第二行 30-39 40-49 50-59
第三行 60-69 70-79 80-89
第四行 90-99 100-109 110-119
第五行 120-129 130-139 140-149
第六行 150-159 160-169 170-179
第七行 180-189 190-199 200-209
第八行 210-219 220-229 230-239
第九行 240-249 250-259 260-269
第十行 270-279 280-289 290-299

了解了验证码里的字对应的div坐标转换规则以后,剩下的问题就是怎么确定每个字在图上的哪个区域,这也是这道题最大的难点。按常规直觉,通过ocr识别是我首先想到的方式。

2.2、验证码图像预处理

由于验证码图片的干扰程度太强,一开始我尝试直接用一些ocr平台(还是太天真)试试能不能识别出里面的字体来,结果果然是失败了,直接用没处理的原图来识别是不可能正常识别出字体的,所以还是要做一些预处理,把图片里面的背景、字体表面的干扰线条去掉。

由于我以前没怎么做过图像处理,所以在网上搜了半天,找到一个大佬的处理方案,看这里,并且做了一点小改动,整体上分下面几步:

2.2.1、处理图像背景

以这个验证码为例,大片的背景颜色对识别这个这些字体造成了严重干扰,所以首先要想办法把背景颜色消除。
在这里插入图片描述

处理代码如下:

im=cv2.imread('验证码图片')
# 读取图片高,宽
h, w = im.shape[0:2]

# np.unique()该函数是去除数组中的重复数字,并进行排序之后输出,这里返回的是所有像素rgb值,以及对应的数量
colors, counts = np.unique(np.array(im).reshape(-1, 3), axis=0, return_counts=True)

#挑选图片中背景最多的两种颜色对应的像素个数
ct=np.sort(counts)
top2_counts=ct[-2:].tolist()

# 把频率最高的两种颜色筛掉
info_dict = {
   counts[i]: colors[i].tolist() for i, v in enumerate(counts) if  not v in top2_counts}
colors_select=np.array([v for v in info_dict.values()])

# 移除了背景的图片,赋值为去了黑色背景的colors就不会出现少一个字的情况
remove_background_rgbs = colors_select
mask = np.zeros((h, w, 3), np.uint8) + 255 # 生成一个全是白色的图片

# 通过循环将不是噪点的像素,赋值给一个白色的图片,最后到达移除背景图片的效果
for rgb in remove_background_rgbs:
    mask[np.all(im == rgb, axis=-1)] = im[np.all(im == rgb, axis=-1)]
cv2.imshow("Image with background removed", mask)  # 移除了背景的图片
cv2.waitKey(0)

经过这一步处理后,图片的背景就被替换为白色了,只剩遮住字体的线条还存在一定干扰。
在这里插入图片描述

2.2.2、移除干扰线条

移除干扰线条的原理比较简单,由于验证码图片里,干扰线条的颜色跟待识别字体是不一样的,所以可以通过选择字体间隔间的像素颜色,将对应颜色的像素都替换为白色,这样就达到去除线条的目的。

# 去掉线条,全部像素黑白化
line_list = [] # 首先创建一个空列表,用来存放出现在间隔当中的像素点
# 两个for循环,遍历9000次
for y in range(h):
    for x in range(w):
        tmp = mask[x, y].tolist()
        if tmp != [0, 0, 0]:
            if 0 < y < 20 or 110 < y < 120 or 210 < y < 220:
                line_list.append(tmp)
            if  0 < x < 10 or 100 < x < 110 or 200 < x < 210:
                line_list.append(tmp)
remove_line_rgbs = np.unique(np.array(line_list).reshape(-1, 3), axis=0)
for rgb in remove_line_rgbs:
    mask[np.all(mask == rgb, axis=-1)] = [255, 255, 255]
# 把所有字体颜色统一替换为黑色
mask[np.any(mask != [255, 255, 255], axis=-1)] = [0, 0, 0]
cv2.imshow("Image with lines removed", mask)  # 移除了线条的图片
cv2.waitKey(0)

移除线条后的图片是这样:
在这里插入图片描述

2.2.3、增强字体显示效果

经过上一步处理后的图片,基本上已经可以很好的辨识出字体原有的形状了,但是由于字形内还存在一些小空隙,整个字看起来会略微有些单薄,所以再做一个腐蚀处理,让字体显示更清晰。

# 生成一个2行三列数值全为1的二维数字,作为腐蚀操作中的卷积核
kernel = np.ones((2, 3), 'uint8')</
  • 4
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值