Python3爬虫关于识别点触点选验证码的实例讲解


Posted in Python onJuly 30, 2020

上一节我们实现了极验验证码的识别,但是除了极验其实还有另一种常见的且应用广泛的验证码,比较有代表性的就是点触验证码。

可能你对这个名字比较陌生,但是肯定见过类似的验证码,比如 12306,这就是一种典型的点触验证码,如图所示:

Python3爬虫关于识别点触点选验证码的实例讲解

我们需要直接点击图中符合要求的图,如果所有答案均正确才会验证成功,如果有一个答案错误,验证就会失败,这种验证码就可以称之为点触验证码。

另外还有一个专门提供点触验证码服务的站点,叫做 TouClick,其官方网站为:https://www.touclick.com/,本节就以它为例讲解一下此类验证码的识别过程。

1. 本节目标

本节我们的目标是用程序来识别并通过点触验证码的验证。

2. 准备工作

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

3. 了解点触验证码

TouClick 官方网站的验证码样式如图 8-19 所示:

Python3爬虫关于识别点触点选验证码的实例讲解

和 12306 站点有相似之处,不过这次是点击图片中的文字,不是图片了,另外还有各种形形色色的点触验证码,其交互形式可能略有不同,但基本原理都是类似的。

接下来我们就来统一实现一下此类点触验证码的识别过程。

4. 识别思路

此种验证码的如果依靠图像识别的话识别难度非常之大。

例如就 12306 来说,其识别难点有两个点,第一点是文字识别,如图 8-20 所示:

Python3爬虫关于识别点触点选验证码的实例讲解

如点击图中所有的漏斗,“漏斗”二字其实都经过变形、放缩、模糊处理了,如果要借助于前面我们讲的 OCR 技术来识别,识别的精准度会大打折扣,甚至得不到任何结果。第二点是图像的识别,我们需要将图像重新转化文字,可以借助于各种识图接口,可经我测试识别正确结果的准确率非常低,经常会出现匹配不正确或匹配不出结果的情况,而且图片本身的的清晰度也不够,所以识别难度会更大,更何况需要同时识别出八张图片的结果,且其中几个答案需要完全匹配正确才能验证通过,综合来看,此种方法基本是不可行的。

再拿 TouClick 来说,如图所示:

Python3爬虫关于识别点触点选验证码的实例讲解

我们需要从这幅图片中识别出植株二字,但是图片的背景或多或少会有干扰,导致 OCR 几乎不会识别出结果,有人会说,直接识别白色的文字不就好了吗?但是如果换一张验证码呢?如图 8-22 所示:

Python3爬虫关于识别点触点选验证码的实例讲解

这张验证码图片的文字又变成了蓝色,而且还又有白色阴影,识别的难度又会大大增加。

那么此类验证码就没法解了吗?答案当然是有,靠什么?靠人。

靠人解决?那还要程序做什么?不要急,这里说的人并不是我们自己去解,在互联网上存在非常多的验证码服务平台,平台 7×24 小时提供验证码识别服务,一张图片几秒就会获得识别结果,准确率可达 90% 以上,但是就需要花点钱来购买服务了,毕竟平台都是需要盈利的,不过不用担心,识别一个验证码只需要几分钱。

在这里我个人比较推荐的一个平台是超级鹰。

其提供的服务种类非常广泛,可识别的验证码类型非常多,其中就包括此类点触验证码。

另外超级鹰平台同样支持简单的图形验证码识别,如果 OCR 识别有难度,同样可以用本节相同的方法借助此平台来识别,下面是此平台提供的一些服务:

英文数字,提供最多20位英文数字的混合识别

中文汉字,提供最多7个汉字的识别

纯英文,提供最多12位的英文的识别

纯数字,提供最多11位的数字的识别

任意特殊字符,提供不定长汉字英文数字、拼音首字母、计算题、成语混合、 集装箱号等字符的识别

坐标选择识别,如复杂计算题、选择题四选一、问答题、点击相同的字、物品、动物等返回多个坐标的识别

而本节我们需要解决的就是属于最后一类,坐标多选识别的情况,我们需要做的就是将验证码图片提交给平台,然后平台会返回识别结果在图片中的坐标位置,接下来我们再解析坐标模拟点击就好了。

原理非常简单,下面我们就来实际用程序来实验一下。

5. 注册账号

在开始之前,我们需要先注册一个超级鹰账号并申请一个软件ID,注册页面链接为:https://www.chaojiying.com/user/reg/,注册完成之后还需要在后台开发商中心添加一个软件ID,最后一件事就是充值一些题分,充值多少可以根据价格和识别量自行决定。

6. 获取API

做好上面的准备工作之后我们就可以开始用程序来对接验证码的识别了。

首先我们可以到官方网站下载对应的 Python API,链接为:https://www.chaojiying.com/api-14.html,但是此 API 是Python2 版本的,是用 Requests 库来实现的,我们可以简单更改几个地方即可将其修改为 Python3 版本。

修改之后的API如下:

import requests
from hashlib import md5
class Chaojiying(object):
    def __init__(self, username, password, soft_id):
        self.username = username
        self.password = md5(password.encode('utf-8')).hexdigest()
        self.soft_id = soft_id
        self.base_params = {
            'user': self.username,
            'pass2': self.password,
            'softid': self.soft_id,
        }
        self.headers = {
            'Connection': 'Keep-Alive',
            'User-Agent': 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)',
        }
    def post_pic(self, im, codetype):
        """
        im: 图片字节
        codetype: 题目类型 参考 http://www.chaojiying.com/price.html
        """
        params = {
            'codetype': codetype,
        }
        params.update(self.base_params)
        files = {'userfile': ('ccc.jpg', im)}
        r = requests.post('http://upload.chaojiying.net/Upload/Processing.php', data=params, files=files, 
        headers=self.headers)
        return r.json()
    def report_error(self, im_id):
        """
        im_id:报错题目的图片ID
        """
        params = {
            'id': im_id,
        }
        params.update(self.base_params)
        r = requests.post('http://upload.chaojiying.net/Upload/ReportError.php', data=params, headers=self.headers)
        return r.json()

这里定义了一个 Chaojiying 类,其构造函数接收三个参数,分别是超级鹰的用户名、密码以及软件ID,保存好以备使用。

接下来是最重要的一个方法叫做 post_pic(),这里需要传入图片对象和验证码的代号,该方法会将图片对象和相关信息发给超级鹰的后台进行识别,然后将识别成功的 Json 返回回来。

另一个方法叫做 report_error(),这个是发生错误的时候的回调,如果验证码识别错误,调用此方法会返还相应的题分。

接下来我们以 TouClick 的官网为例来进行演示点触验证码的识别过程,链接为:http://admin.touclick.com/,如果没有注册账号可以先注册一个。

7. 初始化

首先我们需要初始化一些变量,如 WebDriver、Chaojiying对象等等,代码实现如下:

EMAIL = 'cqc@cuiqingcai.com'
PASSWORD = ''
# 超级鹰用户名、密码、软件ID、验证码类型
CHAOJIYING_USERNAME = 'Germey'
CHAOJIYING_PASSWORD = ''
CHAOJIYING_SOFT_ID = 893590
CHAOJIYING_KIND = 9102
class CrackTouClick():
    def __init__(self):
        self.url = 'http://admin.touclick.com/login.html'
        self.browser = webdriver.Chrome()
        self.wait = WebDriverWait(self.browser, 20)
        self.email = EMAIL
        self.password = PASSWORD
        self.chaojiying = Chaojiying(CHAOJIYING_USERNAME, CHAOJIYING_PASSWORD, CHAOJIYING_SOFT_ID)

这里的账号和密码请自行修改。

8. 获取验证码

接下来的第一步就是完善相关表单,然后模拟点击呼出验证码,此步非常简单,代码实现如下:

def open(self):
    """
    打开网页输入用户名密码
    :return: None
    """
    self.browser.get(self.url)
    email = self.wait.until(EC.presence_of_element_located((By.ID, 'email')))
    password = self.wait.until(EC.presence_of_element_located((By.ID, 'password')))
    email.send_keys(self.email)
    password.send_keys(self.password)
def get_touclick_button(self):
    """
    获取初始验证按钮
    :return:
    """
    button = self.wait.until(EC.element_to_be_clickable((By.CLASS_NAME, 'touclick-hod-wrap')))
    return button

在这里 open() 方法负责填写表单,get_touclick_button() 方法则是获取验证码按钮,随后触发点击即可。

接下来我们需要类似上一节极验验证码图像获取一样,首先获取验证码图片的位置和大小,随后从网页截图里面截取相应的验证码图片就好了。代码实现如下:

def get_touclick_element(self):
    """
    获取验证图片对象
    :return: 图片对象
    """
    element = self.wait.until(EC.presence_of_element_located((By.CLASS_NAME, 'touclick-pub-content')))
    return element
def get_position(self):
    """
    获取验证码位置
    :return: 验证码位置元组
    """
    element = self.get_touclick_element()
    time.sleep(2)
    location = element.location
    size = element.size
    top, bottom, left, right = location['y'], location['y'] + size['height'], location['x'], location['x'] + size[
        'width']
    return (top, bottom, left, right)
def get_screenshot(self):
    """
    获取网页截图
    :return: 截图对象
    """
    screenshot = self.browser.get_screenshot_as_png()
    screenshot = Image.open(BytesIO(screenshot))
    return screenshot
def get_touclick_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_touclick_image() 方法即为从网页截图中截取对应的验证码图片,其中验证码图片的相对位置坐标由 get_position() 方法返回得到,最后我们得到的是一个 Image 对象。

9. 识别验证码

随后我们调用 Chaojiying 对象的 post_pic() 方法即可把图片发送给超级鹰后台,在这里发送的图像是字节流格式,代码实现如下:

image = self.get_touclick_image()
bytes_array = BytesIO()
image.save(bytes_array, format='PNG')
# 识别验证码
result = self.chaojiying.post_pic(bytes_array.getvalue(), CHAOJIYING_KIND)
print(result)

这样运行之后 result 变量就是超级鹰后台的识别结果,可能运行需要等待几秒,毕竟后台还有人工来完成识别。

返回的结果是一个 Json,如果识别成功后一个典型的返回结果类似如下:

{'err_no': 0, 'err_str': 'OK', 'pic_id': '6002001380949200001', 'pic_str': '132,127|56,77', 'md5': '1f8e1d4bef8b
11484cb1f1f34299865b'}

其中 pic_str 就是识别的文字的坐标,是以字符串形式返回的,每个坐标都以 | 分隔,所以接下来我们只需要将其解析之后再模拟点击即可,代码实现如下:

def get_points(self, captcha_result):
    """
    解析识别结果
    :param captcha_result: 识别结果
    :return: 转化后的结果
    """
    groups = captcha_result.get('pic_str').split('|')
    locations = [[int(number) for number in group.split(',')] for group in groups]
    return locations
 
def touch_click_words(self, locations):
    """
    点击验证图片
    :param locations: 点击位置
    :return: None
    """
    for location in locations:
        print(location)
        ActionChains(self.browser).move_to_element_with_offset(self.get_touclick_element(), location[0], 
        location[1]).click().perform()
        time.sleep(1)

在这里我们用 get_points() 方法将识别结果变成了列表的形式,最后 touch_click_words() 方法则通过调用 move_to_element_with_offset() 方法依次传入解析后的坐标,然后点击即可。

这样我们就可以模拟完成坐标的点选了,运行效果如图所示:

Python3爬虫关于识别点触点选验证码的实例讲解

最后我们需要做的就是点击提交验证的按钮等待验证通过,再点击登录按钮即可成功登录,后续实现在此不再赘述。

这样我们就借助于在线验证码平台完成了点触验证码的识别,此种方法也是一种通用方法,用此方法来识别 12306 等验证码也是完全相同的原理。

10. 本节代码

本节代码地址为:https://github.com/Python3WebSpider/CrackTouClick。

11. 结语

本节我们通过在线打码平台辅助完成了验证码的识别,这种识别方法非常强大,几乎任意的验证码都可以识别,如果遇到难题,借助于打码平台无疑是一个极佳的选择。

Python 相关文章推荐
Python中变量交换的例子
Aug 25 Python
对于Python编程中一些重用与缩减的建议
Apr 14 Python
Python基于二分查找实现求整数平方根的方法
May 12 Python
总结python爬虫抓站的实用技巧
Aug 09 Python
python安装模块如何通过setup.py安装(超简单)
May 05 Python
Python + selenium + requests实现12306全自动抢票及验证码破解加自动点击功能
Nov 23 Python
python实现的按要求生成手机号功能示例
Oct 08 Python
简单了解python调用其他脚本方法实例
Mar 26 Python
Python如何实现爬取B站视频
May 20 Python
python3.7 openpyxl 在excel单元格中写入数据实例
Sep 01 Python
使用Python中tkinter库简单gui界面制作及打包成exe的操作方法(二)
Oct 12 Python
Python绘制K线图之可视化神器pyecharts的使用
Mar 02 Python
Python3爬虫关于识别检验滑动验证码的实例
Jul 30 #Python
Python3爬虫中识别图形验证码的实例讲解
Jul 30 #Python
Python3以GitHub为例来实现模拟登录和爬取的实例讲解
Jul 30 #Python
Python如何实现线程间通信
Jul 30 #Python
Python如何输出警告信息
Jul 30 #Python
Python设计密码强度校验程序
Jul 30 #Python
详解Pandas 处理缺失值指令大全
Jul 30 #Python
You might like
Cannot modify header information错误解决方法
2008/10/08 PHP
THINKPHP3.2使用soap连接webservice的解决方法
2017/12/13 PHP
PHP实现浏览器中直接输出图片的方法示例
2018/03/14 PHP
对textarea框的代码调试,而且功能上使用非常方便,酷
2006/06/30 Javascript
javascript 日期时间函数(经典+完善+实用)
2009/05/27 Javascript
jQuery编辑器KindEditor4.1.4代码高亮显示设置教程
2013/03/01 Javascript
jQuery 借助插件Lavalamp实现导航条动态美化效果
2013/09/27 Javascript
jquery 隐藏与显示tr标签示例代码
2014/06/06 Javascript
必备的JS调试技巧汇总
2016/07/20 Javascript
Nodejs 发送Post请求功能(发短信验证码例子)
2017/02/09 NodeJs
详解ECMAScript6入门--Class对象
2017/04/27 Javascript
javascript代码优化的8点总结
2018/01/29 Javascript
Vue+Webpack完美整合富文本编辑器TinyMce的方法
2018/11/30 Javascript
微信小程序自定义带价格显示日历效果
2018/12/29 Javascript
浅谈vue加载优化策略
2019/03/19 Javascript
解决vue 单文件组件中样式加载问题
2019/04/24 Javascript
Nodejs实现WebSocket代码实例
2020/05/19 NodeJs
js实现鼠标点击飘爱心效果
2020/08/19 Javascript
JavaScript实现网页下拉菜单效果
2020/11/20 Javascript
详解如何在vue+element-ui的项目中封装dialog组件
2020/12/11 Vue.js
使用Python的Flask框架实现视频的流媒体传输
2015/03/31 Python
解读Python编程中的命名空间与作用域
2015/10/16 Python
使用Python设计一个代码统计工具
2018/04/04 Python
python对list中的每个元素进行某种操作的方法
2018/06/29 Python
Django中的forms组件实例详解
2018/11/08 Python
Python常见数字运算操作实例小结
2019/03/22 Python
Python匿名函数及应用示例
2019/04/09 Python
python实现趣味图片字符化
2019/04/30 Python
python修改FTP服务器上的文件名
2019/09/11 Python
python tkinter图形界面代码统计工具
2019/09/18 Python
基于Pytorch版yolov5的滑块验证码破解思路详解
2021/02/25 Python
html5菜单折纸效果
2014/04/22 HTML / CSS
中国电子产品外贸网站:MiniIntheBox
2017/02/06 全球购物
俄罗斯卫浴采暖及维修用品超级市场:Dkrussia
2020/05/12 全球购物
结婚保证书
2015/01/16 职场文书
Python中re模块的元字符使用小结
2022/04/07 Python