質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.83%
PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

Q&A

解決済

PHPでAmazonの商品を検索する

setouchi
setouchi

総合スコア6

PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

3回答

0グッド

0クリップ

3505閲覧

投稿2018/06/27 03:18

編集2018/06/27 09:07

PHPでAmazonの商品を検索してみたいのですが、どうしても400エラーが帰ってきてしまいます。公式にもサンプルがないのでなぜ遮断されているかわかりません。もしわかる方がいらっしゃいましたら教えてください。問い合わせコード自体は公式のものを改造したものです。公式のページを末尾に追記。公式ではSignatureは自分で用意する感じ。Signatureの生成がおかしいのでしょうか。

参考サイト

当初AmazonのAPIの使い方のコード自体はImplementing a Product Advertising API Request - Product Advertising APIを参考にし、SignatureはOAuth1.0の署名(Signature)を作成する方法を参考にしました。あと、誠に自分勝手で申し訳ないですが、勉強がてら今回は外部ライブラリを使わない方法でやってみたいとうのが目的でやっています。

実際のコード

その後、m6uさんに助けていただき、下記のコードまで成長し、Signatureの問題までこぎつけました。Amazon側のステータスコードは400から403へ変わりました。Signatureの生成に何らかの欠陥があり403は解決されません。

php

1define("AccessKey", "hoge"); 2define("AssociateTag", "hoge"); 3define("SecretKey", "hoge"); 4 5function ItemSearch($SearchIndex, $Keywords) { 6 7 $base = "http://webservices.amazon.com/onca/xml"; 8 // $base = "http://ecs.amazonaws.jp/onca/xml"; 9 10 $params = []; 11 foreach ([ 12 "Service" => 'AWSECommerceService', 13 "AssociateTag" => AssociateTag, 14 "AWSAccessKeyId" => AccessKey, 15 "Operation" => "ItemSearch", 16 "Version" => "2013-08-01", 17 "SearchIndex" => $SearchIndex, 18 "Keywords" => $Keywords, 19 "ResponseGroup" => "ItemAttributes,Offers", 20 "Timestamp" => gmdate('Y-m-d\TH:i:s\Z'), 21 "SignatureVersion" => 2, 22 "SignatureMethod" => "HmacSHA256" 23 ] as $key => $value) { 24 $params[urlEncodeRfc3986($key)] = urlEncodeRfc3986($value); 25 } 26 27 ksort($params); 28 29 $parsed = parse_url($base); 30 $sign = join("\n", ['GET', $parsed['host'], $parsed['path'], http_build_query($params, '', '&')]); 31 32 $signature = base64_encode(hash_hmac('sha256', $sign, SecretKey , true)); 33 34 $queries = []; 35 foreach ($params as $key => $value) { 36 $queries[] = $key . "=" . $value; 37 } 38 39 $request = $base . "?" . join("&", $queries) . "&Signature=" . urlEncodeRfc3986($signature); 40 41 var_dump($request); 42 43 $response = file_get_contents($request); 44 $parsed_xml = simplexml_load_string($response); 45 46 print_r($parsed_xml); 47} 48 49function urlEncodeRfc3986($str) { 50 return str_replace('%7E', '~', rawurlencode($str)); 51} 52 53ItemSearch('シーツ', 'PetSupplies');

気になる質問をクリップする

クリップした質問は、後からいつでもマイページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

下記のような質問は推奨されていません。

  • 質問になっていない投稿
  • スパムや攻撃的な表現を用いた投稿

適切な質問に修正を依頼しましょう。

回答3

2

さすがにそんなところから自分で書いてたら面倒すぎる。
大昔は一から書いてたけどすぐにclass化。
今ならこの辺。
https://github.com/Exeu/apai-io

これでも面倒なのでさらにLaravel用には自分で作ってるけど。
https://github.com/kawax/laravel-amazon-product-api

投稿2018/06/27 04:03

kawax

総合スコア10268

m.ts10806👍を押しています

下記のような回答は推奨されていません。

  • 質問の回答になっていない投稿
  • スパムや攻撃的な表現を用いた投稿

このような回答には修正を依頼しましょう。

2

$signature_data = "GET&$baseEncode&$request_params";
GET の後にブランク入れる必要があるような?

rawurlencode()を2度もかけてるのも改善しないと。

日本の場合のエンドポイントは
http://ecs.amazonaws.jp/onca/xml」と「https://aws.amazonaws.jp/onca/xml」
らしいです。
サンプルコードだとUSに問い合わせしちゃうように見える。

リクエスト文字列の途中に半角空白があると、
そこまでしか読み取られず、
リクエストに必要なパラメータが足りないことで
400 Bad requestになる可能性がある。

Signature送信のために、Timestampも必要になるけど、
その対応が漏れているのかも。
参考:REST リクエストのサンプル

投稿2018/06/27 03:25

編集2018/06/27 04:59
退会済みユーザー

退会済みユーザー

総合スコア0

m.ts10806, setouchi👍を押しています

下記のような回答は推奨されていません。

  • 質問の回答になっていない投稿
  • スパムや攻撃的な表現を用いた投稿

このような回答には修正を依頼しましょう。

回答へのコメント

setouchi

2018/06/27 03:27 編集

回答ありがとうございます!下記の様にすればよいのでしょうか? ``` $signature_data = "GET $baseEncode $request_params"; ``` これでも残念ながらエラーとなってしまいます。
setouchi

2018/06/27 03:31

こういうことでしょうか?これでもエラーとなってしまいます。 ``` $signature_data = "GET " . $base . " " . http_build_query($params, '', '&'); ```
setouchi

2018/06/27 03:32

ありがとうございました!
退会済みユーザー

退会済みユーザー

2018/06/27 04:13

GET のリクエスト文字列の、キーに当たるところはrawurlencode()する必要がなくて、 値のみrawurlencode()しての&で連結っていうシンプルな流れで十分じゃないかなぁ。
setouchi

2018/06/27 04:16

おおー参考になります!! > リクエスト文字列の途中に半角空白があると、そこまでしか読み取られず、リクエストに必要なパラメータが足りないことで400 Bad requestになる可能性がある。
setouchi

2018/06/27 04:48 編集

最終的に下記のようなリクエストURLが生成されるんですが、なんとなくSignatureが間違っているなら403となっても良い気がするんですが400。URLが壊れているようには見受けられなく。リクエストのエンドポイントを http://ecs.amazonaws.jp/onca/xml に変更しても変わらず。 ``` http://webservices.amazon.com/onca/xml?AWSAccessKeyId=[アクセスキーURLエンコード値]& AssociateTag=[アソシエイトタグのURLエンコード値]& Keywords=PetSupplies& Operation=ItemSearch& ResponseGroup=ItemAttributes%2COffers& SearchIndex=[キーワードのURLエンコード値]& Service=AWSECommerceService& Version=2013-08-01& Signature=[SignatureのURLエンコード値] ``` Signatureを抜いてcurlでリクエストを投げるとSignatureは必須だと正常に返してくれる。 ``` <?xml version="1.0"?> <ItemSearchErrorResponse xmlns="http://ecs.amazonaws.com/doc/2013-08-01/"><Error><Code>MissingParameter</Code><Message>The request must contain the parameter Signature.</Message></Error><RequestID>hoge</RequestID></ItemSearchErrorResponse> ```
退会済みユーザー

退会済みユーザー

2018/06/27 05:00

Timestampかな、と。
setouchi

2018/06/27 05:13

おおお!本当にありがとうございます! 403までこぎつけました!!Signatureが駄目だと言っています。あとはSignatureがなぜ駄目なのかを調査すればいけそうです。 ``` <?xml version="1.0"?> <ItemSearchErrorResponse xmlns="http://ecs.amazonaws.com/doc/2013-08-01/"><Error><Code>SignatureDoesNotMatch</Code><Message>The request signature we calculated does not match the signature you provided. Check your AWS Secret Access Key and signing method. Consult the service documentation for details.</Message></Error><RequestID>hogehoge</RequestID></ItemSearchErrorResponse> ```
退会済みユーザー

退会済みユーザー

2018/06/27 05:33

hash_hmac() するときのアルゴリズム指定が'sha256'、かな
setouchi

2018/06/27 07:27 編集

あれからいろいろ試しましたが、403は変わらず。 * sha256による生成 * RFC3986によるURLエンコード * SignatureVersionの指定 * SignatureMethodの指定 * SecretKeyの使用方法の誤りを修正 上記の変更を適用したコードを質問のコードに適用(質問を修正)しました。署名文字列はスペース区切りから改行区切りといろいろ試しましたが、どちらも変わらず。
退会済みユーザー

退会済みユーザー

2018/06/27 08:19 編集

signatureにhash_hmac()したあとにさらにrawurlencode()加えるのとかOK? 得られたsignature文字列に+とか=とか入っている場合にはそこもURLエンコードしないといけない。
setouchi

2018/06/27 08:56

signatureはhash_hmac -> base64_encode -> rawurlencode -> %7Eを~に変換(RFC3986)という処理を加えております。
setouchi

2018/06/27 09:44

解決しました。1日がかりでしたが、m6uさんが適切なヒントをくださったおかげです。最大限の感謝をお送り致します。とても楽しかったです。ありがとうございました!!

0

自己解決

m6uさん協力の元解決しました。下記のコードが完成形です。

php

1$accessKey = "access"; 2$secretKey = "secret"; 3$associateTag = "tag"; 4$endpoint = "webservices.amazon.co.jp"; 5$uri = "/onca/xml"; 6 7foreach ([ 8 "Service" => "AWSECommerceService", 9 "Operation" => "ItemSearch", 10 "AWSAccessKeyId" => $accessKey, 11 "AssociateTag" => $associateTag, 12 "SearchIndex" => "PetSupplies", 13 "ResponseGroup" => "Images,ItemAttributes,Offers", 14 "Keywords" => "シーツ", 15 "Timestamp" => gmdate('Y-m-d\TH:i:s\Z') 16] as $key => $value) { 17 $params[rawurlencode($key)] = rawurlencode($value); 18}; 19 20ksort($params); 21 22$queries = []; 23foreach ($params as $key => $value) { 24 $queries[] = "$key=$value"; 25} 26 27$queryString = join("&", $queries); 28$signature = base64_encode(hash_hmac("sha256", join("\n", ["GET", $endpoint, $uri, $queryString]), $secretKey, true)); 29 30echo file_get_contents("https://$endpoint$uri?$queryString&Signature=" . rawurlencode($signature));

署名のコードでhttp_build_queryで二重にURLエンコードされていた点(完全に見落としです)。ここが解決された後エラーは503エラーとなり、そこからAmazonへのリクエス時の不要なクエリを削除していくことで無事200が返りました。

投稿2018/06/27 09:42

setouchi

総合スコア6

下記のような回答は推奨されていません。

  • 質問の回答になっていない投稿
  • スパムや攻撃的な表現を用いた投稿

このような回答には修正を依頼しましょう。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.83%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問

同じタグがついた質問を見る

PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。