Summer
1先舷、起點AppConfig.client.addUser(image, imageType, groupId, userId, options)
調用人臉注冊api,從addUser進入
def addUser(self, image, image_type, group_id, user_id, options=None):
"""
人臉注冊
"""
options = options or {}
data = {}
data['image'] = image
data['image_type'] = image_type
data['group_id'] = group_id
data['user_id'] = user_id
data.update(options)
return self._request(self.__userAddUrl, json.dumps(data, ensure_ascii=False), {
'Content-Type': 'application/json',
})
詳解:由此可見我們需要傳四個必要參數(shù)image, image_type, group_id, user_id险掀,一個可選參數(shù)options魁索;
接下來是 return self._request(self.__userAddUrl, json.dumps(data, ensure_ascii=False)履植,根據(jù)__userAddUrl和序列化字段以及Content-Type來發(fā)送請求烫堤,看一下__userAddUrl是__userAddUrl = 'https://aip.baidubce.com/rest/2.0/face/v3/faceset/user/add',也就是說是向這個路由發(fā)送請求昧谊;
2刽虹、那就要看一下_request請求是怎么發(fā)送的,點進去看
def _request(self, url, data, headers=None):
"""
self._request('', {})
"""
try:
result = self._validate(url, data) # 判斷路由和數(shù)據(jù)是否有效
if result != True:
return result
authObj = self._auth() # 實例化認證對象
params = self._getParams(authObj) # 實例化參數(shù)對象呢诬,傳入的是認證對象涌哲,這里的參數(shù)就是access token
data = self._proccessRequest(url, params, data, headers) # 對請求進行處理,傳入路由尚镰,參數(shù)阀圾,數(shù)據(jù)和報頭
headers = self._getAuthHeaders('POST', url, params, headers) # 對報頭進行認證,我猜測這里面有反爬措施
# 通過實例化的對象發(fā)送請求狗唉,攜帶的參數(shù)為路由初烘,數(shù)據(jù),access token參數(shù)分俯,報頭肾筐,認證,超時時間缸剪;
# 這里的__connectTimeout是自定義的連接超時時間吗铐,__socketTimeout是通過打開的連接傳輸數(shù)據(jù)的超時時間,單位都為ms
response = self.__client.post(url, data=data, params=params,
headers=headers, verify=False, timeout=(
self.__connectTimeout,
self.__socketTimeout,
), proxies=self._proxies
)
obj = self._proccessResult(response.content) # 最后返回的結果對象
# 如果云端未注冊并且調用接口的返回錯誤碼是110,表示Access Token失效杏节,這時就要再次進行認證
if not self._isCloudUser and obj.get('error_code', '') == 110:
authObj = self._auth(True)
params = self._getParams(authObj)
response = self.__client.post(url, data=data, params=params,
headers=headers, verify=False, timeout=(
self.__connectTimeout,
self.__socketTimeout,
), proxies=self._proxies
)
obj = self._proccessResult(response.content)
# 拋異常唬渗,連接超時
except (requests.exceptions.ReadTimeout,requests.exceptions.ConnectTimeout) as e:
return {
'error_code': 'SDK108',
'error_msg': 'connection or read data timeout',
}
return obj
3典阵、至此調用基本結束,接下來看里面用到的幾個方法:
- ._auth()
def _auth(self, refresh=False):
"""
api access auth镊逝,獲得access_token
"""
#未過期
if not refresh:
# 設置超時時間為一個月
tm = self._authObj.get('time', 0) + int(self._authObj.get('expires_in', 0)) - 30
if tm > int(time.time()):
return self._authObj
# 如果access token過期壮啊,重新獲取,傳入?yún)?shù):grant_type蹋半,client_id他巨,client_secret和設置超時時間
obj = self.__client.get(self.__accessTokenUrl, verify=False, params={
'grant_type': 'client_credentials',
'client_id': self._apiKey,
'client_secret': self._secretKey,
}, timeout=(
self.__connectTimeout,
self.__socketTimeout,
), proxies=self._proxies).json()
self._isCloudUser = not self._isPermission(obj)
obj['time'] = int(time.time()) # 按照當前時間重置access token超時時間
self._authObj = obj
return obj # 返回認證過access token的對象
- ._getParams(authObj)
# 得到的參數(shù)就是access_token,認證校驗傳輸都要攜帶
def _getParams(self, authObj):
"""
api request http url params
"""
params = {}
if self._isCloudUser == False:
params['access_token'] = authObj['access_token']
return params
- ._proccessRequest(url, params, data, headers)
def _proccessRequest(self, url, params, data, headers):
"""
參數(shù)處理:在參數(shù)中加入語言减江、版本,比如python3
"""
params['aipSdk'] = 'python'
params['aipVersion'] = self.__version
return data
- ._getAuthHeaders('POST', url, params, headers)
def _getAuthHeaders(self, method, url, params=None, headers=None):
"""
api request http headers
"""
headers = headers or {}
params = params or {}
if self._isCloudUser == False: # 不是云端用戶捻爷,返回報頭
return headers
urlResult = urlparse(url) # 解析路由
for kv in urlResult.query.strip().split('&'): # 對路由進行切分辈灼,取出有用的值把它賦給參數(shù)
if kv:
k, v = kv.split('=')
params[k] = v
# UTC timestamp,獲取時間戳也榄,主機號巡莹,版本等信息,保證唯一性
timestamp = datetime.datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%SZ')
headers['Host'] = urlResult.hostname
headers['x-bce-date'] = timestamp
version, expire = '1', '1800'
# 1 Generate SigningKey
# 拼接簽名鑰匙甜紫,使用hmac(HMAC是密鑰相關的哈希運算消息認證碼降宅,HMAC運算利用哈希算法,以一個密鑰和一個消息為輸入囚霸,生成一個消息摘要作為輸出)
# 還要使用hashlib.sha256進行二次加密
val = "bce-auth-v%s/%s/%s/%s" % (version, self._apiKey, timestamp, expire)
signingKey = hmac.new(self._secretKey.encode('utf-8'), val.encode('utf-8'),
hashlib.sha256
).hexdigest()
# 2 Generate CanonicalRequest
# 2.1 Genrate CanonicalURI
canonicalUri = quote(urlResult.path)
# 2.2 Generate CanonicalURI: not used here
# 2.3 Generate CanonicalHeaders: only include host here
canonicalHeaders = []
for header, val in headers.items():
canonicalHeaders.append(
'%s:%s' % (
quote(header.strip(), '').lower(),
quote(val.strip(), '')
)
)
canonicalHeaders = '\n'.join(sorted(canonicalHeaders))
# 2.4 Generate CanonicalRequest
canonicalRequest = '%s\n%s\n%s\n%s' % (
method.upper(),
canonicalUri,
'&'.join(sorted(urlencode(params).split('&'))),
canonicalHeaders
)
# 3 Generate Final Signature
signature = hmac.new(signingKey.encode('utf-8'), canonicalRequest.encode('utf-8'),
hashlib.sha256
).hexdigest()
headers['authorization'] = 'bce-auth-v%s/%s/%s/%s/%s/%s' % (
version,
self._apiKey,
timestamp,
expire,
';'.join(headers.keys()).lower(),
signature
)
return headers # 最后頭部攜帶信息進行傳輸腰根,其中最重要的是signature,為此進行了兩次大加密拓型,大加密中又有兩次小加密额嘿。不得不說,重要信息在傳輸過程中一定要慎重A哟臁2嵫!
- ._proccessResult(response.content)
def _proccessResult(self, content):
"""
formate result压固,格式化返回結果
"""
if sys.version_info.major == 2:
return json.loads(content) or {}
else:
return json.loads(content.decode()) or {}