上級 PHP 外部API XML 署名 動作停止中

Amazon の商品を検索する(旧 Product Advertising API)

Amazon の商品データを検索できる API に、PHP からアクセスして本の情報を取り出すサンプルです。API が提供終了したため現在は動きませんが、「APIキーで署名してリクエストする」という考え方は今も多くの API で使われています。

このサンプルは2017〜2018年ごろに作成したものです。PHPのバージョンが上がり、そのままでは動かなくなっていたため、2026年に PHP 8.3 で動くように書き直し、解説も新しくしました。

動作停止中

このサンプルは現在動きません。

使っていた Amazon の「Product Advertising API」(AWSECommerceService)は、Amazon 側で提供が終了しました。そのため、デモの代わりにコードの考え方だけを下で解説しています。

  • 当時の動き:キーワード(例: 本のタイトル)を送ると、本の題名・著者・出版社・発売日・画像の URL が XML で返ってくる
  • 今の状況:後継の PA-API 5.0 も非推奨になり、さらに新しい「Creators API」への移行が案内されています(2026年10月時点)

解説コードを読んでみよう

このサンプルでわかること

  • 外部の API に、PHP からリクエストを送る流れ
  • 秘密のキーで「署名」を作り、本人からのリクエストだと証明するしくみ
  • 返ってきた XML を simplexml で読み取る方法

このコードの API(AWSECommerceService)は提供終了しているため、そのまま動かしても結果は返りません。今から Amazon の商品データを使う場合は、Amazon アソシエイトの管理画面で案内されている新しい API(Creators API)のドキュメントを参照してください。

使っているファイル

  • search.phpAPI にリクエストを送り、結果を表示するスクリプト(キーはダミーに置き換え済み)

1. 認証情報と検索条件を用意する

API を使うには、Amazon から発行される「アクセスキー」「シークレットキー」「アソシエイトタグ」が必要でした。ここではダミーの文字に置き換えています。

検索の条件は、$params という配列にまとめます。Operation が「何をしたいか」(ItemSearch = 検索)、Keywords が検索語、SearchIndex が検索するカテゴリー(Books = 本)です。

search.php(8〜23行目) PHP
// 1. 認証情報と API の URL
define("ACCESS_KEY_ID"     , '********');
define("SECRET_ACCESS_KEY" , '********');
define("ASSOCIATE_TAG"     , 'YOUR_ASSOCIATE_TAG');
define("ACCESS_URL"        , 'https://aws.amazonaws.jp/onca/xml');

// 2. 検索の条件(パラメーター)
$params = array();
$params['AssociateTag']   = ASSOCIATE_TAG;
$params['Service']        = 'AWSECommerceService';
$params['Operation']      = 'ItemSearch';
$params['Keywords'] ='tofle';
$params['SearchIndex']    = 'Books';
$params['ResponseGroup']  = 'ItemAttributes,Offers,Images,Reviews';
$params['Timestamp']      = gmdate('Y-m-d\TH:i:s\Z');

本物のキーは、絶対にコードに直接書いて公開しないでください。他人に使われてしまいます。実際のサイトでは、公開フォルダの外にある設定ファイルや環境変数に書きます。

2. 署名(シグネチャ)を作る

「このリクエストは確かにキーの持ち主から来た」ことを証明するために、決まった手順で文字列を作り、シークレットキーで暗号化します。これを署名といいます。

  1. パラメーターを名前の順に並べる(ksort)
  2. 「名前=値」を & でつないだ文字列を作る(値は URL エンコード)
  3. 「GET」「ホスト名」「パス」「2の文字列」を改行でつなぐ
  4. 3を、シークレットキーを使って hash_hmac('sha256', ...) で暗号化し、Base64 に変換する
search.php(36〜56行目) PHP
// 3. パラメーターを名前順に並べる(署名の決まり)
ksort($params);

function urlencode_RFC3986($str) {
    return str_replace('%7E', '~', rawurlencode($str));
}

$canonical_string = 'AWSAccessKeyId='.ACCESS_KEY_ID;
foreach ($params as $k => $v) {
    $canonical_string .= '&'.urlencode_RFC3986($k).'='.urlencode_RFC3986($v);
}
 

 
// 4. 署名を作る(秘密キーで HMAC-SHA256 → Base64)
$parsed_url = parse_url(ACCESS_URL);
$string_to_sign = "GET\n{$parsed_url['host']}\n{$parsed_url['path']}\n{$canonical_string}";
 
$signature = base64_encode(
    hash_hmac('sha256', $string_to_sign, SECRET_ACCESS_KEY, true)
);

受け取った側(Amazon)も、同じ手順で同じ文字列を作り、自分が持っているシークレットキーで署名を計算します。結果が一致すれば「本人からのリクエストで、途中で書き換えられていない」と判断できます。シークレットキーそのものは送らないのがポイントです。

3. API にアクセスして XML を受け取る

署名をつけた URL を file_get_contents() で開くと、結果が XML という形式の文字列で返ってきます。simplexml_load_string() で、PHP から扱いやすい形に変換します。

search.php(58〜68行目) PHP
// 5. 署名をつけた URL にアクセスして、XML を受け取る
$url = ACCESS_URL.'?'.$canonical_string.'&Signature='.urlencode_RFC3986($signature);
 
// 注意: この旧 Product Advertising API (AWSECommerceService) は Amazon 側で提供終了しているため、現在はデータを取得できません。
$response = @file_get_contents($url); //Amazonへレスポンス

$parsed_xml = null;
// レスポンスを配列で取得
if (is_string($response) && $response !== '') {
    $parsed_xml = @simplexml_load_string($response) ?: null;
}

4. XML から必要な情報を取り出す

XML は入れ子の構造になっていて、-> でたどっていきます。$parsed_xml->Items->Item が本の一覧なので、foreach で1冊ずつ取り出します。

search.php(75〜95行目) PHP
// 6. 結果の XML から、本のタイトルなどを取り出す
if ($response && isset($parsed_xml) && empty($parsed_xml->faultstring) && empty($parsed_xml->Items->Request->Errors)) {

$data=[];
$i = 0;

    foreach ($parsed_xml->Items->Item as $current) {
        $data[$i]['title']  = $current->ItemAttributes->Title;
        $data[$i]['author']       = $current->ItemAttributes->Author;
        $data[$i]['manufacturer']    = $current->ItemAttributes->Manufacturer;
        $data[$i]['imgURL']          = $current->LargeImage->URL;
        $data[$i]['bookURL']         = $current->DetailPageURL;
        $data[$i]['releaseDate']     = $current->ItemAttributes->ReleaseDate;
 
        // 著者が複数いる場合(PHP8.3対応: 未定義の $author を count() すると TypeError になっていたため修正)
        $author = [];
        foreach ($current->ItemAttributes->Author as $a) { $author[] = (string)$a; }
        $data[$i]['authors']  = implode(',', $author);
        $i++;
    }
}

PHP 8.3 で書き直したポイント

  • 旧版では、未定義の変数 $author を count() に渡していました。PHP 8 からは配列以外を count() に渡すとエラーで止まるため、XML から著者を取り出して配列にするよう直しました。
  • API から何も返ってこないとき(false)に XML として読もうとしないよう、事前に確認するようにしました。

天気・地図・翻訳など、無料で試せる API はたくさんあります。「キーを付けてリクエスト → JSON や XML を受け取る → 必要な部分を取り出す」という流れは、どの API でもほぼ同じです。

↑