使用Python + Selenium破解滑块验证码
在前面一篇博客《使用 Python + Selenium 打造浏览器爬虫》中,我介绍了 Selenium 的基本用法和爬虫开发过程中经常使用的一些小技巧,利用这些写出一个浏览器爬虫已经完全没有问题了。看了前一篇博客,可能有人会有疑惑,浏览器爬虫的优势感觉并不比传统爬虫多多少啊,特别是通过遍历页面元素来获取爬虫数据的方式和传统爬虫解析 HTML 文档结构的方式如出一辙。为了体现浏览器爬虫的优越性,我特意准备了这篇博客,来看看如果要破解滑块验证码,浏览器爬虫比传统爬虫要容易多少。
一、滑块验证码简述
有爬虫,自然就有反爬虫,就像病毒和杀毒软件一样,有攻就有防,两者彼此推进发展。反爬技术历经多年,从最简单的检测 UserAgent 或者 Referrer 等头部,到限制访问频率封 IP 等手段,到关键路径的行为识别,到前端页面的混淆和加密,到目前最流行的验证码技术,可以说,为了防止网络上大量爬虫的肆意妄为,特别是一些垃圾机器人,技术人员真的是绞尽脑汁。但是道高一尺魔高一丈,直到目前为止,也并没有完全无懈可击的反爬方案。
目前最流行的反爬技术是验证码,几乎所有网站的注册页面都会用到验证码技术,为了防止爬虫自动注册,批量生成垃圾账号。验证码技术从一诞生,就是黑客们最感兴趣的话题,验证码的英文为 CAPTCHA(Completely Automated Public Turing test to tell Computers and Humans Apart),翻译成中文就是 全自动区分计算机和人类的公开图灵测试,它是一种可以区分用户是计算机还是人的测试,只要能通过 CAPTCHA 测试,该用户就可以被认为是人类。使用计算机模拟人类的行为一直以来都是黑客们最热衷的事情,也是黑客们梦寐以求的理想。所以验证码技术从一提出,就有大量的人尝试破解,其实这些人并不是为了制造垃圾爬虫,他们只是相信计算机可以和人一样,阿西莫夫的机器人世界在未来是可能的。
最初的验证码只是一张图片,图片上显示扭曲变形的文字和数字,这样的验证码通过图像处理和识别的技术可以达到很高的识别率。后来验证码技术又在图片上加入了各种干扰项,并且将字符粘连在一起,增加了字符切割和识别的难度,但是很快人们就想出了很多种不同的去噪方法,并使用骨架算法切割粘连字符,还有些人提出使用机器学习算法来切割字符。和图片验证码类似的是语音验证码,不过这种验证码只是在表现形式上有所区别,实质上和图片并没有太大的变化,采用语音识别技术破解也不是难事。而且语音和图片比起来缺乏交互,花样要少很多,识别难度也要低一些,所以只有在给盲人或者对颜色分辨有障碍的人提供服务时才可能会使用语音验证码,一般情况下使用的比较少。在静态的图片验证码被破解之后,又出现了动态的图片验证码,将字符动态的显示在 gif 动画上,不过这也没什么用,通过图像识别技术一样可以破解,实在破解不了的,还可以通过网上一些廉价的打码平台来人肉识别。
打码平台的诞生可以说是验证码领域的一件大事,它虽然不是什么高科技,只是把全世界廉价的劳动力汇集在了一起,就这样,再复杂的验证码都不在话下。这虽然不是什么光荣的事,但是它推动了验证码技术的发展,交互式验证码被开发出来。传统的图片验证码采用一问一答的形式,只要答案正确,就认为验证通过,它并不关心答案是怎么来的,所以出现了一些人工打码平台,你提供一个问题,它们提供一个答案,仅此而已。如果不仅仅关注答案的正确性,还将提交答案的过程记录下来,通过分析提交答案的过程,完全可以识别出这是不是一个人在操作,这就是交互式验证码的基本思路。这种验证码很难通过打码平台来破解,因为你必须对着浏览器,使用鼠标对验证码进行一系列的交互操作。
最耳熟能详的交互验证码莫过于 12306 的了,这种验证码叫做 图中点选 式验证码,同时提供多个图像,让用户根据条件点击选择。也有些验证码是同时显示 N 个变形的汉字让你选,原理与 12306 的类似,但这种验证码以其极差的用户体验遭到很多人的唾弃,这也是大多数产品不愿意选用的一个原因。滑块验证码 比图中点选体验好很多,它只需要用户使用鼠标将滑块从某个位置拖动到另一个位置即可。程序通过记录用户拖动滑块的轨迹,这一串的轨迹数据采用模式识别的手段就可以判断出这是否是真人在操作。最简单的滑块验证码是用户拖动滑块从左拖到右即可,后来又出现了 拼图式 的滑块,滑块作为图的一部分,然后背景图中有一个缺口刚好和滑块相同形状,需要用户将滑块拖到缺口中拼成一张完整的图片。现在比较流行的滑块验证码有 极验 和 网易云易盾,本篇博客以极验的滑块验证码为例,其他的滑块验证码原理是类似的。
最新的交互式验证码甚至只需要用户点击一个按钮即可验证,不需要做任何其他的操作,譬如 极验的第三代行为验证技术 和 易盾的智能无感知验证码。这种验证码的破解方式和滑块验证码不一样,我目前也没有太多的了解,后面有时间再研究研究吧。
最后不得不说的是,还有一种交互式验证码为短信或电话验证码,通过将验证码以短信的形式发送到你的手机,或者使用语音机器人自动打电话播报验证码,更有甚者,需要用户自己编辑短信将验证码发送到某个号码。对于这种验证码我认为并不能算作是 CAPTCHA,因为它利用的是用户的有限资源(手机号)这个客观限制,而并非是从技术角度来区分人和机器人的区别。如果某个人拥有大量的手机号(其实,黑产中确实也有专门养卡卖卡的),这种验证手段就形同虚设了。
二、破解思路
目前,极验正在推广其第三代行为验证技术,滑块验证码貌似已经没有前两年那么流行了,不过仍然有很多网站还在使用滑块验证码。譬如我这篇博客就以 春秋航空的会员注册页面 为例。
好了,上面讲了那么多,下面就开始我们的破解之旅吧。
2.1 传统爬虫
如果采用传统爬虫的方式来破解,首先我们需要测试下正常验证的情况是什么样的。在 Chrome 浏览器中按 F12 打开开发者工具,然后拖动滑块到正确位置,可以观察到 Network 面板发送的 Ajax 请求。
可以看到这个请求的参数非常复杂,每个参数的含义也完全没有头绪,如果要破解这个验证码,则必须模拟发送这个请求,这个请求的每个参数都必须弄清楚,于是我们在代码中搜索发送这个请求的地方。但事实上到这里我们就遇到了困难,ajax.php 这个请求根本就搜不到,甚至在浏览器中下 XHR 断点也不行(很显然它并不是一个 Ajax 请求),这是因为极验的核心代码经过了代码混淆。
这样的代码也就只有机器能读懂,大多数人肯定是直接放弃了。不过网上也有大量的分析文章,如果你感兴趣可以自己研究下,譬如 Windows应用开发 的知乎专栏上就有几篇介绍 极验验证码破解的系列文章,还有 FanhuaandLuomu 的 这篇破解文章 也写得很好,推荐。
我在这里跳过对混淆代码的分析,总结下破解这样的滑块验证码的思路:
1、捕获所有关键请求;
2、分析调试混淆的代码,弄懂每个请求每个参数的含义,其中肯定会有一个参数,是拖动滑块的轨迹;
3、验证码图片是打乱的,需要解析页面上的样式,并使用图像处理方法还原出原始图像;
4、根据原始的图像和滑块位置得到缺口的偏移量;
5、滑块轨迹的模拟;
6、如果参数有加密处理,还需要模拟它的加密过程;实在不行可以直接在代码里模拟执行页面上的 JS;
7、...
可见这里的工作量非常大,破解难度可想而知,而且混淆的代码随时可能会发布新的版本,一旦版本升级,参数都有可能发生变化,之前的所有分析工作都可能前功尽弃。
除非实在是迫不得已,我并不推荐传统的这种破解方法。首先这样的破解方法太脆弱,不够通用,随时可能失效;其次这样的破解工作费时费力,就算破解出来也得不到成就感和满足感,对程序员的打击太大,他可能再也不会玩第二次了(除非他是极客中的极客,就以破解混淆代码为乐)。所以,还是让我们来看看浏览器爬虫如何。
2.2 浏览器爬虫
由于浏览器爬虫完全是以人为第一视角,你所看到的,就是浏览器爬虫看到的,甚至,它能比你看到更多。我们可以大概的总结下浏览器爬虫的破解思路:
图像识别,找到滑块的位置和缺口的位置;
模拟鼠标拖动,将滑块拖到缺口位置;
没错,就两步。虽然其中会遇到一些坑,但真的就这两步。使用上一篇博客中介绍的 Selenium 技巧,可以很快的写下下面的代码:
chrome_options = webdriver.ChromeOptions()
chrome_options.add_argument("--start-maximized")
browser = webdriver.Chrome(
executable_path="./drivers/chromedriver.exe",
chrome_options=chrome_options
)
browser.get('https://account.ch.com/NonRegistrations-Regist')
Wait(browser, 60).until(
Expect.visibility_of_element_located((By.CSS_SELECTOR, "div[data-target='account-login']"))
)
email = browser.find_element_by_css_selector("div[data-target='account-login']")
email.click()
Wait(browser, 60).until(
Expect.visibility_of_element_located((By.ID, "emailRegist"))
)
register = browser.find_element_by_id("emailRegist")
register.click()
offset = get_gap_offset(browser)
drag_and_drop(browser, offset)
关键就在于最后两个方法 get_gap_offset() 和 drag_and_drop(),下面就来看下这两个方法的实现。
三、验证码图片处理
审查验证码图片元素,可以看到下面这样的 HTML 代码:
<div class="gt_cut_fullbg_slice" style="background-image: url('https://static.geetest.com/pictures/gt/3999642ae/3999642ae.webp'); background-position: -157px -58px;"></div>
这样的代码一共有 52 行,每一个 div 都是 10px * 58px 的小块。我们打开这个 background-image 对应的图片可以看出这是一张乱序的图片,这里的 background-position 用于显示出正确的图片。在代码上面,可以发现和这里完全类似的代码,background-position 都完全一样,只是 background-image 不一样,我们打开对应的图片,也是乱序的,但和上一张图片对比,可以猜测出,这是带有缺口的背景图片。
<div class="gt_cut_bg_slice" style="background-image: url('https://static.geetest.com/pictures/gt/3999642ae/bg/fbdb18152.webp'); background-position: -157px -58px;"></div>
一个很自然的想法就是把这两张乱序的图片根据 background-position 重组成两张看得懂的图片,然后对比两张图片,得到缺口的偏移量,然后将缺口偏移量减去滑块偏移量,就可以得到要拖动的偏移量。如下图所示:
其中滑块的偏移量可以通过下面的代码得到(其中,left: 12px 就是滑块的偏移量):
<div class="gt_slice gt_show" style="left: 12px; background-image: url('https://static.geetest.com/pictures/gt/e6e7e0440/slice/fa2d5bbd8.png'); width: 55px; height: 55px; top: 20px;"></div>
这里需要用到一点点图像处理的知识,我们采用大名鼎鼎的 Pillow,Pillow 是 Python 里的图像处理库(PIL:Python Image Library),在开始之前,可以先看下它的官网教程,这里有一份中文文档也可以参考。计算缺口偏移量的关键代码如下:
def get_slider_offset(image_url, image_url_bg, css):
image_file = io.BytesIO(requests.get(image_url).content)
im = Image.open(image_file)
image_file_bg = io.BytesIO(requests.get(image_url_bg).content)
im_bg = Image.open(image_file_bg)
# 10*58 26/row => background image size = 260*116
captcha = Image.new('RGB', (260, 116))
captcha_bg = Image.new('RGB', (260, 116))
for i, px in enumerate(css):
offset = convert_css_to_offset(px)
region = im.crop(offset)
region_bg = im_bg.crop(offset)
offset = convert_index_to_offset(i)
captcha.paste(region, offset)
captcha_bg.paste(region_bg, offset)
diff = ImageChops.difference(captcha, captcha_bg)
return get_slider_offset_from_diff_image(diff)
代码很好理解,就是根据 css 将两张背景图片重新排序生成两张新图片,然后通过 ImageChops.difference() 方法得到两张图片的差值图像,最后通过差值图像得到缺口的偏移量。其中有一点要注意的是,Pillow 的 Image.open() 方法只支持文件,不支持 URL,所以将图片转换为 BytesIO 对象,BytesIO 和 StringIO 一样,是 Python 提供的在内存中操作 bytes 和 str 的类,并且和读写文件具有一致的接口。
这种计算缺口位置的方法需要解析页面源码以及图片的 CSS 样式,其实还有一种更简单的方法:在显示验证码图片时对浏览器进行截图,这个时候的图像是完整的背景图像;然后再点击滑块,这个时候滑块和缺口都会显示出来,再对浏览器进行截图;分析两次的截图也可以计算出拖动的偏移量。有兴趣的同学可以一试。
......
使用Python + Selenium破解滑块验证码的更多相关文章
- 使用 Python+Selenium 破解滑块验证码
开发工具 Python版本:3.6.4 相关模块: pillow模块: selenium模块: numpy模块: 以及一些Python自带的模块. 其他: chromedriver 环境搭建 安装 ...
- Python Selenium Cookie 绕过验证码实现登录
Python Selenium Cookie 绕过验证码实现登录 之前介绍过博客园的通过cookie 绕过验证码实现登录的方法.这里并不多余,会增加分析和另外一种方法实现登录. 1.思路介绍 1.1. ...
- python+selenium破解极验验证登录
1.前言: 目前很多网站会在正常的账号密码认证之外加一些验证码,以此来明确区分人/机行为,最典型的就是极验滑动验证.(如下图) 这里我们以简单实例说明如何实现自动校验类似验证. 2.步骤: 1)点击验 ...
- Python脚本破解图形验证码(tesserocr和pytesseract)
在学习之前,我们先了解OCR.tesseract.tesserocr.pytesseract和opencv这几个跟图片处理有关的库. OCR(Optical Character Recognition ...
- selenium破解数字验证码
搞了半天,总算弄出来了,识别率还可以,普通的数字验证码 from selenium import webdriver from PIL import Image import pytesseract ...
- python+selenium进行简单验证码获取
# _*_ coding:utf-8 _*_from PIL import Imagefrom selenium import webdriverimport pytesseractimport ti ...
- python+selenium识别图片验证码
import timeimport pytesseractfrom PIL import Image, ImageEnhancefrom selenium import webdriver url = ...
- python+selenium滑动式验证码解决办法
from selenium.webdriver import ActionChains action = ActionChains(driver) source=driver.find_element ...
- python3 破解 geetest(极验)的滑块验证码
Kernel_wu 快速学习的实践者 python3 破解 geetest(极验)的滑块验证码 from selenium import webdriver from selenium.webdriv ...
随机推荐
- 【Java】forward & redirect 的差异
1.从地址栏显示来说 forward是服务器请求资源,服务器直接访问目标地址的URL,把那个URL的响应内容读取过来,然后把这些内容再发给浏览器.浏览器根本不知道服务器发送的内容从哪里来的,所以它的地 ...
- 【iCore4 双核心板_ARM】例程十一:DMA实验——存储器到存储器的传输
实验原理: DAM(直接存储器访问)传输不需要占用CPU,可以在存储器至存储器实现高速的数据 传输.本实验采用DAM2控制器的数据流0,选用通道0进行数据传输.通过LED的颜色来 判断传输是否成功. ...
- Java多线程系列——线程池原理之 ThreadPoolExecutor
ThreadPoolExecutor 简介 ThreadPoolExecutor 是线程池类. 通俗的讲,它是一个存放一定数量线程的线程集合.线程池允许多个线程同时运行,同时运行的线程数量就是这个线程 ...
- Manager升职了
公司去年从每年七月份公布officer升职改成了每年四月份公布. 早上收到大头发给全公司的邮件,赫然发现Manager升了一级到VP,虽然是金融公司,但我司的VP好像会比银行多一点点福利,比如额外假期 ...
- java InputStream和OutputStream
InputStream类型 类 功能 构造器参数 如何使用 ByteArrayInputStream 允许将内存的缓冲区当做InputStreams使用 缓冲区,字节将从中取出 作为一种数据源:将其与 ...
- Git之右键没有Git Bash Here的解决办法
1.Win+R 打开运行输入regedit 回车打开注册表 2.找到[HKEY_CLASSES_ROOT\Directory\Background]. 3.在[Background]下如果没有[she ...
- 学习Mysql过程中拓展的其他技术栈:设置linux虚拟机的固定ip和克隆linux虚拟机
一.设置linux虚拟机的固定ip 1. 安装好虚拟机后在菜单栏选择编辑→ 虚拟网络编辑器,打开虚拟网络编辑器对话框,选择Vmnet8 Net网络连接方式,随意设置子网IP,点击NAT设置页面,查看子 ...
- ABBYY OCR技术教电脑阅读缅甸语(下)
文本行检测到之后,我们开始寻找单词和字母之间的间隙,这一次,我们运用了水平直方图,将大的间隙假设为单词之间的空隙,小的间隙理解为字母之间的空隙,检测缅甸文本中的空隙几乎没有出现问题,不像泰语,几乎没有 ...
- [Understanding] Compressive Sensing and Deep Model
低维模型与深度模型的殊途同归 有助理解核心,陌生概念需要加强理解. 对于做机器学习,和做图像视觉的研究者来说,过去的十年是非常激动人心的十年.以我个人来讲,非常有幸接触了两件事情: 第一件是压缩感知( ...
- .net 上传文件大小限制
1.ii. System.web下修改节点HttpRuntime为 <system.web> <compilation debug="true" targetFr ...