Amazon SP-API をpythonで動かしてみた【実コードあり】（part3 POST編　: feed API使ってみた）

この記事は、2022年8月18日に企業ブログで掲載したものの転記となります。


# ■□■ はじめに ■□■

part2 の続きなります。
前回のGETの処理は無事に動きましたか？
最初の１回動くまで結構苦戦しませんでしたか？　私はしました(泣)
ではpart3ではPOST編です。

GETと大体の流れは同じなのですがfeedは準備が多い・・・
そんなイメージでした。
ちなみにfeedAPIは要は更新処理を行うAPIで在庫情報だったり、売価情報だったりを
更新できたりします。
なにか更新を行うときには避けては通れないAPIですね。はい。


# ■□■ 準備 ■□■

part2 でGETまで動いているのであれば、これといった準備は不要です。


# ■□■ とりあえずコード化するまえに手で取得して見ましょう ■□■

っといきたいんですが、feedAPIを使う際はいろいろとステップがあるんです。

1. 基本更新はtsvにて行います　そのtsvファイルの読み込みを行います
1. /feeds/2021-06-30/documentsにてdocument_id,document_urlというものを取得します
1. 取得したdocument_urlの配下に１．のｔｓｖファイルを配置します
1. /feeds/2021-06-30/feeds　にて 2.のdocument_idを指定して更新指示をだします
1. feed APIは非同期処理なので更新ステータス待ちをします
1. 更新ステータスが完了になったら結果urlを返すので結果を取得します

ざっくりいうとこんな流れになります。
本パートのメインは2と4になります。(これがPOST処理です)
全部書くと超絶長くなるので、５，６(これはGET処理)に関しては割愛いたします m(_ _)m


## 2. /feeds/2021-06-30/documentsの実行

:::note
必要な情報です。

method : POST
url : https://sellingpartnerapi-fe.amazon.com/feeds/2021-06-30/documents
　　　　　　　　　　　→日本であればsellingpartnerapi-fe.amazon.com 部分は固定
x-amz-access-token：　取得したaccess_token
user-agent： ★適当に設定（サンプルでは　My Selling Tool/2.0 (Language=Java/1.8.0.221; Platform=Windows/10)　）
body : json形式
{
     'contentType': 'text/xml; charset=UTF-8'
}
:::

[Authorization]
![スクリーンショット 2022-07-01 15.48.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/344266/0613e539-28c4-d790-11fd-f6432bd6d61b.png)

[Headers]
![スクリーンショット 2022-07-01 15.48.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/344266/26d171ed-9edd-0a10-99db-1a7f9794dd11.png)

[body]
![スクリーンショット 2022-07-01 15.48.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/344266/e18730d5-5aa0-2117-4abf-9c1fd84321b8.png)

`</>code` はこんな感じです
![スクリーンショット 2022-07-01 15.49.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/344266/f8d5a3f1-32bd-e2e0-c209-39bb4efdd92f.png)


ご覧の通り設定項目が増えました・・・
設定項目のbodyの設定のところがドキュメントからだと読み取りづらく、結構悩んだ箇所でもあります。

# ■□■ コード化するとこんな感じになります ■□■

ではこれをpythonで書くとこうなります。
※あくまでサンプルなので環境にあわせて適時変更お願いいたします。

```python

def main() -> dict:
    method = 'POST'
    canonical_uri = '/feeds/2021-06-30/documents'

    access_token = '取得したaccess_token'

    request_parameters_dict = {
        'contentType': 'text/xml; charset=UTF-8'
    }

    request_parameters = json.dumps(request_parameters_dict)

    # AWS 認証情報を作成
    headers = make_post_authorization_header(
        method, canonical_uri, request_parameters, access_token)

    # リクエスト発行
    response = apicall_sp_api_post(
        canonical_uri, headers, request_parameters)

    # 実行制限にかからないように待機する
    time.sleep(20)

    return response


def apicall_sp_api_post(canonical_uri, headers, request_parameters):

    # リクエストデータを作成
    endpoint = 'https://sellingpartnerapi-fe.amazon.com' + canonical_uri
    request_url = endpoint + '?' + request_parameters

    # リクエストを送信
    response = requests.post(
        endpoint, data=request_parameters, headers=headers)
    response.raise_for_status()

    result_dict = json.loads(response.text)

    return result_dict



def make_post_authorization_header(method, canonical_uri, request_parameters, access_token):

    # ************* REQUEST VALUES *************
    service = 'execute-api'
    host = 'sellingpartnerapi-fe.amazon.com'
    region = 'us-west-2'
    user_agent = "My Selling Tool/2.0 (Language=Java/1.8.0.221; Platform=Windows/10)"
    content_type = 'application/json'

    access_key = '★AWS のアクセスキーを記載'
    secret_key = '★AWS のシークレットキーを記載'

    # Key derivation functions. See:
    # http://docs.aws.amazon.com/general/latest/gr/signature-v4-examples.html#signature-v4-examples-python
    def sign(key, msg):
        return hmac.new(key, msg.encode("utf-8"), hashlib.sha256).digest()

    def getSignatureKey(key, date_stamp, regionName, serviceName):
        kDate = sign(('AWS4' + key).encode('utf-8'), date_stamp)
        kRegion = sign(kDate, regionName)
        kService = sign(kRegion, serviceName)
        kSigning = sign(kService, 'aws4_request')
        return kSigning

    if access_key is None or secret_key is None:
        raise Exception('No access key is available.')

    t = datetime.datetime.utcnow()
    amz_date = t.strftime('%Y%m%dT%H%M%SZ')
    date_stamp = t.strftime('%Y%m%d')

    # ************* TASK 1: CREATE A CANONICAL REQUEST *************
    # http://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html

    canonical_querystring = ''
    canonical_headers = 'content-type:' + content_type + '\n' + \
        'host:' + host + '\n' + 'x-amz-date:' + amz_date + '\n'

    signed_headers = 'content-type;host;x-amz-date'

    payload_hash = hashlib.sha256(
        request_parameters.encode('utf-8')).hexdigest()

    canonical_request = method + '\n' + canonical_uri + '\n' + canonical_querystring + \
        '\n' + canonical_headers + '\n' + signed_headers + '\n' + payload_hash

    # ************* TASK 2: CREATE THE STRING TO SIGN*************
    # Match the algorithm to the hashing algorithm you use, either SHA-1 or
    # SHA-256 (recommended)
    algorithm = 'AWS4-HMAC-SHA256'
    credential_scope = date_stamp + '/' + region + \
        '/' + service + '/' + 'aws4_request'
    string_to_sign = algorithm + '\n' + amz_date + '\n' + credential_scope + \
        '\n' + \
        hashlib.sha256(canonical_request.encode('utf-8')).hexdigest()

    # ************* TASK 3: CALCULATE THE SIGNATURE *************
    # Create the signing key using the function defined above.
    signing_key = getSignatureKey(secret_key, date_stamp, region, service)

    signature = hmac.new(signing_key, (string_to_sign).encode(
        'utf-8'), hashlib.sha256).hexdigest()

    # ************* TASK 4: ADD SIGNING INFORMATION TO THE REQUEST *************
    authorization_header = algorithm + ' ' + 'Credential=' + access_key + '/' + \
        credential_scope + ', ' + 'SignedHeaders=' + \
        signed_headers + ', ' + 'Signature=' + signature

    headers = {'x-amz-access-token': token,
               'user-agent': user_agent,
               'Content-Type': content_type,
               'X-Amz-Date': amz_date,
               'Authorization': authorization_header}

    return headers

```

これでfeedDocumentIdとurlが取得できます。

## 3. 取得したdocument_urlの配下に１．のｔｓｖファイルを配置

urlの場所にデータをputするのは単純にこんな感じです。

```python
def put_document_file ():
        content_type = "text/xml; charset=UTF-8"
        url = '★取得したurl'
        filename = '★tsvファイル(私の場合はaws lambdaを使っていたので/tmpとかに一旦配置しました)'

        # ファイルをオープン
        with open('/tmp/' + filename) as f:
            upload_data = f.read()

        # リクエストを発行
        response = requests.put(
            url,
            data=upload_data,
            headers={'Content-Type': content_type}
        )
        response.raise_for_status()
```

## 4. /feeds/2021-06-30/feeds　にて 2.のdocument_idを指定して更新指示をだします

ここはPostmanでもできるんですが、ながれは2と一緒なのでもうそのままコードで行きます
先程のurlとbodyの値がちょっと変わるだけですね。

```python
def main() -> dict:

    method = 'POST'
    canonical_uri = '/feeds/2021-06-30/feeds'
    access_token = '★取得したアクセストークン'

    feed_document_id = '★取得したfeed_document_id'

    # 並び順が違うとエラーになるので注意
    # おそらくこの順番で書かないとエラーになります
    # https://developer-docs.amazon.com/sp-api/docs/reports-api-v2021-06-30-reference#createreportspecification
    request_parameters_dict = {
        'feedType': 'POST_FLAT_FILE_PRICEANDQUANTITYONLY_UPDATE_DATA',
        'marketplaceIds': ['A1VC38T7YXB528'],
        'inputFeedDocumentId': feed_document_id
    }

    request_parameters = json.dumps(request_parameters_dict)

    # AWS 認証情報を作成
    headers = make_post_authorization_header(
        method, canonical_uri, request_parameters, access_token)

    # リクエスト発行
    response = apicall_sp_api_post(
        canonical_uri, headers, request_parameters)

    # 実行制限にかからないように待機する
    time.sleep(20)
```

make_post_authorization_headerとapicall_sp_api_postは使いまわしになります。


その後は
5. `GET /feeds/2021-06-30/feeds/{feedId}` にて更新ステータスを確認して
6. `GET /feeds/2021-06-30/documents/{feedDocumentId}`　にて更新レポートがあるurｌを取得して更新レポートをGETする。

という流れになります。
ここはGETになるので、part2のモジュールを使うことができます。

以上がAmazon SP-APIのPOST編になります。
一度処理が通るとあとは同じようにできるのですが、はじめの一回はかなりハテナマークが頭をよぎりながらやっていました。


このページを見た方に少しでも役に立てれば嬉しいです。
ではよいAmazon SP-APIライフを