前几天,有同窗来问,想接入腾讯AI开放平台,怎么搞?而后就扔了个连接:ai.qq.com/doc/auth.sh…php
打开一看,接口鉴权?签名算法?第一反应是,是否是给错连接了,接入个api还要那么麻烦?html
后来仔细看了看,的确是腾讯AI开放平台的官网,真没打错;git
原本是放到收费OCR那边的,可是想着这个边幅有点长,就单独弄一篇了,仅此而已;算法
既然如此,就那试试吧;api
(非广告贴,非广告贴)数组
既然要接入别人,那就先了解下这个平台是干吗的吧,进入官网;app
其中,情感分析是吸引jb眼光了,由于以前看到过相似的脚本,根据某人发的微博内容作情感分析,从而通知接收者要怎么处理,好玩,哈哈哈哈~dom
这种相似的平台,确定是有收费跟免费的,对于普通使用者来讲,免费就够了;函数
既然要接入,那就挑一个通用识别吧,点击下,跳转;post
会跳转到一个优图OCR的界面,直接点击免费试用,而后就登陆,注册,输入一大堆信息,这块就不介绍了;
最后根据要求执行,等建立应用完毕,最后网页会弹出两个信息:
App_ID XXX
App_Key XXX
复制代码
app_id请求的时候须要带上,app_key在弄签名的时候须要,这两个玩意很重要,固然也别泄露了;
而后点击通用OCR的查看文档,就跳转到这里
官网上介绍蛮全的,基本总结是这样:
规则 | 描述 |
---|---|
传输方式 | HTTPS |
请求方法 | POST |
字符编码 | 统一采用UTF-8编码 |
响应格式 | 统一采用JSON格式 |
接口鉴权 | 签名机制,详情请点击接口受权 |
参数名称 | 是否必选 | 数据类型 | 数据约束 | 示例数据 | 描述 |
---|---|---|---|---|---|
app_id | 是 | int | 正整数 | 1000001 | 应用标识(AppId) |
time_stamp | 是 | int | 正整数 | 1493468759 | 请求时间戳(秒级) |
nonce_str | 是 | string | 非空且长度上限32字节 | fa577ce340859f9fe | 随机字符串 |
sign | 是 | string | 非空且长度固定32字节 | B250148B284956EC5218D4B0503E7F8A 签名信息,详见接口鉴权 | |
image | 是 | string | 原始图片的base64编码数据(原图大小上限1MB,支持JPG、PNG、BMP格式) | ... | 待识别图片 |
这么看上,请求的时候,body要带上app_id、time_stamp、nonce_str、sign、image这几个参数,
其中,sign是最难搞的。。
这个是个重点,想看看介绍吧,不着急。。
腾讯AI开放平台HTTP API使用签名机制对每一个接口请求进行权限校验,对于校验不经过的请求,API将拒绝处理,并返回鉴权失败错误。
接口调用者在调用API时必须带上接口请求签名,其中签名信息由接口请求参数和应用密钥根据本文提供的签名算法生成。
那这个签名算法,怎么算?下面是官网给的步骤,也能够点击这里查看;
用于计算签名的参数在不一样接口之间会有差别,但算法过程固定以下4个步骤。
而后官网下面就是一个php的示范,嗯,不懂Php的,跪下吧~
固然,官方提供demo下载,Python用的是2.7版本(嗯,没看错),php是5.3.0版本及以上; 若是刚好用Python 3.X的,再次跪下吧。。最新一次更新时间是18年4月,都不弄一个Python 3.X的版本,大写的服~
demo的处理逻辑也很简单,就是获取请求须要的参数,发请求,完;
其实整个过程很简单,就是获取图片,按照要求的参数请求,而后获取响应便可,那咱们就一步步来撸代码吧;
看回上面的请求参数,要这几个参数:app_id、time_stamp、nonce_str、sign、image
参数名称 | 是否必选 | 数据类型 | 数据约束 | 示例数据 | 描述 |
---|---|---|---|---|---|
app_id | 是 | int | 正整数 | 1000001 | 应用标识(AppId) |
time_stamp | 是 | int | 正整数 | 1493468759 | 请求时间戳(秒级) |
nonce_str | 是 | string | 非空且长度上限32字节 | fa577ce340859f9fe | 随机字符串 |
sign | 是 | string | 非空且长度固定32字节 | B250148B284956EC5218D4B0503E7F8A 签名信息,详见接口鉴权 | |
image | 是 | string | 原始图片的base64编码数据(原图大小上限1MB,支持JPG、PNG、BMP格式) | ... | 待识别图片 |
这里有几个须要实现的功能:
其实,重点仍是在第4那里,先同样同样来吧;
这个的话,没什么疑问,直接使用base64便可
def get_img_base64str(image):
"""
原始图片的base64编码数据
:param image:图片路径
:return:图片的base64数据
"""
with open(image,"rb") as f:
pic = f.read()
pic_base64 = base64.b64encode(pic)
return pic_base64
复制代码
固然,jb也不懂什么是base64,因而乎作了下笔记,会的同窗,直接跳过吧;
Base64是一种用64个字符来表示任意二进制数据的方法。
用记事本打开exe
、jpg
、pdf
这些文件时,都会看到一大堆乱码,由于二进制文件包含不少没法显示和打印的字符,
因此,若是要让记事本这样的文本处理软件能处理二进制数据,就须要一个二进制到字符串的转换方法。
Base64是一种最多见的二进制编码方法。
Base64的原理很简单,首先,准备一个包含64个字符的数组:
['A', 'B', 'C', ... 'a', 'b', 'c', ... '0', '1', ... '+', '/']
而后,对二进制数据进行处理,每3个字节一组,一共是3x8=24
bit,划为4组,每组正好6个bit:
这样获得4个数字做为索引,而后查表,得到相应的4个字符,就是编码后的字符串。
因此,Base64编码会把3字节的二进制数据编码为4字节的文本数据,长度增长33%,
好处是编码后的文本数据能够在邮件正文、网页等直接显示。
若是要编码的二进制数据不是3的倍数,最后会剩下1个或2个字节怎么办?Base64用\x00字节在末尾补足后,再在编码的末尾加上1个或2个=号,表示补了多少字节,解码的时候,会自动去掉。
Python内置的base64能够直接进行base64的编解码:
import base64
print(base64.b64encode(b'binary\x00string'))
print(base64.b64decode(b'YmluYXJ5AHN0cmluZw=='))
结果:
b'YmluYXJ5AHN0cmluZw=='
b'binary\x00string'
复制代码
因为标准的Base64编码后可能出现字符+和/,在URL中就不能直接做为参数,因此又有一种"url safe"的base64编码,其实就是把字符+和/分别变成-和_:
import base64
print(base64.b64encode(b'i\xb7\x1d\xfb\xef\xff'))
print(base64.urlsafe_b64encode(b'i\xb7\x1d\xfb\xef\xff'))
print(base64.urlsafe_b64decode('abcd--__'))
运行的结果:
b'abcd++//'
b'abcd--__'
b'i\xb7\x1d\xfb\xef\xff'
复制代码
小小结:
Base64是一种任意二进制到文本字符串的编码方法,经常使用于在URL、Cookie、网页中传输少许二进制数据。
随机字符串的话,random跟string是跑不掉的,百度下看看;
Python里面生成随机数的方法就是random,因此使用以前要记得import random;
random.random()用于生成
描述 | 方法 |
---|---|
指定范围内的随机符点数 | random.uniform(20, 10) |
指定范围内的整数 | random.randint(12, 20) |
从指定范围内,按指定基数递增的集合 | random.randrange(0, 101, 2) 随机选取0到100的偶数 |
随机字符 | random.choice('abcdefg&#%^*f') |
多个字符中选取特定数量的字符 | random.sample('abcdefghij',3) |
多个字符中选取特定数量的字符组成新字符串 | string.join(random.sample(['a','b','c','d','e','f','g','h','i','j'], 3)).replace(" ","") |
随机选取字符串 | random.choice ( ['apple', 'pear', 'peach', 'orange', 'lemon'] ) |
洗牌/从新排序 | items = [1, 2, 3, 4, 5, 6] random.shuffle(items) |
顺便说起到:
random() 方法返回随机生成的一个实数,它在[0,1)范围内。
import random
print(random.random())
复制代码
string是Python内置的方法,这里就来分两部分介绍:
1)经常使用方法
因此,随机字符串的功能代码以下:
def get_random_str():
"""
随机字符串
:return:
rule就是小写字母+数字0-9组成的字符串,而后用random.sample获取16位
"""
rule = string.ascii_lowercase + string.digits
str = random.sample(rule, 16)
return "".join(str)
复制代码
由于官网要求是请求时间戳(秒级),所以直接使用time处理便可,无需额外加工
def get_time_stamp():
return str(int(time.time()))
复制代码
这个签名是最难的,再次贴一下官方的要求。。
用于计算签名的参数在不一样接口之间会有差别,但算法过程固定以下4个步骤。
这里要求将请求参数进行排序,那就先折腾一堆请求参数吧:
app_id="你的ID"
app_key="你的key"
file = '你的图片路径'
def ExecTecentAPI():
Req_Dict = {}
Req_Dict['app_id'] = app_id
Req_Dict["image"] = get_img_base64str(file)
Req_Dict['time_stamp'] = get_time_stamp()
Req_Dict['nonce_str'] = get_random_str()
#这样的话,Req_Dict已经准备好了,那就能够计算签名了
sign = gen_dict_md5(Req_Dict, app_key)
复制代码
有请求数据了,那就进行排序吧。按照要求,升序处理;
这里的话,采用sorted()函数,这里来介绍下sorted()
描述
sorted() 函数对全部可迭代的对象进行排序操做
与sort的区别
sort是应用在list上的方法,sorted能够对全部可迭代的对象进行排序的操做;
list的sort方法返回值的是对已经存在的列表进行操做,无返回值,而内建函数sorted方法返回
的是一个新的list,而不是在原来的基础上进行的操做;
复制代码
语法
sorted(iterable[, cmp[, key[, reverse]]])
返回值
返回从新排序的列表
排序实现
代码很简单,这样就能获取到升序后的字符串:
sort_dict = sorted(req_dict.items(), key=lambda item: item[0], reverse=False)
req_dict.items() #以列表返回可遍历的(键, 值) 元组数组。如[(key:value),(key:value)]的方式
reverse=False #降序
key=lambda item: item[0] #利用key来排序,item入口,item[0]是函数体,意思就是用每一个item的第一个内容
来排序,好比('app_id', '2108258706'),('time_stamp', '1536312440')
复制代码
按照官网描述,排序完,就先对参数拼接成字符串,而后编码,再把app_key拼接到字符串的末尾,获得新的字符串,可是嘛,为了方便,咱们先添加app.key吧
#这个app_key就是你的app_key,本身定义
sort_dict.append(('app_key', app_key))
复制代码
MD5加密的话,通常都用hashlib
sha = hashlib.md5()
复制代码
而后就到了拼接URL键值对
rawtext = urlencode(sort_dict).encode()
urlencode能够把key-value这样的键值对转换成咱们想要的格式,返回的是a=1&b=2这样的字符串
复制代码
而后就是获取拼接后的URL md5
sha.update(rawtext)
复制代码
而后把获得的MD5值转换成大写
md5text = sha.hexdigest().upper()
此时,md5text就是请求签名了
hash.digest() 返回摘要,做为二进制数据字符串值
hash.hexdigest() 返回摘要,做为十六进制数据字符串值
upper() 将字符串中的小写字母转为大写字母。
复制代码
最后,在请求头里面新增sign:
req_dict['sign'] = md5text
复制代码
这样的话,所须要的请求参数都获取到了,就发起请求吧
response = requests.post(url="https://api.ai.qq.com/fcgi-bin/ocr/ocr_generalocr",
data=Req_Dict,verify=False)
复制代码
最后执行的结果:
import requests
import base64
import time
import random,string
import hashlib
from urllib.parse import urlencode
app_id="2108258706"
app_key="dIX8rxJFymoHipm7"
file = 'test.png'
def ExecTecentAPI():
Req_Dict = {}
Req_Dict['app_id'] = app_id
Req_Dict["image"] = get_img_base64str(file)
Req_Dict['time_stamp'] = get_time_stamp()
Req_Dict['nonce_str'] = get_random_str()
sign = gen_dict_md5(Req_Dict, app_key)
response = requests.post(url="https://api.ai.qq.com/fcgi-bin/ocr/ocr_generalocr",data=Req_Dict,verify=False)
print(response.text)
def gen_dict_md5(req_dict, app_key):
try:
# 方法,先对字典排序,排序以后,写app_key,再urlencode
sort_dict = sorted(req_dict.items(), key=lambda item: item[0], reverse=False)
sort_dict.append(('app_key', app_key))
sha = hashlib.md5()
rawtext = urlencode(sort_dict).encode()
sha.update(rawtext)
md5text = sha.hexdigest().upper()
# 字典能够在函数中改写
if md5text:
req_dict['sign'] = md5text
return md5text
except Exception as e:
return None
def get_img_base64str(image):
"""
原始图片的base64编码数据
:param image:图片路径
:return:图片的base64数据
"""
with open(image,"rb") as f:
pic = f.read()
pic_base64 = base64.b64encode(pic)
return pic_base64
def get_time_stamp():
"""
获取请求时间戳(秒级)
:return:
"""
return str(int(time.time()))
def get_random_str():
"""
随机字符串
:return:
"""
rule = string.ascii_lowercase + string.digits
str = random.sample(rule, 16)
return "".join(str)
if __name__ == "__main__":
# 通用ocr
rest = ExecTecentAPI()
print(rest)
复制代码
对了,测试的二维码是这个:
嗯,腾讯的接入真的有点恶心的感受,识别率也没有太特别的地方,文中也没有什么特别的地方,都是涉及到一些基础知识,包括md5,sorted等功能;
最重要是,识别率并无明显的增加,对于百度不能处理的验证码,腾讯AI也不能处理,因此,仍是找收费打码平台吧,完~
谢谢你们~