wordpress サーバー

Amazonの商品データを取得して、WordPress上で表示する方法

Product Advertising API(以下AmazonAPI)はAmazonから商品データを取得することができるAPIです。 AmazonからデータをWordPressに投稿するコードを紹介します。アフィリエイトサイトや、プラットフォーム型サイトを作っている人にはおすすめです。

まず機能構築

まず最初に、実行を行う関数について見ていきましょう。

以下は、プラグインとして作成して有効化することをオススメします。

Amazonが提供している定数の準備

AmazonAPIで必ず必要になるアクセスキーやシークレットキーなどを定義します。
実際のキーの取得やアカウント登録などの手順についてはAmazon Webサービス入門 サービス利用の準備をご覧ください。

//Amazon共通の定数
define('AF','nets0f-22'); //トラッキング(自分で入力する。アフィリエイトID)
define('AC','AAAAAAAAAAAAAAAA'); //アクセスキー(自分で入手する)
define('SEC','abcdefghijklmnopqrstuvwxyz'); //シークレットキー(自分で入手する)
define('URL','http://ecs.amazonaws.jp/onca/xml'); //リクエスト先のURL

商品データ取得するのに使うパラメータを取得

次は商品データ取得の際に必要となるパラメータを取得します。

中にあるget_key()という関数は、AmazonAPIに必要なタイムスタンプによる認証キーを生成しています。

認証(Timestamp及びSignature)

//認証取得用関数
function urlencode_rfc3986($str) {
	return str_replace('%7E', '~', rawurlencode($str));
}

function get_key($param){
	ksort($param);
	$canonical_string = '';
	foreach ($param as $k => $v) {
		$canonical_string .= '&'.urlencode_rfc3986($k).'='.urlencode_rfc3986($v);
	}
	$canonical_string = substr($canonical_string, 1);
	$parsed_url = parse_url(URL);
	$string_to_sign = "GETn{$parsed_url['host']}n{$parsed_url['path']}n{$canonical_string}";
	$signature = base64_encode(hash_hmac('sha256', $string_to_sign, SEC, true));
	
	// 返り値のURLにアクセスするとXMLが取得できます。
	return URL.'?'.$canonical_string.'&Signature='.urlencode_rfc3986($signature);
}

//画像をサーバに保存するfunction
//URLを投げたら指定したディレクトリに保存するだけ。。。。
function dl_image($url){
	$tempimg = file_get_contents($url);
	file_put_contents(WP_CONTENT_DIR.'/upload/'.basename($url),$tempimg);
	unset($tempimg);
	return WP_CONTENT_DIR.'/upload/'.basename($url);
}

パラメータを取得したので、実際にコンテンツを取得し、WordPressに保存する関数を作成

パラメータを取得したので、実際にAmazonAPIにアクセスして商品データを取得します。

カスタムフィールドにどの値を持つか、本文はどうするかなどは適宜変更してください。

//例:Amazon内のDVDカテゴリの中のアニメカテゴリから新着順に商品を取得する
//ここから実際の取得処理------------------------------------------
//取得処理はwp_cronで動かしますのでadd_actionは不要です。
function get_amazon(){
	$ct = array(
		'1' => '562020' //WP上のカテゴリID => Amazon上のカテゴリNo
	);
	
	//ページカウンター
	//1ページずつ取っていきます。どこまで取得できたかをget_optionで保持している
	if(get_option('count')){
		$i = get_option('count');
	} else {
		$i = 1;
		add_option('count',1);
	}

	foreach($ct as $key=>$val){
			//Amazonで必要なパラメーター類
			$param = array(
				'Service' => 'AWSECommerceService',
				'AWSAccessKeyId' => AC,
				'Operation' => 'ItemSearch',
				'AssociateTag' => AF,
				'ResponseGroup' => 'ItemAttributes,Images,Reviews',
				'SearchIndex' => 'DVD',//DVDなど、任意の検索単語
				'BrowseNode' => $val,
				'Timestamp' => gmdate('Y-m-dTH:i:sZ'),
				'ItemPage' => $i
			);
			
			//先に定義した認証パラメータ付きURLを生成
			$recurl = get_key($param);
			
			//ページカウントを進める
			//XML取得の途中で落ちてもいいようにforeach入る前にしてますがforeach後のほうが自然かも
			update_option('count',$i + 1);

			//XML取得
			$xml = simplexml_load_file($recurl);
			foreach($xml->Items->Item as $key2=>$val2){
				
				$actarray = array();
				foreach($val2->ItemAttributes->Actor as $act){
					$actarray[] = (String)$act;
				}
				
				
				//ここで記事として投稿処理を行う
				//ページスラッグがEANコード(ISBN的なものだと思われる)、タグをキャストとして投稿していますが適宜カスタムフィールドや本文などにしてもいい。
				$postid = wp_insert_post(array(
					'post_status' => 'publish',
					'post_category' => array($key),
					'post_title' => $val2->ItemAttributes->Title,
					'post_name' => $val2->ItemAttributes->EAN,
					'tags_input' => $actarray
				));
				
				//重複チェック
				//同じ商品を取ってしまった場合、EANが同じになるので末尾に「-2」などと付加される。その「-」があれば一回投稿した記事を削除する。
				//なお、無条件で「-」入りだと削除しているのでEAN以外をpost_nameにしていると間違って削除されるおそれがあります。
				$temppost = get_page($postid);
				if(strpos($temppost->post_name, '-') === true){
					wp_delete_post($postid);
				} else {
				
					//カスタムフィールドの付加
					//商品URLや画像URLに加えて最終更新日としてtime()を追加している。理由は後述。
					if($postid){
						add_post_meta($postid,'DetailPageURL',(String)$val2->DetailPageURL,true);
						add_post_meta($postid,'SmallImage',(Array)$val2->SmallImage,true);
						add_post_meta($postid,'MediumImage',(Array)$val2->MediumImage,true);
						add_post_meta($postid,'LargeImage',(Array)$val2->LargeImage,true);
						add_post_meta($postid,'IFrameURL',(String)$val2->CustomerReviews->IFrameURL,true);
						add_post_meta($postid,'LastUpdate',(String)time(),true);
						

						//画像をローカルに保存する
						$smalimg = $val2->SmallImage->URL;
						$smalimg_local = dl_image($smalimg);

						$midimg = $val2->MediumImage->URL;
						$midimg_local = dl_image($midimg);

						$largeimg = $val2->LargeImage->URL;
						$largeimg_local = dl_image($largeimg);

					}
				}
			}//end foreach xml
	}//end foreach
}//Amazonからの取得処理ここまで

Cronで定期的に処理をはしらsる

上記の処理を手動で走らせたり連続で走らせるとすぐにサーバが落ちたりAPIへのアクセスがブロックされるので、Cron(wp_cron)を使って定期的に自動実行させます。

AmazonAPIの利用規約では1時間2000リクエストとなっています。

//Cronの設定---------------------------------------------------------
//デフォルトのdailyやweeklyだけだと使い勝手が悪いのでいくつか足しておく
//このcron_schedulesとwp_schedule_eventはテンプレ的にこのままでいいでしょう。
add_filter('cron_schedules','cron_add');
function cron_add($schedules){
    $schedules['90min'] = array(
        'interval' => 5400,
        'display' => __( '90min' )
    );
    $schedules['100min'] = array(
        'interval' => 6000,
        'display' => __( '100min' )
    );
    $schedules['120min'] = array(
        'interval' => 7200,
        'display' => __( '120min' )
    );
    return $schedules;
}

//Cronに上記のget_amazon()を登録する
//90分単位で動作します。
add_action('cron_for_amazon', 'get_amazon');
function my_activation() {
	if ( !wp_next_scheduled( 'cron_for_amazon' ) ) {
		wp_schedule_event(time(), '90min', 'cron_for_amazon');
	}
}
add_action('wp', 'my_activation');

取得した商品データの更新

AmazonAPIの規約では、商品データをローカル(というか自分のサーバ)に保持することは許可されていますが、24時間以上保持することが禁止されています。
そこでデータ取得時に現在時刻をカスタムフィールドに保存していましたので、その時刻と現在時刻の差が24時間以上であれば再度商品取得処理を行うという処理を加えます。

これはCronではなくshutdownにフックしています。
誰かがアクセスしてそのときが24時間を超えていれば更新する、という仕組みです。

//ここからは商品データアップデート処理------------------------------------------
//Amazonは規約で24時間以上のキャッシュ保存が認められません。そこで前述のカスタムフィールドに加えている「LastUpdate」を見て、24時間が経過している場合は更新処理を行います。

function update_amazon(){
	if(is_single()){
		global $post;
		
		//LastUpdateと現在時刻の差が「60秒*60分*24時間」以上なら処理する
		if($post->LastUpdate  'AWSECommerceService',
				'AWSAccessKeyId' => AC,
				'Operation' => 'ItemLookup',
				'AssociateTag' => AF,
				'ResponseGroup' => 'ItemAttributes,Images,Reviews',
				'IdType' => 'EAN',
				'SearchIndex' => 'DVD',
				'ItemId' => $post->post_name, //EANさえあれば商品は引っ張れる
				'Timestamp' => gmdate('Y-m-dTH:i:sZ'),
			);
			$recurl = get_key($param);
			$xml = simplexml_load_file($recurl);
			if($xml->Items->Item){
				foreach($xml->Items->Item as $val2){
						update_post_meta($postid,'DetailPageURL',(String)$val2->DetailPageURL);
						update_post_meta($postid,'SmallImage',(Array)$val2->SmallImage);
						update_post_meta($postid,'MediumImage',(Array)$val2->MediumImage);
						update_post_meta($postid,'LargeImage',(Array)$val2->LargeImage);
						update_post_meta($postid,'IFrameURL',(String)$val2->CustomerReviews->IFrameURL);
						update_post_meta($postid,'LastUpdate',(String)time());
						
						//画像の差し替え
						//引数無しfile_put_contentsなので上書きされます
						$smalimg = $val2->SmallImage->URL;
						$smalimg_local = dl_image($smalimg);

						$midimg = $val2->MediumImage->URL;
						$midimg_local = dl_image($midimg);

						$largeimg = $val2->LargeImage->URL;
						$largeimg_local = dl_image($largeimg);
				}
			}

		}
	}
}//func end

//shutdownにフックすることで見る人にも優しい(はず)
add_action('shutdown','update_amazon');

これで、完了です。

タグ