Masataka Miki's Blog

すみません、わかりません。もっと勉強して改善します。

.NETでAWSのAPIを使う

      2018/03/28

VB.NETでAmazonのWEB APIを使った際の備忘録。

今回調べて分かったことは、
・URLのパラメータに、タイムスタンプ(GMT)と、シグネチャが必須となっている
・他にもパラメータの順番や使用可否など、細かいガイドラインがある。しかも国で違うらしい
・リクエストには制限がある(1回/秒、最大10回/秒?)ので、連続して実行する場合は、sleepをかける必要がある

リクエスト数の上限/秒 = 1 + (過去30日間の配送済み商品売上実績 / 460,000円)の小数点以下を四捨五入した数

・新品価格がうまく取れない場合もあるらしい(未確認)

以下、ASINから商品情報、在庫情報などを取得(ItemLookUp)するVBのサンプルコード

Public Class Form1

    Private Const AWS_ASSOCIATE_TAG As String = "xxxx-22"
    Private Const AWS_ACCESS_KEY_ID As String = "ABCDEFGHIJKLMNOPQRST"
    Private Const AWS_SECRET_KEY As String = "UVWXYZ1234567890abcdefghijklmnopqrstuvwx"

    Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click

        ' ASINを指定
        Dim asin As String = "B015IFTSMM"
        ' リクエストURLを作成する
        Dim url As String = CreateAWSUrl(asin)
        Console.Write(url)

    End Sub

    Private Function CreateAWSUrl(ByVal asin As String) As String

        Try
            ' パラメータハッシュテーブル
            Dim reqtionary As New Dictionary(Of String, String)
            reqtionary("Service") = "AWSECommerceService"
            reqtionary("Version") = "2008-04-07"
            reqtionary("AssociateTag") = AWS_ASSOCIATE_TAG
            reqtionary("ContentType") = "Text/XML"
            reqtionary("Operation") = "ItemLookup" ' "ItemSearch"
            reqtionary("Availability") = "Available"
            reqtionary("ResponseGroup") = "ItemAttributes,OfferSummary" ' 商品情報と在庫情報なんかを見たいとき
            'reqtionary("ResponseGroup") = "Small,ItemAttributes,Reviews,EditorialReview,Images,BrowseNodes,SalesRank,Offers"
            reqtionary("AWSAccessKeyId") = AWS_ACCESS_KEY_ID
            'reqtionary("Condition") = "New"
            'reqtionary("SearchIndex") = "All"   ' ItemSearchなら必要
            reqtionary("ItemId") = asin
            'reqtionary("Keywords") = ""
            ' タイムスタンプはGMT!
            Dim dteNow As DateTime = DateTime.UtcNow
            Dim sTimeStamp As String = dteNow.ToString("yyyy-MM-ddTHH:mm:ssZ")
            reqtionary("Timestamp") = sTimeStamp

            ' ソートして
            Dim paramComp As New ParameterComparer()
            Dim sortedReqtionary As New SortedDictionary(Of String, String)(reqtionary, paramComp)
            ' canonical用正規URL取得
            Dim canonicalQueryStr As String = CreateCanonicalQueryString(sortedReqtionary)

            ' ヘッダ部に以下の文字列を追加する
            'GET
            'ecs.amazonaws.jp(US:ecs.amazonaws.com)
            '/onca/xml
            Dim urlBuilder As New System.Text.StringBuilder()
            urlBuilder.Append("GET")
            urlBuilder.Append(vbLf)
            urlBuilder.Append("ecs.amazonaws.jp")
            urlBuilder.Append(vbLf)
            urlBuilder.Append("/onca/xml")
            urlBuilder.Append(vbLf)
            urlBuilder.Append(canonicalQueryStr)

            ' シグネチャを作成する
            Dim urlStr As Byte() = System.Text.Encoding.UTF8.GetBytes(urlBuilder.ToString())
            Dim secKey As Byte() = System.Text.Encoding.UTF8.GetBytes(AWS_SECRET_KEY)
            Dim signHMAC As System.Security.Cryptography.HMAC = New Security.Cryptography.HMACSHA256(secKey)
            Dim signByte As Byte() = signHMAC.ComputeHash(urlStr)
            Dim signature As String = Convert.ToBase64String(signByte)

            ' 最終整形
            urlBuilder.Length = 0
            urlBuilder.Append("http://")
            urlBuilder.Append("ecs.amazonaws.jp")
            urlBuilder.Append("/onca/xml")
            urlBuilder.Append("?")
            urlBuilder.Append(canonicalQueryStr)
            urlBuilder.Append("&Signature=")
            urlBuilder.Append(EncodeRfc3986(signature))
            Return urlBuilder.ToString()
        Catch ex As Exception
            MsgBox("ERROR!" & vbCrLf & "詳細:" & ex.Message, vbOKOnly)
            Return ""
        End Try

    End Function

    ''' <summary>
    ''' canonical用URLを作成する
    ''' </summary>
    ''' <param name="sortedParamHash"></param>
    ''' <returns></returns>
    Private Function CreateCanonicalQueryString(ByVal sortedParamHash As SortedDictionary(Of String, String)) As String

        Try
            If sortedParamHash.Count = 0 Then Return ""
            Dim queryBuilder As New System.Text.StringBuilder()
            For Each kvp As KeyValuePair(Of String, String) In sortedParamHash
                queryBuilder.Append(EncodeRfc3986(kvp.Key))
                queryBuilder.Append("=")
                queryBuilder.Append(EncodeRfc3986(kvp.Value))
                queryBuilder.Append("&")
            Next
            Dim canonicalQueryStr As String = queryBuilder.ToString()
            canonicalQueryStr = canonicalQueryStr.Substring(0, canonicalQueryStr.Length - 1)
            Return canonicalQueryStr
        Catch ex As Exception
            Throw
        End Try
    End Function

    ''' <summary>
    ''' Rfc3986に基づき、URLをエンコーディングする
    ''' </summary>
    ''' <param name="str"></param>
    ''' <returns></returns>
    Private Function EncodeRfc3986(ByVal str As String) As String

        Try
            ' UTF-8でURLエンコ
            str = System.Web.HttpUtility.UrlEncode(str, System.Text.Encoding.UTF8)
            ' RFC3986に基づき、文字列を置換する
            str = str.Replace("'", "%27").Replace("(", "%28").Replace(")", "%29").Replace("*", "%2A").Replace("!", "%21").Replace("%7e", "~").Replace("+", "%20")
            Dim strBuilder As New System.Text.StringBuilder(str)
            For i As Integer = 0 To strBuilder.Length - 1
                ' マルチバイト文字対応
                If strBuilder(i) = "%"c Then
                    strBuilder(i + 1) = [Char].ToUpper(strBuilder(i + 1))
                    strBuilder(i + 2) = [Char].ToUpper(strBuilder(i + 2))
                End If
            Next
            Return strBuilder.ToString()
        Catch ex As Exception
            Throw
        End Try
    End Function

End Class

''' <summary>
''' Parameter Comparer Class
''' </summary>
Class ParameterComparer
    Implements IComparer(Of String)

    Public Function Compare(ByVal pram1 As String, ByVal param2 As String) As Integer Implements IComparer(Of String).Compare
        Return String.CompareOrdinal(pram1, param2)
    End Function

End Class

多くに人が利用するので当然といえば当然ですが、ガイドラインがやたら細かいし、複雑に思えました。
それをインターネットで調べようとすると古い情報がそのまま残っていたりするおかげで、さらに混乱しそうになりました。
この記事も、すぐに古い情報になるでしょう。
シグネチャ(署名)の作成あたりは特に敷居が高いように思います。
とりあえず、検索結果が返ってくるようになったので、よし!

 - 技術 , , ,