版本比较

密钥

  • 该行被添加。
  • 该行被删除。
  • 格式已经改变。

...

这次,我们再来介绍一种更灵活更强大的全模拟点击破解方案,整体思路就是将全部的验证码图片进行识别,并根据识别结果对 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 点数,所以识别一次就一分钱,还是比较便宜的。

...