はじめに
本サイトのテーマ(Iconic One)はシンプルで使いやすいのはいいのですが、少しは変化が欲しいところです。ページを訪問するごとに「少し変わったな。」と思わせるようにするという視覚的な効果も期待して(もちろん、収益もものすごく期待してます。)広告を文章の途中やサイトバーに入れています。そういうこともあるので、できれば広告の挿入位置は機動的にかつ確実に変えられるようにしたいと思っています。
Google AdSenseやAmazonアソシエイトのコードをダッシュボードからウィジェットのテキストフィールドに直接入力してしまうと、「Wordpressのウィジェットにショートコードを入れたいなぁ。」と思っていたのですが、調べる時間と気力がなく後回しにしていました。
ショートコードを入れようと思い始めて
ようやく、ググるのに必要な検索語が思いつき、ウィジェットにショートコードを入れる方法がわかったので、これを機にAmazonのProduct Advertising APIからデータを取り出してウィジェットに入れてみることにしました。
最初に下準備。
本題に入る前に、以下の手順で、ウィジェットにショートコードを入れることが本当にできるのかどうかを確認します。
- この記事を参考にさせていただいて、functions.phpの末尾(といっても、”?>”の手前です。)に以下のコードを追加します。
add_filter(‘widget_text’, ‘do_shortcode’);
- 次に、テスト用のショートコード用の関数を追加します。まずはテスト用ということで、以下のようなシンプルなものを用意しました。
function test_for_widget() { return “Hello widget world!!”; } add_shortcode(‘test-for-widget’,’test_for_widget’);
- ダッシュボードから「外観」→「ウィジェット」と選択します。
- 「ウィジェット」画面が表示されるので、テスト用のウィジェットを準備します。テキスト部には[test-for-widget]とだけ書きます。
- サイドバーに以下のように表示されていれば、テストは成功です。
Amazon Product Advertising APIを使ってデータを取り出してみる。
仕様の検討
APIを使う前に、APIに利用上の制限がないかどうか、また制限がある場合にはその制限に対する対応策を考える必要があります。
実は、Amazon Product Advertising APIは1日あたりのAPIの呼び出し回数が原則8640回までに制限されています(2019年1月現在。詳細は本記事の「補足」の項参照)。つまり、「広告のコードをただ置いておくだけ」ではこの上限に達してしまう可能性がないとは言い切れません。また、表示する広告は商品の広告になるので、商品を単純にランダムに選択して表示させるようなものにしてしまうと、リロードしたときに表示した広告が変わってしまうので、その商品に興味を持っていただいた訪問者の方を逃してしまうことになりかねません。
そこで、以下のような仕様にすることにしました。データベースをキャッシュのかわりに使用することになります。
- Amazon Product Advertising APIを実行して、商品データを取得します。商品データの取得後にHTMLのコードを作成して、これを広告として表示します。
- 作成されたHTMLのコードはWordpress用のデータベースに格納します。
- 上記のコードがデータベースに格納されたら、格納から1時間が経過するまでの間はAmazon Product Advertising APIの実行は行わずに、データベースに格納されたHTMLのコードを出力します。
- 格納から1時間が経過した後に表示要求が来たら、Amazon Product Advertising APIから商品データを取得し、HTMLのコードを作成し、これを新しい広告として表示します。作成されたHTMLのコードはデータベースに格納します。このとき、古いHTMLのコードは(少なくとも論理的には)破棄します。
上記の仕様とすることにより、Amazon Product Advertising APIによる商品データの取得の間隔を1時間以上とすることができます。
さくさくと実装です。
仕様が決まったところで、さくさくと実装です。といっても、Amazon Product Advertising APIのサイトにあるサンプルコードを元にして作ったコードをさらに改造して実装しています。
スポンサーリンク
まず、キャッシュデータ及び検索用のSQL文を書きます。Wordpress用のデータベース上に作りますので、テーブル名はWordpress用のテーブルで使われているものと重ならないようにします。
SQL文によって作成するテーブルは以下の3個です。
- amazon_product_linkテーブル: 商品の検索のための検索語を格納するテーブル。このテーブル名に”link”という単語が含まれているのは、改造前のコードで使用していたHTMLのコード片(snippet)を入れるためのカラムが存在していたからです。テーブルの作成のためのSQL文は以下の通りです。
CREATE TABLE `amazon_product_link` ( `id` int(11) NOT NULL AUTO_INCREMENT, `caption` varchar(255) DEFAULT NULL, `disabled` int(11) DEFAULT ‘0’, `search_term` varchar(1024) DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `caption` (`caption`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
- link_cacheテーブル: 作成及び表示した広告を格納するテーブルです。テーブルの作成のためのSQL文は以下の通りです。
CREATE TABLE `link_cache` ( `id` int(11) NOT NULL AUTO_INCREMENT, `card_for_index` varchar(4096) DEFAULT NULL, `card_for_page` varchar(4096) DEFAULT NULL, `generated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
- 秘密鍵などを格納するテーブル。ショートコード中に埋め込みたくなかったので、データベースのレコードとして保持することにしました。このテーブルについては、具体的な仕様は非公開とします。
次に、ショートコードを書きます。上記の仕様をほぼそのまま実装していますが、link_cacheテーブルのレコードは削除せずにそのまま蓄積し、最新のもののみ使用する実装としています。
以下のように実装してみました。ただし、秘密鍵などを読み出すためのSQL文及びそのカラム名をそのまま使用している部分についてはコードを削除し、削除した部分に日本語で説明文を入れています。
<?php | |
function create_url( |
|
// The region you are interested in | |
$endpoint = "webservices.amazon.co.jp"; | |
$uri = "/onca/xml"; | |
// Set current timestamp if not set | |
if (!isset($params["Timestamp"])) { | |
$params["Timestamp"] = gmdate('Y-m-d\TH:i:s\Z'); | |
} | |
// Sort the parameters by key | |
ksort($params); | |
$pairs = array(); | |
foreach ( |
|
array_push( |
|
} | |
// Generate the canonical query | |
|
|
// Generate the string to be signed | |
|
|
// Generate the signature required by the Product Advertising API | |
|
|
// Generate the signed URL | |
|
|
// echo "Signed URL: \"".$request_url."\""; | |
return $request_url; | |
} | |
function parse_response( |
|
$rcode = 0; | |
foreach( |
|
if (preg_match("#HTTP/[0-9\.]+\s+([0-9]+)#", |
|
|
|
} | |
} | |
if ($rcode >= 300) { | |
return ""; | |
} | |
$parsed_xml = null; | |
if (isset($response)) { | |
|
|
} | |
$content = ""; | |
$items = array(); | |
if ( |
|
foreach ( |
|
$item = array(); | |
|
|
|
|
|
|
if (isset($current->ItemAttributes->EAN)) { | |
|
|
} else { | |
|
|
} | |
|
|
|
|
|
|
} | |
} | |
//var_dump($items); | |
if (count($items) == 0) { | |
return ""; | |
} | |
return |
|
} | |
function get_card() { | |
global $wpdb; | |
$cards = array(); | |
// First, I try to get card on the database. | |
|
|
if (count($card_rows) != 0) { | |
|
|
|
|
return $cards; | |
} | |
// Get some keys from the database. | |
|
|
if (count($key_rows) != 0) { | |
|
|
|
|
|
|
} else { | |
return ''; | |
} | |
|
|
if (count($rows)==0) { | |
//echo "No rows. Abort here!\n"; | |
return ''; | |
} | |
|
|
$params = array( | |
"Service" => "AWSECommerceService", | |
"Operation" => "ItemSearch", | |
"AWSAccessKeyId" => $aws_access_key_id, | |
"AssociateTag" => $amazon_associate_id, | |
"SearchIndex" => "All", | |
"ResponseGroup" => "Images,ItemAttributes,Offers", | |
"Keywords" => $kws | |
); | |
//echo "Keywords:".$kws."\n"; | |
$context = stream_context_create(array( | |
'http' => array('ignore_errors' => true))); | |
|
|
|
|
if ($pos === FALSE) { | |
// Workaround against an error. | |
|
|
if (count($card_rows) != 0) { | |
|
|
|
|
return $cards; | |
} | |
return ''; | |
} | |
|
|
if ($item === "") { | |
return ''; | |
} | |
$ean = ''; | |
if (isset($item["EAN"])) { | |
|
|
} | |
$price = ''; | |
if (isset($item["Price"])) { | |
|
|
} | |
if (!isset($item["Title"])) { | |
error_log("Error! keyword=".urlencode($kws),0); | |
return ''; | |
} | |
$feature_str = ""; | |
$feature_max_count = 4; | |
$feature_count = 0; | |
if (count($item["Feature"]) > 0) { | |
$feature_str .= '<div style="border-top-style: solid;border-color: #1E3E8A;margin-top:4px;border-width:1px;padding-top:4px;text-align:left;">'; | |
foreach ( |
|
if ( |
|
|
|
} | |
$feature_count++; | |
} | |
|
|
$feature_str .= '</div>'; | |
} | |
$cards["index"] = '<div style="width:305px;text-align:center;margin: 0 auto;"> | |
<a href="'. |
|
<br clear="left"/> | |
</div> | |
<div class="clear"></div>'; | |
$cards["page"] = ""; | |
|
|
return $cards; | |
} | |
?> |
WordPressへの組み込み
実装が完了したら、以下の手順でWordpressへ組み込みます。
- 前項で作成したPHPファイル(ファイル名はamazon_widget.phpとします。)をfunctions.phpと同じディレクトリに置きます。
- 以下のようなショートコード組み込み用の関数をfunctions.phpに追加します。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters/* 詳細は https://pandanote.info/?p=893 を参照のこと。 */ require_once( get_template_directory() . ‘/amazon_widget.php’); function amazon_fancy_ads() { $cards = get_card(); return $cards[“index”]; } add_shortcode(‘amazon-fancy-ads’,’amazon_fancy_ads’); - ダッシュボードから「外観」→「ウィジェット」と選択します。
- 「ウィジェット」画面が表示されるので、広告の表示用のウィジェットを準備します。テキスト部には[amazon-fancy-ads]とだけ書きます。
表示例
表示例はこのページの下かサイドバーのあたりに(たぶん)ありますので、探してみてください。
まとめ
この記事ではAmazon Product Advertising APIを使ってウィジェットにちょっと見栄えが良くて、かつ挿入位置を機動的に変えることのできる広告を表示させる方法について記述しました。SEO指南的なことが書いてあるブログの記事をいくつか読んでみると、「サイドバーに置いた広告はクリックされにくい。っていうか、ほとんどクリックされない。」と書かれているのを見かけますが、あまり見慣れない形式の広告を配置すれば、その場所が本文であるかサイドバーであるかとかに関係なく、その広告を目当てにして本サイトを訪問される方が少しは増えるんではないかと固く信じて疑わない今日この頃であります。
この記事の本編は以上です。
補足
Product Advertising API 利用ポリシーが2019年1月23日から変更されています。
内容をざっくり要約すると、以下の通りになるものと理解しています。
- Amazon Product Advertising APIは1日あたりのAPIの呼び出し回数が原則8640回(=10秒に1回)まで。
- 発送済み商品売上の発生状況により発行できる回数が追加される。
- また、発送済み商品売上が30日間連続して発生しないと、次に発送済み商品売上が発生するまでAmazon Product Advertising APIの利用ができなくなる(呼び出してもエラーメッセージが返される)。
詳細についてはAmazon発表のこちらをご覧ください。