...
这次,我们再来介绍一种更灵活更强大的全模拟点击破解方案,整体思路就是将全部的验证码图片进行识别,并根据识别结果对 ReCAPTCHA 验证码进行模拟点击,从而最终通过验证码。
ReCAPTCHA 介绍
在开始之前,我这里先简单提下什么是 ReCAPTCHA,可能大家见的不多,因为这个验证码在国内并没有那么普及。
...
ReCAPTCHA 也有体验地址,大家可以打开 https://www.google.com/recaptcha/api2/demo 查看,打开之后,我们可以发现有如上图所示的内容,然后点选图片进行识别即可。
整体识别思路
其实我们看,这种验证码其实主要就是一些格子的点选,我们只要把一些相应的位置点击对了,最后就能验证通过了。
...
下面我们来借助 YesCaptcha 来试试识别过程。
YesCaptcha
在使用之前我们需要先注册下这个网站,网站地址是 https://yescaptcha.com/,注册个账号之后大家可以在后台获取一个账户密钥,也就是 ClientKey,保存备用。
...
OK,知道了代号之后,模拟点击就好办多了吧,我们用一些模拟点击操作就可以完成了。
代码基础实现
行,那有了基本思路之后,那我们就开始用 Python 实现下整个流程吧,这里我们就拿 https://www.google.com/recaptcha/api2/demo 这个网站作为样例来讲解下整个识别和模拟点击过程。
识别封装
首先我们对上面的任务 API 实现一下封装,来先写一个类:
...
接着我们定义了一个 create_task 方法,接收两个参数,第一个参数 image_base64_string
就是验证码图片对应的 Base64 编码,第二个参数 question_id
就是要识别的目标是什么,这里就是将整个请求用 requests 模拟实现了,最后返回对应的 JSON 内容的响应结果就好了。
基础框架
OK,那么接下来我们来用 Selenium 来模拟打开这个实例网站,然后模拟点选来触发验证码,接着识别验证码就好了。
...
这里我们先在构造方法里面初始化了一个 Chrome 浏览器操作对象,然后调用对应的 get 方法打开实例网站,接着声明了一个 WebDriverWait 对象和 CaptchaResolver 对象,以分别应对节点查找和验证码识别操作,留作备用。
iframe 切换支持
接着,下一步我们就该来模拟点击验证码的入口,来触发验证码了对吧。
...
这样的话,我们只需要调用 switch_to_captcha_content_iframe 就能查找验证码图片里面的内容,调用 switch_to_captcha_entry_iframe 就能查找验证码入口里面的内容。
触发验证码
OK,那么接下来的一步就是来模拟点击验证码的入口,然后把验证码触发出来了对吧,就是模拟点击这里:
...
点击完了之后我们再调用 switch_to_captcha_content_iframe 切换到验证码本身对应的 iframe 里面,查找验证码本身对应的节点是否加载出来了,如果加载出来了,那么就证明触发成功了。
找出识别目标
OK,那么现在验证码可能就长这样子了:
那接下来我们要做的就是两件事了,一件事就是把匹配目标找出来,就是上图中的加粗字体,第二件事就是把验证码进行保存,然后转成 Base64 编码,提交给 CaptchaResolver 来识别。
...
通过调用这个方法,我们就能得到上图中类似 traffic lights 的内容了。
验证码识别
接着,我们对验证码图片进行下载,然后转 Base64 进行识别吧,整体代码如下:
...
接着我们把这个图片发给 YesCaptcha 进行识别就好了。
Base64 编码
接着,我们把这张图片转下 Base64 编码,定义这样一个方法:
...
这里值得注意的是,由于 API 对图片大小有限制,如果是 3x3 的图片,那么我们需要将图片调整成 300x300 才可以,如果是 4x4 的图片,那么我们需要将图片调整成 450x450,所以这里我们先调用了 Image 的 resize 方法调整了大小,接着再转成了 Base64 编码。
问题 ID 处理
那问题 ID 怎么处理呢?通过 API 文档 https://yescaptcha.atlassian.net/wiki/spaces/YESCAPTCHA/pages/18055169 我们可以看到如下映射表:
...
最后将上面的参数直接调用 CaptchaResovler 对象的 create_task 方法就能得到识别结果了。
模拟点击
得到结果之后,我们知道返回结果的 objects 就是需要点击的验证码格子的列表,下面进行模拟点击即可:
...
当然我们也可以通过执行 JavaScript 来对每个节点进行模拟点击,效果是类似的。
这样我们就可以实现验证码小图的逐个识别了。
小图识别
等等,在识别过程中还发现了一个坑,那就是有时候我们点击完一个小格子之后,这个小格子就消失了!然后在原来的小格子的位置出现了一个新的小图,我们需要对新出现的图片进行二次识别才可以。
...
二次识别的步骤也是一样的,我们需要将小格子对应的图片单独获取其 url,然后下载下来,接着调整大小并转化成 Base64 编码,然后发给 API,API 会通过一个 hasObject 字段告诉我们这个小图里面是否包含我们想要识别的目标内容,如果是,那就接着点击,然后递归进行下一次检查,如果不是,那就跳过。
点击验证
好,那么有了上面的逻辑,我们就能完成整个 ReCAPTCHA 的识别和点选了。
...
代码块 |
---|
def get_verify_button(self) -> WebElement: verify_button = self.wait.until(EC.presence_of_element_located( (By.CSS_SELECTOR, '#recaptcha-verify-button'))) return verify_button # after all captcha clicked verify_button: WebElement = self.get_verify_button() if verify_button.is_displayed: verify_button.click() time.sleep(3) |
校验结果
点击完了之后,我们可以尝试检查网页变化,看看有没有验证成功。
...
如果返回结果是 False,我们可以进一步递归调用上述逻辑进行二次识别,直到识别成功即可。
运行结果
最后看看运行效果吧:
看起来整个运行过程还是比较稳定的。
代码
以上代码可能比较复杂,这里我将代码进行了规整,然后放到 GitHub 上了,大家如有需要可以自取:https://github.com/Python3WebSpider/RecaptchaResolver
注册地址
最后需要说明一点,上面的验证码服务是收费的,每验证一次可能花一定的点数,比如识别一次 3x3 的图要花 10 点数,而充值一块钱就能获得 1000 点数,所以识别一次就一分钱,还是比较便宜的。
...