python版 —— 验证码校验 打码兔平台的使用介绍

2018-07-27  本文已影响0人  Py_Explorer

1. 背景

验证码(CAPTCHA)的全称是全自动区分计算机和人类的图灵测试(Completely Automated Public Turing Test to tell Computers and Humans Apart),是一种用于区分人与计算机自动程序的挑战应答系统测试。CAPTCHA可通过设置一些人类很容易执行而自动程序很难完成的任务来区分人类和自动程序。
CAPTCHA经常被用来阻止自动程序使用博客来影响搜索引擎排名、签署电子邮箱帐户发送垃圾邮件或参与网上投票等。
通常,CAPTCHA是一个轻微扭曲的字母数字字符图像文件,人通常可以很容易读取图像中的字符。而自动程序能够识别该内容包含一个图像,但不知道是什么图像。考虑到弱视群体,一些CAPTCHA使用音频文件。在这样一个系统中,人可以听到一个字母或短句并打出他所听到的,从而证明他不是自动程序。

2. 环境

3. 打码兔平台的使用流程

3.1. 原理

3.2. 流程

4. 自定义方法

# 对打码兔平台提供的代码进行了一些改动,更加适应本软件的需求
import hashlib
import urllib.request
import urllib 
import json
import base64
import requests
# md5加密字符串  
def md5str(str):
    m = hashlib.md5(str.encode(encoding="utf-8"))
    return m.hexdigest()


# md5加密byte
def md5(byte):
    m = hashlib.md5(byte)
    return m.hexdigest()


# 打码兔API类
class DamatuApi():
    # 关联的开发者帐号
    # 此帐号关联的验证码类型,默认是1~8位随机英文和数字组合
    ID = '51773'  # 开发者帐号 软件的AppID
    KEY = 'fbfb9022a1499b0c6436f223f98b714e'  # 开发者帐号 软件的key,   我的软件 生成的Key

    # 打码平台host
    HOST = 'http://api.dama2.com:7766/app/'

    def __init__(self, username, password, limitCount):
        self.username = username
        self.password = password
        # 限制每个实例请求验证码的次数,防止意外,导致请求验证码过多,消费不可控
        self.limitCount = limitCount
        # 用于统计验证码请求次数
        self.count = 0

    # 计算用户签名,按照一定的规则对 key和userName 进行加密
    def getSign(self, param=b''):
        return (md5(bytes(self.KEY, encoding="utf8") +  bytes(self.username, encoding="utf8") + param))[:8]

    # 获得加密后的密钥 key , userName, password
    def getPwd(self):
        return md5str(self.KEY + md5str(md5str(self.username) + md5str(self.password)))

    # 向打码平台提交请求
    def post(self, urlPath, formData = {}):
        '''
        :param urlPath: 用于构造url,向不同的地址请求不同的数据
        :param formData: 提交的数据
        :return:
        '''
        url = self.HOST + urlPath
        try:
            response = requests.request(method='post',
                                    url=url,
                                    data=formData,
                                    timeout=60
                                    )
            print(f"text = {response.text}")
            # 余额,balance, text = {"ret":0,"balance":"9957","sign":"2428e5f9"}
            # 验证码,captcha, text = {"ret":0,"id":558899326,"result":"230876","sign":"8d55bdb1"}
            return response.text
        except Exception as e:
            print(f"postRequest error. exception = {e}, urlPath = {urlPath}, formData = {formData}")
        return {"ret": -1}

    # 查询余额 return 是正数为余额, 如果为负数 则为错误码
    def getBalance(self):
        data = {
            'appID': self.ID,
            'user': self.username,
            'pwd': self.getPwd(),
            'sign': self.getSign()
        }
        res = self.post('d2Balance', data)
        jres = json.loads(res)
        if jres['ret'] == 0:
            return (True, jres['balance'])
        else:
            return (False, jres['ret'])

    # 上传验证码图片
    def decode(self, filePath, type):
        '''
        :param filePath:  验证码图片路径 如d:/1.jpg
        :param type: 验证码类型,查看  http://wiki.dama2.com/index.php?n=ApiDoc.Pricedesc
        :return: 元组,result[0] = True为成功,False为错误码
        '''
        if self.count >= self.limitCount:
            print(f"decode: 请求验证码数量超过限制自定义数量。")
            return (False, False)

        # 拿到验证码图片的数据
        f = open(filePath, 'rb')
        fdata = f.read()
        filedata = base64.b64encode(fdata)
        f.close()
        data = {
            'appID': self.ID,
            'user': self.username,
            'pwd': self.getPwd(),
            'type': type,
            'fileDataBase64': filedata,
            'sign': self.getSign(fdata)
        }
        res = self.post('d2File', data)
        jres = json.loads(res)
        self.count += 1
        if jres['ret'] == 0:
            # 注意这个json里面有ret,id,result,cookie,根据自己的需要获取
            return (True, jres['result'])
        else:
            return (False, jres['ret'])

    # url地址打码,提供验证码链接
    def decodeUrl(self, url, type):
        '''
        :param url: 验证码地址
        :param type: 验证码类型 http://wiki.dama2.com/index.php?n=ApiDoc.Pricedesc
        :return: 元组,result[0] = True为成功,False为错误码
        '''
        if self.count >= self.limitCount:
            print(f"decodeUrl: 请求验证码数量超过限制自定义数量。")
            return (False, False)

        data = {
            'appID': self.ID,
            'user': self.username,
            'pwd': self.getPwd(),
            'type': type,
            'url': urllib.parse.quote(url),
            'sign': self.getSign(url.encode(encoding="utf-8"))
        }
        res = self.post('d2Url', data)
        jres = json.loads(res)
        self.count += 1
        if jres['ret'] == 0:
            # 注意这个json里面有ret,id,result,cookie,根据自己的需要获取
            return (True, jres['result'])
        else:
            return (False, jres['ret'])

    # 报错,暂时先不关心。 参数id(string类型)由上传打码函数的结果获得 return 0为成功 其他见错误码
    def reportError(self, id):
        data = {
            'appID': self.ID,
            'user': self.username,
            'pwd': self.getPwd(),
            'id': id,
            'sign': self.getSign(id.encode(encoding="utf-8"))
        }
        res = self.post('d2ReportError', data)
        res = str(res, encoding="utf-8")
        jres = json.loads(res)
        return jres['ret']

5. 嵌入代码中使用

验证码截图,请参考文章:http://blog.csdn.net/zwq912318834/article/details/78605486

# 打码兔API类 实例化,参数是打码兔用户账号和密码. 最后一个是限制验证码的次数,防止出故障时,无限刷验证码,消费过大
# dmt=DamatuApi("test","test")
# 目前本软件验证码定位为: 1~8位数字英文混搭, 题分21分
dmt = DamatuApi("ancode", "ancode2017", 200)
# 先查看余额是否充足
# BalanceRes = (True, '9931')    
balanceRes = dmt.getBalance()   # 查询余额
if balanceRes[0] == True and int(balanceRes[1]) > 0:
    # 余额充足, 可以放心爬取
    print(f"main: balanceRes = {balanceRes}")
    # 开始爬取数据
    #......
# 部分代码
# 通过Image处理图像,截取验证码图片
imgCaptcha.save('clawerImgs/captcha.png')

# 将验证码送往打码兔进行解码
# codeRes = (True, 'FMAE')  
# 56 代表验证码类型,1~8位数字英文组合
# 参考:http://wiki.dama2.com/index.php?n=ApiDoc.Pricedesc
damatuRes = DamatuInstance.decode('clawerImgs/captcha.png', 56)
while(damatuRes[0] == False):
    if damatuRes[1] == False:
        raise Exception(f"打码兔超出自定义最大限制数,终止软件.     damatuRes = {damatuRes}")
    else:
        # 验证码请求失败,查询下余额,看是否充足
        balanceRes = DamatuInstance.getBalance()
        if balanceRes[0] == True and int(balanceRes[1]) > 0:
            damatuRes = DamatuInstance.decode('clawerImgs/captcha.png', 56)
            time.sleep(2)
        else:
            # 打码兔 余额不足, 抛出异常,终止软件
            raise Exception(f"打码兔余额不足,或者超出自定义最大限制数,终止软件. balanceRes = {balanceRes}")

# 点击提交按钮
input = browser.find_element_by_xpath("//div[@class='input']/input[@id='J_CodeInput']")
input.clear()
input.send_keys(damatuRes[1])
print(f"发送验证码为: damatuRes = {damatuRes}")
time.sleep(3)
# 提交
submit = browser.find_element_by_xpath("//button[@id='J_submit']")
submit.click()
time.sleep(5)  
################################################
# 处理完,提交完验证码之后,回归到主页面,继续抓取商品
browser.switch_to.default_content()
time.sleep(2)
browser.refresh()   # 处理完之后进行页面刷新
time.sleep(5)
# 然后重新检测验证码是否存在,如果还在就循环重复处理,如果不在,下面这个流程也不会继续
captchaHandler(browser, DamatuInstance)

感谢支持:https://blog.csdn.net/zwq912318834/article/details/78616462

上一篇下一篇

猜你喜欢

热点阅读