更新 Python 代码以签署 Amazon API 请求以使用 Python 3
Updating Python code for signing an Amazon API request to work with Python 3
我有一些工作代码可以从 Amazon API 获取一些销售数据。它在 python 2.7 中工作,但我无法将其更新到 python 3.6 错误来自签署请求。我的代码如下:
import base64, hashlib, hmac, urllib
from time import gmtime, strftime
from requests import request
import xml.etree.ElementTree as ET
def get_timestamp():
"""Return correctly formatted timestamp"""
return strftime("%Y-%m-%dT%H:%M:%SZ", gmtime())
def calc_signature(method, domain, URI, request_description, key):
"""Calculate signature to send with request"""
sig_data = method + '\n' + \
domain.lower() + '\n' + \
URI + '\n' + \
request_description
hmac_obj = hmac.new(key, sig_data, hashlib.sha256)
digest = hmac_obj.digest()
return urllib.parse.quote(base64.b64encode(digest), safe='-_+=/.~')
SECRET_KEY = 'xxxxxxxxxxxxxxxxxxxxx'
AWS_ACCESS_KEY = 'xxxxxxxxxxxxxxxxxxx'
SELLER_ID = 'xxxxxxxxxxxxxxxxxx'
MARKETPLACE_ID = marketplace_id
version = '2013-09-01'
Action = 'ListOrders'
SignatureMethod = 'HmacSHA256'
SignatureVersion = '2'
Timestamp = get_timestamp()
Version = '2013-09-01'
CreatedAfter = '2017-05-26T23:00:57Z'
URI = '/Orders/2013-09-01'
domain = 'mws.amazonservices.com'
proto = 'https://'
method = 'POST'
payload = {
'AWSAccessKeyId': AWS_ACCESS_KEY,
'Action': Action,
'SellerId': SELLER_ID,
'SignatureVersion': SignatureVersion,
'Timestamp': Timestamp,
'Version': Version,
'SignatureMethod': SignatureMethod,
'CreatedAfter': CreatedAfter,
'MarketplaceId.Id.1': MARKETPLACE_ID
}
request_description = '&'.join(['%s=%s' % (k, urllib.parse.quote(payload[k], safe='-_.~').encode('utf-8')) for k in sorted(payload)])
sig = calc_signature(method, domain, URI, request_description, SECRET_KEY)
url = '%s%s?%s&Signature=%s' % \
(proto+domain, URI, request_description, urllib.parse.quote(sig))
headers = {
'Host': domain,
'Content-Type': 'text/xml',
'x-amazon-user-agent': 'python-requests/1.2.0 (Language=Python)'
}
r = request(method, url, headers=headers)
print(r.status_code)
print(r.text)
错误是由 calc_signature 方法抛出的(同样,这在 python 2.7 中有效)告诉我:
TypeError: key: expected bytes or bytearray, but got 'str'
在做了一些挖掘之后,我能够通过将 .encode('utf-8') 添加到行来更正该问题:
hmac_obj = hmac.new(key.encode('utf-8'), sig_data.encode('utf-8'), hashlib.sha256)
在将编码应用于 hmac.new 的输入后,代码现在可以运行,但是,亚马逊拒绝了请求并说:
<Error>
<Type>Sender</Type>
<Code>InvalidParameterValue</Code>
<Message>Value b'2' for parameter SignatureVersion is invalid.
</Message>
</Error>
我无法找出 hmac 模块在 python 版本之间发生了什么变化,导致它计算出不正确的签名。
不将其添加为评论,因为它可能超出字符数限制。
问题出在下面一行:
request_description = '&'.join(
['%s=%s' % (
k,
urllib.parse.quote(payload[k], safe='-_.~').encode('utf-8')
)
for k in sorted(payload)]
)
您正在对 header 值进行编码,然后在字符串插值 ('%s=%s' % (...)
) 期间将它们转换回 str
。例如,对于 SignatureVersion
,这会导致
>>> str('2'.encode('ascii'))
"b'2'"
>>> '%s' % '2'.encode('ascii')
"b'2'"
这是 AWS 报告的内容(使用 HTML 个实体;'
表示 '
)。
我不确定你想用这条线达到什么目的。您可以通过
对有效载荷进行编码
urllib.parse.urlencode(payload).encode('ASCII')
(参考this answer。)
我有一些工作代码可以从 Amazon API 获取一些销售数据。它在 python 2.7 中工作,但我无法将其更新到 python 3.6 错误来自签署请求。我的代码如下:
import base64, hashlib, hmac, urllib
from time import gmtime, strftime
from requests import request
import xml.etree.ElementTree as ET
def get_timestamp():
"""Return correctly formatted timestamp"""
return strftime("%Y-%m-%dT%H:%M:%SZ", gmtime())
def calc_signature(method, domain, URI, request_description, key):
"""Calculate signature to send with request"""
sig_data = method + '\n' + \
domain.lower() + '\n' + \
URI + '\n' + \
request_description
hmac_obj = hmac.new(key, sig_data, hashlib.sha256)
digest = hmac_obj.digest()
return urllib.parse.quote(base64.b64encode(digest), safe='-_+=/.~')
SECRET_KEY = 'xxxxxxxxxxxxxxxxxxxxx'
AWS_ACCESS_KEY = 'xxxxxxxxxxxxxxxxxxx'
SELLER_ID = 'xxxxxxxxxxxxxxxxxx'
MARKETPLACE_ID = marketplace_id
version = '2013-09-01'
Action = 'ListOrders'
SignatureMethod = 'HmacSHA256'
SignatureVersion = '2'
Timestamp = get_timestamp()
Version = '2013-09-01'
CreatedAfter = '2017-05-26T23:00:57Z'
URI = '/Orders/2013-09-01'
domain = 'mws.amazonservices.com'
proto = 'https://'
method = 'POST'
payload = {
'AWSAccessKeyId': AWS_ACCESS_KEY,
'Action': Action,
'SellerId': SELLER_ID,
'SignatureVersion': SignatureVersion,
'Timestamp': Timestamp,
'Version': Version,
'SignatureMethod': SignatureMethod,
'CreatedAfter': CreatedAfter,
'MarketplaceId.Id.1': MARKETPLACE_ID
}
request_description = '&'.join(['%s=%s' % (k, urllib.parse.quote(payload[k], safe='-_.~').encode('utf-8')) for k in sorted(payload)])
sig = calc_signature(method, domain, URI, request_description, SECRET_KEY)
url = '%s%s?%s&Signature=%s' % \
(proto+domain, URI, request_description, urllib.parse.quote(sig))
headers = {
'Host': domain,
'Content-Type': 'text/xml',
'x-amazon-user-agent': 'python-requests/1.2.0 (Language=Python)'
}
r = request(method, url, headers=headers)
print(r.status_code)
print(r.text)
错误是由 calc_signature 方法抛出的(同样,这在 python 2.7 中有效)告诉我:
TypeError: key: expected bytes or bytearray, but got 'str'
在做了一些挖掘之后,我能够通过将 .encode('utf-8') 添加到行来更正该问题:
hmac_obj = hmac.new(key.encode('utf-8'), sig_data.encode('utf-8'), hashlib.sha256)
在将编码应用于 hmac.new 的输入后,代码现在可以运行,但是,亚马逊拒绝了请求并说:
<Error>
<Type>Sender</Type>
<Code>InvalidParameterValue</Code>
<Message>Value b'2' for parameter SignatureVersion is invalid.
</Message>
</Error>
我无法找出 hmac 模块在 python 版本之间发生了什么变化,导致它计算出不正确的签名。
不将其添加为评论,因为它可能超出字符数限制。
问题出在下面一行:
request_description = '&'.join(
['%s=%s' % (
k,
urllib.parse.quote(payload[k], safe='-_.~').encode('utf-8')
)
for k in sorted(payload)]
)
您正在对 header 值进行编码,然后在字符串插值 ('%s=%s' % (...)
) 期间将它们转换回 str
。例如,对于 SignatureVersion
,这会导致
>>> str('2'.encode('ascii'))
"b'2'"
>>> '%s' % '2'.encode('ascii')
"b'2'"
这是 AWS 报告的内容(使用 HTML 个实体;'
表示 '
)。
我不确定你想用这条线达到什么目的。您可以通过
对有效载荷进行编码urllib.parse.urlencode(payload).encode('ASCII')
(参考this answer。)