拒绝反爬虫!教你搞定爬虫验证码

点击上方“Python爬虫与数据挖掘”,进行关注

回复“书籍”即可获赠Python从入门到进阶共10本电子书

疏影横斜水清浅,暗香浮动月黄昏。

「数仓宝贝库」,带你学数据!

导读:目前,许多网站采取各种各样的措施来反爬虫,其中一个措施便是使用验证码。随着技术的发展,验证码的花样越来越多。验证码最初是几个数字组合的简单的图形验证码,后来加入了英文字母和混淆曲线。有的网站还可能看到中文字符的验证码,这使得识别越发困难。

使用验证码可以防止应用或者网站被恶意注册、攻击,对于网站、APP而言,大量的无效注册、重复注册甚至是恶意攻击很令人头痛。使用验证码能够很大程度上减少这些恶意操作。验证码变得越来越复杂,爬虫的工作也变得越发艰难。有时候我们必须通过验证码的验证才能够访问页面(如图1所示)。

图1 验证码界面

目前主流的 4 种验证码为输入式验证码、滑动式验证码、宫格式验证码和点击式的图文验证,下面我们来分别讲解它们的解决思路。

4种验证码的解决思路

01 输入式验证码

这种验证码主要是通过用户输入图片中的字母、数字、汉字等进行验证,如图2所示。

图2 输入式验证码

解决思路:这是最简单的一种验证码,只要识别出里面的内容,然后填入输入框中即可。这种识别技术叫OCR,这里推荐使用 Python 的第三方库 tesserocr。tesserocr 与 pytesseract 是 Python 的一个 OCR 识别库,但其实是对 Tesseract 做的一层 Python API 封装,pytesseract 是 Google 的 Tesseract-OCR 引擎包装器;所以它们的核心是 Tesseract。对于没有什么背景影响的验证码,直接通过这个库来识别就可以。但是对于有嘈杂的背景的验证码,直接识别的识别率会很低,遇到这种验证码需要先对图片进行灰度化,然后再进行二值化,再去识别,这样识别率会大大提高。

02 滑动式验证码

这种是将备选碎片直线滑动到正确的位置,如图3所示。 

图3 滑动式验证码

解决思路:对于这种验证码就比较复杂一点,但也是有相应的办法。我们直接想到的就是模拟人去拖动验证码的行为,点击按钮,然后看到了缺口的位置,最后把拼图拖到缺口位置处完成验证。 

第一步:点击按钮。当没有点击按钮的时候图片中的缺口和拼图是没有出现的,点击后才出现,这为我们找到缺口的位置提供了灵感。 

第二步:拖到缺口位置。我们知道拼图应该拖到缺口处,但是这个距离如何用数值来表 示?通过第一步观察到的现象,我们可以找到缺口的位置。这里我们可以比较两张图的像素, 设置一个基准值,如果某个位置的差值超过了基准值,那我们就找到了这两张图片不一样的位置,当然我们是从那块拼图的右侧开始并且从左到右,找到第一个不一样的位置时就结束,这时的位置应该是缺口的 left,所以我们使用 selenium 拖到这个位置即可。这里还有个疑问,就 是如何能自动保存这两张图?我们可以先找到这个标签,然后获取它的 location 和 size,接着 是 top = int(location['y'])、bottom = int(location['y'] + size['height'])、left = int(location['x']) 以及right = int(location['x'] + size['width']),然后截图,最后抠图填入这四个位置就行。具体的使用 可以查看 selenium 文档,点击按钮前抠一张图,点击后再抠一张图。最后拖动时需要模拟人的 行为,先加速然后减速。因为这种验证码有行为特征检测,人是不可能做到一直匀速的,否则 它就判定为是机器在拖动,这样就无法通过验证了。

03 宫格验证码

如图4所示的验证码,爬虫难度比较大,每一次出现的都不一样,就算出现一样的,其拖动顺序也不相同。但是,我们发现不一样的验证码个数是有限的,这里采用模版匹配的方 法,把所有出现的验证码保存下来,然后挑出不一样的验证码,按照拖动顺序命名。我们从 左到右从上到下,将其分别设为 1、2、3、4。上图的滑动顺序为 4→3→2→1,所以我们命名 4_3_2_1.png。当验证码出现的时候,用我们保存的图片一一枚举,与出现的这种来比较像素, 方法见“滑动式验证码”部分。如果匹配上了,拖动顺序就为 4→3→2→1。然后使用 selenium 模拟即可。


图4 宫格验证码

04 点击式的图文验证和图标选择

1)图文验证:通过文字提醒用户点击图中相同字的位置从而进行验证。

2)图标选择:给出一组图片,按要求点击其中一张或多张。借用万物识别的难度阻挡 机器。这两种原理相似,只不过一个是给出文字,点击图片中的文字,而一个是给出图片,点 出内容相同的图片。这两种都没有特别好的方法,只能借助第三方识别接口来识别出相同的内 容。推荐一个方法,把验证码发过去,会返回相应的点击坐标,然后再使用 selenium 模拟点击即可。

OCR验证码

OCR(Optical Character Recognition,光学字符识别)是指电子设备(例如扫描仪或数码相机)检查纸上打印的字符,通过检测暗、亮的模式来确定其形状,然后用字符识别方法将形状翻译成计算机文字的过程,下面介绍使用这种图像识别技术输入验证码的方法。

验证码识别基本步骤: 

1)预处理 

2)灰度化 

3)二值化

4)去噪

5)分割 

6)识别 

在使用pytesseract之前,必须安装 Tesseract-OCR,因为pytesserat 依赖于 Tesseract-OCR, 若未安装则无法使用。首先使用 pytesseract 将彩色的图像转化为灰色的图像。

# 使用路径导入图片
im = Image.open(imgimgName) 
# 使用byte流导入图片 
# im = Image.open(io.BytesIO(b)) 
# 转化到灰度图
imgry = im.convert('L') 
# 保存图像
imgry.save('gray-'+imgName)

灰度化的图像如图5所示。 

图5 灰度化图像

紧接着将所得的图像二值化,将图片处理为只有黑白两色的图片,利于后面的图像处理和识别。

# 二值化,采用阈值分割法,threshold为分割点 
Threshold = 140 
Table = [ ]
For j in range(256): 
If j < threshold: 
Table.append(0) 
Else: 
Table.append(1) 
Out = imgry.point(table,'1') 
Out.save('b'+imgName)

二值化的图像如图 6 所示。 

图6 二值化图像

最后进行识别,得到的结果如图7所示。 

图7 识别结果

# 识别 
Text = pytesseract.image_to_string(out) 
Print(“识别结果:" +text)

实战案例

目前,很多网站为了防止爬虫肆意模拟浏览器登录,采用增加验证码的方式来拦截爬虫。验证码的形式有多种,最常见的就是图片验证码。

1 基本识别原理概述

1)每一幅图像在结构上,都是由一个个像素组成的矩阵,每一个像素都为单元格。

2)彩色图像的像素由三原色(红、绿、蓝)构成 元组,灰度图像的像素是一个单值,每个像素的值范围为(0, 255)。

某系统门户登录界面中的验证码如图8所示, 现在我们要实现自动的验证码识别。

图8 验证码

2 图像特征 

首先,我们仔细观察一下这个验证码图像,可以发现一些如图9所示的固定特征。

1)验证码中的字符数始终为 6,并且是灰度图像。

2)字符间的间隔看起来每次都一样。 

3)每个字符都是完全定义的。 

4)图像有许多杂散的黑暗像素,以及穿过图像的线条作为干扰因素。

图9 固定特征

3 图像分析 

使用一个工具(binary-image)以二进制形式可视化图像(0 表示黑色像素,1 表示白色像 素)。图像尺寸为 45×180,每个字符分配 30 个像素的空间来进行适配,从而使它们的间隔比 较均匀。因此,取得了验证码识别路上的第一步。如图10所示的结果:把图像裁剪成 6 个不同的部分,每个部分的宽度均为 30 像素。

图10 二进制可视化图像

4 字符部分裁剪

图像裁剪的语法如下:

from PIL import Image 
image = Image.open("filename.png") 
cropped_image = image.crop((left, upper, right, lower)

比如要裁剪第一个字符:

from PIL import Image 
image = Image.open("captcha.png").convert("L") 
cropped_image = image.crop((0, 0, 30, 45))
cropped_image.save("cropped_image.png")

得到的图像如图11所示。 

图11 结果图像

将其打包到一个循环中,编写了一个简单的脚本,从该站点获取 500 个验证码图像,并将所有裁剪后的字符保存到一个文件夹中。

5 图像去杂 

为了“清理”图像中的干扰因素(删除不必要的线和点),我们可以使用一个很简单的算法:字符中的所有像素都是纯黑色(0)。如果它不是完全黑色的,则将它当成白色的。因此,对于值大于0的每个像素,将给其重新赋值为255。使用 load() 函数将图像转换为 45×180 数字矩阵,然后对其进行处理。

pixel_matrix = cropped_image.load() 
for col in range(0, cropped_image.height): 
for row in range(0, cropped_image.width):
if pixel_matrix[row, col] != 0: 
pixel_matrix[row, col] = 255
image.save("thresholded_image.png"

为了清晰起见,将代码应用于原始图像。原始图像如图12所示。

图12 原始图像

矫正后的图像如图13所示。

图13 矫正后的图像

可以看到,并非完全黑暗的所有像素都被删除了,比如通过图像的线。上述方法在图像处理 中的专业术语叫作阈值处理,当然还有很多其他的处理方法,阈值处理只是最简单实用的方法。

6 去除图像中的黑点 

图像中有许多杂散的黑暗像素作为干扰因子。循环遍历图像矩阵,如果相邻像素是白色的,并且与相邻像素相对的像素也是白色的,而中心像素是黑色的,则设定中心像素为白色。

for column in range(1, image.height - 1):
for row in range(1, image.width - 1):
if pi xel_matrix[row, column] == 0 and pixel_matrix[row, column - 1] == 255 and
pixel_matrix[row, column + 1] == 255:
pixel_matrix[row, column] = 255
if pi xel_matrix[row, column] == 0 and pixel_matrix[row - 1, column] == 255 and
pixel_matrix[row + 1, column] == 255:
pixel_matrix[row, column] = 25

处理后的结果如图14所示。

图14 处理后的结果

经过以上步骤的处理,图像已经只剩下字符框架了。虽然有些字符已经丢失了一些基础像 素,但是每个字符的图像骨架基本上都完备。当然这个是必需的,我们进行这么多处理的主要 目的就是为每个可能的字符都截取生成合适的字符图。 

7 构建字符图库 

将上述算法裁剪得到的所有字符图像都存储于文件夹下。下一个任务是为属于“ A-Z0-9” 的每个字符找到至少一个样本图像(如图15所示)。这一步就像“训练”步骤,手动为每个 字符选择了一个字符图像并对其更名。

图15 样本图像

8 选择最优的字符图 

运行其他几个脚本,以确保每个字符的图像中都有最佳的图像,例如,如果有 20 个“ A” 的字符图像,那么暗色数量最少的图像显然是噪声最少的图像,因此最适合作为骨架图像。选择的原则如下:

1)一个按照字符排序的相似图像(约束条件:黑色像素数量大小,并且相似度 > = 90%~95%)。 

2)一个从每个分组字符获得的最佳图像。因此,到目前为止,我们生成了一个像素图像库。我们将其转换为像素矩阵,并将位图字符图转为数字点阵 JSON 文件。

9 识别算法 

最后是获取任何新的验证码图像的算法:使用相同的算法尽量减少新图像中不必要的干扰因子。对于新验证码图片中的每个字符,强制通过与生成的 JSON 文件的像素矩阵来匹配,基 于相应的黑色像素匹配来计算相似度。如果一个像素是黑色的,其在图像中的位置恰好是待破 解的验证码,并且此像素位于字符库中的骨架图像 / 位图内的相同位置处,则计数会递增 1。与骨架图像中黑色像素的数量进行对比,计算匹配百分比,选择具有最高匹配百分比的字符就是识别结果的字符。 

最终结果如图16所示,若得到的字符为 Z5M3MQ,则验证码被成功识别出来了。

图16 识别结果

本文摘编于《Python网络爬虫技术与实践》,经出版方授权发布。

------------------- 送书 -------------------

内容简介

1、如正文所介绍的那样。

活动规则

参与方式:在下方公众号后台回复 “送书”关键字,记得是“送书”二字哈,即可参与本次的送书活动。

公布时间:2021年6月17号(周四)晚上20点

领取事宜:请小伙伴添加小助手微信: WebFighting,或者扫码添加好友。添加小助手的每一个人都可以领取一份Python学习资料,更重要的是方便联系。

注意事项:一定要留意微信消息,如果你是幸运儿就尽快在小程序中填写收货地址、书籍信息。一天之内没有填写收货信息,送书名额就转给其他人了噢,欢迎参与

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值