Python爬虫之极验滑动验证码的识别

极验滑动验证码的识别

上节我们了解了可以直接利用 tesserocr 来识别简单的图形验证码。近几年出现了一些新型验证码,其中比较有代表性的就是极验验证码,它需要拖动拼合滑块才可以完成验证,相对图形验证码来说识别难度上升了几个等级。本节将讲解极验验证码的识别过程。

1. 本节目标

我们的目标是用程序来识别并通过极验验证码的验证,包括分析识别思路、识别缺口位置、生成滑块拖动路径、模拟实现滑块拼合通过验证等步骤。

2. 准备工作

本次我们使用的 Python 库是 Selenium,浏览器为 Chrome。请确保已经正确安装 Selenium 库、Chrome 浏览器,并配置 ChromeDriver,相关流程可以参考第 1 章的说明。

3. 了解极验验证码

极验验证码官网为:极验GEETEST-全球交互安全创领者-交互安全|业务安全 | 移动安全。它是一个专注于提供验证安全的系统,主要验证方式是拖动滑块拼合图像。若图像完全拼合,则验证成功,即表单成功提交,否则需要重新验证。

验证码示例

验证码示例

现在极验验证码已经更新到 3.0 版本。截至 2017 年 7 月,全球有 16 万家企业使用极验,每天服务响应超过 4 亿次。极验验证码广泛应用于直播视频、金融服务、电子商务、游戏娱乐、政府企业等各大类型网站。下面图中是斗鱼、魅族的登录页面,它们都对接了极验验证码。

斗鱼登录页面

魅族登录页面

4. 极验验证码的特点

极验验证码相较于图形验证码来说识别难度更大。对于极验验证码 3.0 版本,我们首先点击按钮进行智能验证。如果验证不通过,则会弹出滑动验证的窗口,拖动滑块拼合图像进行验证。之后三个加密参数会生成,通过表单提交到后台,后台还会进行一次验证。

极验验证码还增加了机器学习的方法来识别拖动轨迹。官方网站的安全防护有如下几点说明。

  • 三角防护之防模拟

恶意程序模仿人类行为轨迹对验证码进行识别。针对模拟,极验拥有超过 4000 万人机行为样本的海量数据。利用机器学习和神经网络构建线上线下的多重静态、动态防御模型。识别模拟轨迹,界定人机边界。

  • 三角防护之防伪造

恶意程序通过伪造设备浏览器环境对验证码进行识别。针对伪造,极验利用设备基因技术。深度分析浏览器的实际性能来辨识伪造信息。同时根据伪造事件不断更新黑名单,大幅提高防伪造能力。

  • 三角防护之防暴力

恶意程序短时间内进行密集的攻击,对验证码进行暴力识别 针对暴力,极验拥有多种验证形态,每一种验证形态都有利用神经网络生成的海量图库储备,每一张图片都是独一无二的,且图库不断更新,极大程度提高了暴力识别的成本。

另外极验的验证相对于普通验证方式更加方便,体验更加友好,其官方网站说明如下:

  • 点击一下,验证只需要 0.4 秒

极验始终专注于去验证化实践,让验证环节不再打断产品本身的交互流程,最终达到优化用户体验和提高用户转化率的效果。

  • 全平台兼容,适用各种交互场景

极验兼容所有主流浏览器甚至古老的 IE6,也可以轻松应用在 iOS 和 Android 移动端平台,满足各种业务需求,保护网站资源不被滥用和盗取。

  • 面向未来,懂科技,更懂人性

极验在保障安全同时不断致力于提升用户体验,精雕细琢的验证面板,流畅顺滑的验证动画效果,让验证过程不再枯燥乏味。

因此,相较于一般验证码,极验的验证安全性和易用性有了非常大的提高。

5. 识别思路

对于应用了极验验证码的网站,如果我们直接模拟表单提交,加密参数的构造是个问题,需要分析其加密和校验逻辑,相对烦琐。所以我们采用直接模拟浏览器动作的方式来完成验证。在 Python 中,我们可以使用 Selenium 来完全模拟人的行为的方式来完成验证,此验证成本相比直接去识别加密算法少很多。

首先我们找到一个带有极验验证的网站,最合适的当然为极验官方后台了,链接为:Geetest Account后台,首先可以看到在登录按钮上方有一个极验验证按钮:

验证按钮

此按钮为智能验证按钮。一般来说,如果是同一个会话,一段时间内第二次点击会直接通过验证。如果智能识别不通过,则会弹出滑动验证窗口,我们要拖动滑块拼合图像完成二步验证。

拖动示例

验证成功后,验证按钮变成如图的状态。

接下来,我们便可以提交表单了。

所以,识别验证需要完成如下三步。

  • 模拟点击验证按钮

  • 识别滑动缺口的位置

  • 模拟拖动滑块

第一步操作是最简单的,我们可以直接用 Selenium 模拟点击按钮即可。

第二步操作识别缺口的位置比较关键,这里需要用到图像的相关处理方法。首先观察缺口的样子。

缺口示例

缺口示例

缺口的四周边缘有明显的断裂边缘,边缘和边缘周围有明显的区别。我们可以实现一个边缘检测算法来找出缺口的位置。对于极验验证码来说,我们可以利用和原图对比检测的方式来识别缺口的位置,因为在没有滑动滑块之前,缺口并没有呈现。

初始状态

我们可以同时获取两张图片。设定一个对比阈值,然后遍历两张图片,找出相同位置像素 RGB 差距超过此阈值的像素点,那么此像素点的位置就是缺口的位置。

第 (3) 步操作看似简单,但其中的坑比较多。极验验证码增加了机器轨迹识别,匀速移动、随机速度移动等方法都不能通过验证,只有完全模拟人的移动轨迹才可以通过验证。人的移动轨迹一般是先加速后减速,我们需要模拟这个过程才能成功。

有了基本的思路之后,我们就用程序来实现极验验证码的识别过程吧。

6. 初始化

这次我们选定的链接为 Geetest Account后台,也就是极验的管理后台登录页面。在这里我们首先初始化一些配置,如 Selenium 对象的初始化及一些参数的配置,如下所示:

EMAIL = 'test@test.com'
PASSWORD = '123456'
​
class CrackGeetest():def __init__(self):self.url = 'https://account.geetest.com/login'self.browser = webdriver.Chrome()self.wait = WebDriverWait(self.browser, 20)self.email = EMAILself.password = PASSWORD

其中 EMAIL 和 PASSWORD 就是登录极验需要的用户名和密码,如果没有的话可以先注册一下。

7. 模拟点击

实现第一步的操作,也就是模拟点击初始的验证按钮。我们定义一个方法来获取这个按钮,利用显式等待的方法来实现,如下所示:

def get_geetest_button(self):"""获取初始验证按钮:return: 按钮对象"""button = self.wait.until(EC.element_to_be_clickable((By.CLASS_NAME, 'geetest_radar_tip')))return button

获取一个 WebElement 对象,调用它的 click() 方法即可模拟点击,代码如下所示:

# 点击验证按钮
button = self.get_geetest_button()
button.click()

第一步的工作就完成了。

8. 识别缺口

接下来识别缺口的位置。首先获取前后两张比对图片,二者不一致的地方即为缺口。获取不带缺口的图片,利用 Selenium 选取图片元素,得到其所在位置和宽高,然后获取整个网页的截图,图片裁切出来即可,代码实现如下:

def get_position(self):"""获取验证码位置:return: 验证码位置元组"""img = self.wait.until(EC.presence_of_element_located((By.CLASS_NAME, 'geetest_canvas_img')))time.sleep(2)location = img.locationsize = img.sizetop, bottom, left, right = location['y'], location['y'] + size['height'], location['x'], location['x'] + size['width']return (top, bottom, left, right)
​
def get_geetest_image(self, name='captcha.png'):"""获取验证码图片:return: 图片对象"""top, bottom, left, right = self.get_position()print(' 验证码位置 ', top, bottom, left, right)screenshot = self.get_screenshot()captcha = screenshot.crop((left, top, right, bottom))return captcha

这里 get_position() 函数首先获取图片对象,获取它的位置和宽高,随后返回其左上角和右下角的坐标。get_geetest_image() 方法获取网页截图,调用了 crop() 方法将图片裁切出来,返回的是 Image 对象。

接下来我们需要获取第二张图片,也就是带缺口的图片。要使得图片出现缺口,只需要点击下方的滑块即可。这个动作触发之后,图片中的缺口就会显现,如下所示:

def get_slider(self):"""获取滑块:return: 滑块对象"""slider = self.wait.until(EC.element_to_be_clickable((By.CLASS_NAME, 'geetest_slider_button')))return slider

这里利用 get_slider() 方法获取滑块对象,调用 click() 方法即可触发点击,缺口图片即可呈现,如下所示:

# 点按呼出缺口
slider = self.get_slider()
slider.click()

调用 get_geetest_image() 方法将第二张图片获取下来即可。

现在我们已经得到两张图片对象,分别赋值给变量 image1 和 image2。接下来对比图片获取缺口。我们在这里遍历图片的每个坐标点,获取两张图片对应像素点的 RGB 数据。如果二者的 RGB 数据差距在一定范围内,那就代表两个像素相同,继续比对下一个像素点。如果差距超过一定范围,则代表像素点不同,当前位置即为缺口位置,代码实现如下:

def is_pixel_equal(self, image1, image2, x, y):"""判断两个像素是否相同:param image1: 图片 1:param image2: 图片 2:param x: 位置 x:param y: 位置 y:return: 像素是否相同"""# 取两个图片的像素点pixel1 = image1.load()[x, y]pixel2 = image2.load()[x, y]threshold = 60if abs(pixel1[0] - pixel2[0]) <threshold and abs(pixel1[1] - pixel2[1]) < threshold and abs(pixel1[2] - pixel2[2]) < threshold:return Trueelse:return Falsedef get_gap(self, image1, image2):"""获取缺口偏移量:param image1: 不带缺口图片:param image2: 带缺口图片:return:"""left = 60for i in range(left, image1.size[0]):for j in range(image1.size[1]):if not self.is_pixel_equal(image1, image2, i, j):left = ireturn leftreturn left

get_gap() 方法即获取缺口位置的方法。此方法的参数是两张图片,一张为带缺口图片,另一张为不带缺口图片。这里遍历两张图片的每个像素,利用 is_pixel_equal() 方法判断两张图片同一位置的像素是否相同。比较两张图 RGB 的绝对值是否均小于定义的阈值 threshold。如果绝对值均在阈值之内,则代表像素点相同,继续遍历。否则代表不相同的像素点,即缺口的位置。

两张对比图片如图所示。

初始状态

后续状态

两张图片有两处明显不同的地方:一个就是待拼合的滑块,一个就是缺口。滑块的位置会出现在左边位置,缺口会出现在与滑块同一水平线的位置,所以缺口一般会在滑块的右侧。如果要寻找缺口,直接从滑块右侧寻找即可。我们直接设置遍历的起始横坐标为 60,也就是从滑块的右侧开始识别,这样识别出的结果就是缺口的位置。

现在,我们获取了缺口的位置。完成验证还剩下最后一步 —— 模拟拖动。

9. 模拟拖动

模拟拖动过程不复杂,但其中的坑比较多。现在我们只需要调用拖动的相关函数将滑块拖动到对应位置,是吗?如果是匀速拖动,极验必然会识别出它是程序的操作,因为人无法做到完全匀速拖动。极验验证码利用机器学习模型,筛选此类数据为机器操作,验证码识别失败。

我们尝试分段模拟,将拖动过程划分几段,每段设置一个平均速度,速度围绕该平均速度小幅度随机抖动,这样也无法完成验证。

最后,完全模拟加速减速的过程通过了验证。前段滑块做匀加速运动,后段滑块做匀减速运动,利用物理学的加速度公式即可完成验证。

滑块滑动的加速度用 a 来表示,当前速度用 v 表示,初速度用 v0 表示,位移用 x 表示,所需时间用 t 表示,它们之间满足如下关系:

x = v0 * t + 0.5 * a * t * t 
v = v0 + a * t

利用这两个公式可以构造轨迹移动算法,计算出先加速后减速的运动轨迹,代码实现如下所示:

def get_track(self, distance):"""根据偏移量获取移动轨迹:param distance: 偏移量:return: 移动轨迹"""# 移动轨迹track = []# 当前位移current = 0# 减速阈值mid = distance * 4 / 5# 计算间隔t = 0.2# 初速度v = 0while current < distance:if current < mid:# 加速度为正 2a = 2else:# 加速度为负 3a = -3# 初速度 v0v0 = v# 当前速度 v = v0 + atv = v0 + a * t# 移动距离 x = v0t + 1/2 * a * t^2move = v0 * t + 1 / 2 * a * t * t# 当前位移current += move# 加入轨迹track.append(round(move))return track

这里定义了 get_track() 方法,传入的参数为移动的总距离,返回的是运动轨迹。运动轨迹用 track 表示,它是一个列表,列表的每个元素代表每次移动多少距离。

首先定义变量 mid,即减速的阈值,也就是加速到什么位置开始减速。在这里 mid 值为 4/5,即模拟前 4/5 路程是加速过程,后 1/5 路程是减速过程。

接着定义当前位移的距离变量 current,初始为 0,然后进入 while 循环,循环的条件是当前位移小于总距离。在循环里我们分段定义了加速度,其中加速过程的加速度定义为 2,减速过程的加速度定义为−3。之后套用位移公式计算出某个时间段内的位移,将当前位移更新并记录到轨迹里即可。

直到运动轨迹达到总距离时,循环终止。最后得到的 track 记录了每个时间间隔移动了多少位移,这样滑块的运动轨迹就得到了。

最后按照该运动轨迹拖动滑块即可,方法实现如下所示:

def move_to_gap(self, slider, tracks):"""拖动滑块到缺口处:param slider: 滑块:param tracks: 轨迹:return:"""ActionChains(self.browser).click_and_hold(slider).perform()for x in tracks:ActionChains(self.browser).move_by_offset(xoffset=x, yoffset=0).perform()time.sleep(0.5)ActionChains(self.browser).release().perform()

这里传入的参数为滑块对象和运动轨迹。首先调用 ActionChains 的 click_and_hold() 方法按住拖动底部滑块,遍历运动轨迹获取每小段位移距离,调用 move_by_offset() 方法移动此位移,最后调用 release() 方法松开鼠标即可。

如果本文对您有帮助,记得点赞,收藏+关注,您的支持是我做大的动力!

下一篇我们将学习极验滑动验证码识别,敬请期待!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/704210.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何在Linux部署Portainer并结合内网穿透远程管理本地Docker容器

文章目录 前言1. 部署Portainer2. 本地访问Portainer3. Linux 安装cpolar4. 配置Portainer 公网访问地址5. 公网远程访问Portainer6. 固定Portainer公网地址 前言 Portainer 是一个轻量级的容器管理工具&#xff0c;可以通过 Web 界面对 Docker 容器进行管理和监控。它提供了可…

用c# 自己封装的Modbus工具类库源码

前言 Modbus通讯协议在工控行业的应用是很多的&#xff0c;并且也是上位机开发的基本技能之一。相关的类库也很多也很好用。以前只负责用&#xff0c;对其并没有深入学习和了解。前段时间有点空就在这块挖了挖。想做到知其然还要知其所以然。所以就有了自己封装的Modbus工具类库…

Mysql 常用数据类型

数值型(整数)的基本使用 如何定义一个无符号的整数 数值型(bit)的使用 数值型(小数)的基本使用 字符串的基本使用 字符串使用细节 日期类型的基本使用

Orange3数据预处理(列选择组件)数据角色及类型描述

在Orange3的文件组件中&#xff0c;datetime、categorical、numeric以及text代表不同种类的数据类型&#xff0c;具体如下&#xff1a; datetime&#xff1a;代表日期和时间类型的数据。通常用于时间序列分析、生存分析和其他需要考虑时间因素的机器学习任务中。例如&#xff0…

图像读取裁剪与人脸识别

图像读取 Image read ⇒ \Rightarrow ⇒ torchvision.datasets from torchvision import datasets dataset datasets.ImageFolder(data_dir, transformtransforms.Resize((512, 512)))Return value illustration dataset[0][0]是PIL.Image objects&#xff0c;这利用IPyth…

小红书关键词爬虫

标题 1 统计要收集的关键词,制作一个文件夹2 爬取每一页的内容3 爬取标题和内容4 如果内容可以被查看,爬取评论内容5 将结果进行汇总,并且每个帖子保存为一个json文件,具体内容6 总结1 统计要收集的关键词,制作一个文件夹 例如,我要收集旅游相关的,就收集: 旅游、旅行…

关系型数据库事务的四性ACID:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability)

关系型数据库事务的四性ACID:原子性&#xff08;Atomicity&#xff09;、一致性&#xff08;Consistency&#xff09;、隔离性&#xff08;Isolation&#xff09;和持久性&#xff08;Durability&#xff09; 事务的四性通常指的是数据库事务的ACID属性&#xff0c;包括原子性&…

Python从入门到精通指南【第101篇—入门到精通】【文末送书-24】

文章目录 Python从入门到精通指南第一步&#xff1a;入门基础1.1 安装Python1.2 Hello World1.3 变量和数据类型1.4 控制流程 第二步&#xff1a;深入学习2.1 函数和模块2.2 列表、元组和字典2.3 文件操作 第三步&#xff1a;高级主题3.1 面向对象编程3.2 异常处理3.3 正则表达…

大型电商日志离线分析系统(一)

一、项目需求分析 某大型网站日志离线分析系统 1.1 概述 该部分的主要目标就是描述本次项目最终七个分析模块的页面展示。 1.2 工作流 在我们的demo展示中&#xff0c;我们使用jqueryecharts的方式调用程序后台提供的rest api接口&#xff0c;获取json数据&#xff0c;然后…

《极简C++学习专栏》之结束语

朋友们&#xff0c;经过这么长的时间&#xff0c;《极简C学习专栏》的文章创作就要结束了&#xff0c;感谢你们一路陪伴&#xff01; 也希望你们能支持我接下来的其他专栏的创作&#xff01; 专栏的初衷 《极简C学习》专栏的初衷源自于我个人的学习笔记&#xff0c;记录下自己…

网络安全与信创产业发展:构建数字时代的护城河

✨✨ 欢迎大家来访Srlua的博文&#xff08;づ&#xffe3;3&#xffe3;&#xff09;づ╭❤&#xff5e;✨✨ &#x1f31f;&#x1f31f; 欢迎各位亲爱的读者&#xff0c;感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua&#xff0c;在这里我会分享我的知识和经验。&#x…

数字人的未来:数字人对话系统 Linly-Talker + 克隆语音 GPT-SoVITS

&#x1f680;数字人的未来&#xff1a;数字人对话系统 Linly-Talker 克隆语音 GPT-SoVITS https://github.com/Kedreamix/Linly-Talker 2023.12 更新 &#x1f4c6; 用户可以上传任意图片进行对话 2024.01 更新 &#x1f4c6; 令人兴奋的消息&#xff01;我现在已经将强…

Vue | (六)使用Vue脚手架(下)| 尚硅谷Vue2.0+Vue3.0全套教程

文章目录 &#x1f4da;Vue 中的自定义事件&#x1f407;使用方法&#x1f407;案例练习&#x1f407;TodoList案例优化 &#x1f4da;全局事件总线&#x1f407;使用方法&#x1f407;案例练习&#x1f407;TodoList案例优化 &#x1f4da;消息订阅与发布&#x1f407;使用方法…

嵌入式C语言(一)

最初我是golang出生&#xff0c;当时做项目的时候java、c、js、python都折腾过&#xff0c;但是关于c语言的接触&#xff0c;基本上都停留在大一的那个暑假。 后面一个项目需要写驱动&#xff0c;再到后门需要做DFX&#xff0c;再到我打开内核的源码&#xff0c;我一脸懵逼&am…

改进 RAG:自查询检索

原文地址&#xff1a;Improving RAG: Self Querying Retrieval 2024 年 2 月 11 日 让我们来解决构建 RAG 系统时的一个大问题。 我们不能依赖语义搜索来完成每个检索任务。只有当我们追求单词的含义和意图时&#xff0c;语义搜索才有意义。 But in case&#xff0c;我们正…

【读文献】DynamicBind生成式模型预测蛋白配体复合物

published at nature communication (2024.01.24) code link paper link 摘要 尽管在预测静态蛋白质结构方面取得了重大进展&#xff0c;但蛋白质的内在动态性&#xff0c;受到配体调节&#xff0c;对于理解蛋白质功能和促进药物发现至关重要。 传统的对接方法&#xff0c;常…

LCR 172. 统计目标成绩的出现次数

解题思路&#xff1a;二分查找 题解一 class Solution {public int countTarget(int[] scores, int target) {// 搜索右边界 rightint i 0, j scores.length - 1;while(i < j) {int m (i j) / 2;if(scores[m] < target) i m 1;else j m - 1;}int right i;// 若数…

wpf 简单实验 数据更新 列表更新

1.概要 1.1 需求 一个列表提供添加修改删除的功能&#xff0c;添加和修改的内容都来自一个输入框 1.2 要点 DisplayMemberPath"Zhi"列表.ItemsSource datalist;(列表.SelectedItem ! null)(列表.SelectedItem as A).Zhi 内容.Text;datalist.Remove((列表.Selec…

9.网络游戏逆向分析与漏洞攻防-游戏网络架构逆向分析-接管游戏连接服务器的操作

内容参考于&#xff1a;易道云信息技术研究院VIP课 上一个内容&#xff1a;游戏底层功能对接类GameProc的实现 码云地址&#xff08;master 分支&#xff09;&#xff1a;https://gitee.com/dye_your_fingers/titan 码云版本号&#xff1a;44c54d30370d3621c1e9ec3d7fa1e2a0…

IDEA配置有道翻译插件

目录 安装Translation插件有道云配置翻译APIIDEA配置有道翻译引擎 关于IDEA Translation插件中有道智云&#xff08;有道翻译&#xff09;应用ID&#xff0c;密钥申请教程 安装Translation插件 File -> Settings ->Plugins ->搜索Translation ->insatll 有道云…