Amazon PA-API v5 に対応させる

かなーり久々にWEB系ブログの更新です。
サラリーの仕事では結構コードを書いていますが、ここにLOGを残している余裕が無いだけ。
今回は、一応残しておきたいLOGなので書いてます。

広告

Amazon PA-API v5 の使い方

私の運営している幾つかのブログで、Amazon PA-API というAPIを使っています。
今月2019/11でバージョン変更という事で、PA-API v5 になるとの事。
その際、旧バージョンは切り捨てとの事でしたので、慌ててAPI取得のコアを変更です。

基本的には『Product Advertising API 5.0 Scratchpad』参考にすれば良い。


class AwsV4 {

    private $accessKey = null;
    private $secretKey = null;
    private $path = null;
    private $regionName = null;
    private $serviceName = null;
    private $httpMethodName = null;
    private $queryParametes = array ();
    private $awsHeaders = array ();
    private $payload = "";

    private $HMACAlgorithm = "AWS4-HMAC-SHA256";
    private $aws4Request = "aws4_request";
    private $strSignedHeader = null;
    private $xAmzDate = null;
    private $currentDate = null;

    public function __construct($accessKey, $secretKey) {
        $this->accessKey = $accessKey;
        $this->secretKey = $secretKey;
        $this->xAmzDate = $this->getTimeStamp ();
        $this->currentDate = $this->getDate ();
    }

    function setPath($path) {
        $this->path = $path;
    }

    function setServiceName($serviceName) {
        $this->serviceName = $serviceName;
    }

    function setRegionName($regionName) {
        $this->regionName = $regionName;
    }

    function setPayload($payload) {
        $this->payload = $payload;
    }

    function setRequestMethod($method) {
        $this->httpMethodName = $method;
    }

    function addHeader($headerName, $headerValue) {
        $this->awsHeaders [$headerName] = $headerValue;
    }

    private function prepareCanonicalRequest() {
        $canonicalURL = "";
        $canonicalURL .= $this->httpMethodName . "\n";
        $canonicalURL .= $this->path . "\n" . "\n";
        $signedHeaders = '';
        foreach ( $this->awsHeaders as $key => $value ) {
            $signedHeaders .= $key . ";";
            $canonicalURL .= $key . ":" . $value . "\n";
        }
        $canonicalURL .= "\n";
        $this->strSignedHeader = substr ( $signedHeaders, 0, - 1 );
        $canonicalURL .= $this->strSignedHeader . "\n";
        $canonicalURL .= $this->generateHex ( $this->payload );
        return $canonicalURL;
    }

    private function prepareStringToSign($canonicalURL) {
        $stringToSign = '';
        $stringToSign .= $this->HMACAlgorithm . "\n";
        $stringToSign .= $this->xAmzDate . "\n";
        $stringToSign .= $this->currentDate . "/" . $this->regionName . "/" . $this->serviceName . "/" . $this->aws4Request . "\n";
        $stringToSign .= $this->generateHex ( $canonicalURL );
        return $stringToSign;
    }

    private function calculateSignature($stringToSign) {
        $signatureKey = $this->getSignatureKey ( $this->secretKey, $this->currentDate, $this->regionName, $this->serviceName );
        $signature = hash_hmac ( "sha256", $stringToSign, $signatureKey, true );
        $strHexSignature = strtolower ( bin2hex ( $signature ) );
        return $strHexSignature;
    }

    public function getHeaders() {
        $this->awsHeaders ['x-amz-date'] = $this->xAmzDate;
        ksort ( $this->awsHeaders );

        // Step 1: CREATE A CANONICAL REQUEST
        $canonicalURL = $this->prepareCanonicalRequest ();

        // Step 2: CREATE THE STRING TO SIGN
        $stringToSign = $this->prepareStringToSign ( $canonicalURL );

        // Step 3: CALCULATE THE SIGNATURE
        $signature = $this->calculateSignature ( $stringToSign );

        // Step 4: CALCULATE AUTHORIZATION HEADER
        if ($signature) {
            $this->awsHeaders ['Authorization'] = $this->buildAuthorizationString ( $signature );
            return $this->awsHeaders;
        }
    }

    private function buildAuthorizationString($strSignature) {
        return $this->HMACAlgorithm . " " . "Credential=" . $this->accessKey . "/" . $this->getDate () . "/" . $this->regionName . "/" . $this->serviceName . "/" . $this->aws4Request . "," . "SignedHeaders=" . $this->strSignedHeader . "," . "Signature=" . $strSignature;
    }

    private function generateHex($data) {
        return strtolower ( bin2hex ( hash ( "sha256", $data, true ) ) );
    }

    private function getSignatureKey($key, $date, $regionName, $serviceName) {
        $kSecret = "AWS4" . $key;
        $kDate = hash_hmac ( "sha256", $date, $kSecret, true );
        $kRegion = hash_hmac ( "sha256", $regionName, $kDate, true );
        $kService = hash_hmac ( "sha256", $serviceName, $kRegion, true );
        $kSigning = hash_hmac ( "sha256", $this->aws4Request, $kService, true );

        return $kSigning;
    }

    private function getTimeStamp() {
        return gmdate ( "Ymd\THis\Z" );
    }

    private function getDate() {
        return gmdate ( "Ymd" );
    }
}

Scratchpadに書いてあった『class AwsV4』ベースに使わせてもらいました。
↑はコピペだけど、実際は少し変えている。


$serviceName="ProductAdvertisingAPI";
$region="us-west-2";
$accessKey="****APIのアクセスキー****";
$secretKey="****APIのシークレットキー*****";
$payload="{"
        ." \"Keywords\": \"PHP\","
        ." \"Resources\": ["
        ."  \"BrowseNodeInfo.BrowseNodes\","
        ."  \"BrowseNodeInfo.BrowseNodes.Ancestor\","
        ."  \"BrowseNodeInfo.BrowseNodes.SalesRank\","
        ."  \"BrowseNodeInfo.WebsiteSalesRank\","
        ."  \"CustomerReviews.Count\","
        ."  \"CustomerReviews.StarRating\","
        ."  \"Images.Primary.Small\","
        ."  \"Images.Primary.Medium\","
        ."  \"Images.Primary.Large\","
        ."  \"Images.Variants.Small\","
        ."  \"Images.Variants.Medium\","
        ."  \"Images.Variants.Large\","
        ."  \"ItemInfo.ByLineInfo\","
        ."  \"ItemInfo.ContentInfo\","
        ."  \"ItemInfo.ContentRating\","
        ."  \"ItemInfo.Classifications\","
        ."  \"ItemInfo.ExternalIds\","
        ."  \"ItemInfo.Features\","
        ."  \"ItemInfo.ManufactureInfo\","
        ."  \"ItemInfo.ProductInfo\","
        ."  \"ItemInfo.TechnicalInfo\","
        ."  \"ItemInfo.Title\","
        ."  \"ItemInfo.TradeInInfo\","
        ."  \"Offers.Listings.Availability.MaxOrderQuantity\","
        ."  \"Offers.Listings.Availability.Message\","
        ."  \"Offers.Listings.Availability.MinOrderQuantity\","
        ."  \"Offers.Listings.Availability.Type\","
        ."  \"Offers.Listings.Condition\","
        ."  \"Offers.Listings.Condition.SubCondition\","
        ."  \"Offers.Listings.DeliveryInfo.IsAmazonFulfilled\","
        ."  \"Offers.Listings.DeliveryInfo.IsFreeShippingEligible\","
        ."  \"Offers.Listings.DeliveryInfo.IsPrimeEligible\","
        ."  \"Offers.Listings.DeliveryInfo.ShippingCharges\","
        ."  \"Offers.Listings.IsBuyBoxWinner\","
        ."  \"Offers.Listings.LoyaltyPoints.Points\","
        ."  \"Offers.Listings.MerchantInfo\","
        ."  \"Offers.Listings.Price\","
        ."  \"Offers.Listings.ProgramEligibility.IsPrimeExclusive\","
        ."  \"Offers.Listings.ProgramEligibility.IsPrimePantry\","
        ."  \"Offers.Listings.Promotions\","
        ."  \"Offers.Listings.SavingBasis\","
        ."  \"Offers.Summaries.HighestPrice\","
        ."  \"Offers.Summaries.LowestPrice\","
        ."  \"Offers.Summaries.OfferCount\","
        ."  \"ParentASIN\","
        ."  \"RentalOffers.Listings.Availability.MaxOrderQuantity\","
        ."  \"RentalOffers.Listings.Availability.Message\","
        ."  \"RentalOffers.Listings.Availability.MinOrderQuantity\","
        ."  \"RentalOffers.Listings.Availability.Type\","
        ."  \"RentalOffers.Listings.BasePrice\","
        ."  \"RentalOffers.Listings.Condition\","
        ."  \"RentalOffers.Listings.Condition.SubCondition\","
        ."  \"RentalOffers.Listings.DeliveryInfo.IsAmazonFulfilled\","
        ."  \"RentalOffers.Listings.DeliveryInfo.IsFreeShippingEligible\","
        ."  \"RentalOffers.Listings.DeliveryInfo.IsPrimeEligible\","
        ."  \"RentalOffers.Listings.DeliveryInfo.ShippingCharges\","
        ."  \"RentalOffers.Listings.MerchantInfo\","
        ."  \"SearchRefinements\""
        ." ],"
        ." \"PartnerTag\": \"***APIのタグ***\","
        ." \"PartnerType\": \"Associates\","
        ." \"Marketplace\": \"www.amazon.co.jp\""
        ."}";
$host="webservices.amazon.co.jp";
$uriPath="/paapi5/searchitems";
$awsv4 = new AwsV4 ($accessKey, $secretKey);
$awsv4->setRegionName($region);
$awsv4->setServiceName($serviceName);
$awsv4->setPath ($uriPath);
$awsv4->setPayload ($payload);
$awsv4->setRequestMethod ("POST");
$awsv4->addHeader ('content-encoding', 'amz-1.0');
$awsv4->addHeader ('content-type', 'application/json; charset=utf-8');
$awsv4->addHeader ('host', $host);
$awsv4->addHeader ('x-amz-target', 'com.amazon.paapi5.v1.ProductAdvertisingAPIv1.SearchItems');
$headers = $awsv4->getHeaders ();
$headerString = "";
foreach ( $headers as $key => $value ) {
    $headerString .= $key . ': ' . $value . "\r\n";
}
$params = array (
        'http' => array (
            'header' => $headerString,
            'method' => 'POST',
            'content' => $payload
        )
    );
$stream = stream_context_create ( $params );

$fp = @fopen ( 'https://'.$host.$uriPath, 'rb', false, $stream );

if (! $fp) {
    throw new Exception ( "Exception Occured" );
}
$response = @stream_get_contents ( $fp );
if ($response === false) {
    throw new Exception ( "Exception Occured" );
}
echo $response;

PartnerTag、$accessKey、$secretKeyは自分の物に合わせて設定。

これでJSON形式でデータを吐き出してくれる。
↑のコードはScratchpadそのままなので、加工無しのデータが吐き出されます。

あとは、使いたいように加工すれば良い。

ちなみに開発途中なのか、レビュー数や、レビューのIframeのURLとは取得出来ない。
それ以外にもV4で取得出来たデータの一部が取得できない。

切り捨てるなら完璧に仕上げてから切り捨てて欲しいと思ってしまうのは、私だけじゃないだろうな・・・

ついでにJanコードでの複数検索も無理になってしまったようです・・・。
ASINでは出来ました。

ではでは・・・・


おすすめのコンテンツ

広告

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください