記事中に設置したアフィリエイトリンクの修正は非常に面倒な問題。これに対処するため以前より様々な機能をワードプレスに実装してきたが、今回はリンク切れチェック機能と自動リンク修正機能を実装してみる

Amazonアソシエイト用のリンクの使い勝手を向上するために、これまでにいくつかの機能を実装していきた。

  • アフィリエイトリンクのショートコード化
  • 商品を管理画面で一元管理化

ざっくり言えばこんな感じの機能をワードプレスに実装した。ここまでの詳細は以下の記事。

Amzonアフィリエイトリンクをショートコードで作成する

ASPのツールもアソシエイトツールバーも使わずにアマゾンのアフェリエイトリンクをそれなりのデザインで生成する方法

Amazonアフィリエイトリンクを管理画面で一元管理する

記事にショートコードでアフィリエイトリンクを挿入していたが、リンク切れの修正が面倒。管理画面で一元管理できるように修正してみ…

これだけでも十分便利になって気がするけど・・・

ツン子

そうなんだけど、やっぱりリンク切れチェックを入れたいね。ユーザーの立場からするとリンク切れ踏まされるとかマジクソだと思うんだよ

ちたまん

というわけで、今回はリンク切れに対応してみよう。

完成イメージ

いきなりコードを見せてもイメージが湧かないと思うので、完成図から・・・

管理画面でリンク切れを一目でわかるようにする

アップデート内容

管理画面はこんな感じ。図のようにリンク切れを起こした商品は赤色で強調表示される。

そしてこのリンク切れチェックはAmazon商品のページを開いた際に自動実行される。ただ開くたびに毎回実行する意味はないので、前回のチェックから一定時間経過した場合のみチェックを実行するようにした。1日1回で十分だと思うけど今回はとりあえず3時間経過したら再チェックとした。

リンク切れの場合、表示内容を変更する

出力結果

図は通常時とリンク切れ時の比較。リンク切れを起こした場合。該当する商品ページが無いので、画像の取得が出来なくなる。そこでデザインはクソだけど汎用性のある代替え画像に変更する。

またリンク先も商品ページでなく検索結果ページに変更する。そのためボタンの表示も「見る」から「探す」に変更した

芸が細かい・・・。文字まで変えなくてもいいと思うんだけど

ツン子

まあそうなんだけど、こうしておけば少なくとも嘘はついていないからね。ユーザーの怒りを和らげられるかなと・・・

ちたまん
注意

商品画像を事前に保存するようにすれば代替画像は不要だけど、Amazonの商品画像を保存して使用することは禁じられている

リンク切れチェック機能の実装方法

リンク切れを確認するには該当ページのHTTPレスポンスコードを確認すればよい

HTTPレスポンスコードとは?

レスポンスコードはサーバーにアクセスした際にブラウザに返される3桁のコード。正常に接続できた場合は200が返ってくる。ページが存在しない場合だと404。他にも状況に応じていくつかある

PHPでレスポンスコードを取得する方法はいくつかあり、通常はget_headersを使うが今回はcurl_multiを使用する。

1個のリンクであればget_headerで大丈夫。ただ100件とかになると時間がかかりすぎてエラーになるらしい。そこで並列処理ができるcurl_multiを使う

データベースにカラムを追加

<?php

/*************************************************

	Data Base
	
*************************************************/
global $wpdb;

//DBのバージョン
$cmt_db_version = '0.1.8';

//現在のDBバージョン取得
$installed_ver = get_option( 'cmt_meta_version');

// DBバージョンが違ったら実行
if( $installed_ver != $cmt_db_version ) {
	require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
	
	$sql = "CREATE TABLE " . $wpdb->prefix .'AFF_AMZ_T' . " (
			ID_C bigint UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
			DP_C char(10),
			BRAND_C char(30),
			TITLE_C char(60),
			SEARCH_C char(60),
			URL_C char(255),
			R_CODE_C smallint UNSIGNED
			
		)
		CHARACTER SET 'utf8';";
	
	dbDelta($sql);
	
?>

Amazon商品はデーターベースで管理している。レスポンスコードの情報を保存する必要があるのでカラムの追加が必要だ。phpMyAdminから追加すれば良いが、新規作成時を考慮してテーブル作成部分のコードも変更した。

R_CODE_Cが今回追加したレスポンスコード用のカラム

リンク切れチェックのための関数

<?php
function checkHttpCode($mode){
	
	global $wpdb;
	$fg = true;
	
	//追加、修正、それ以外かを判定→それぞれ処理
	if(empty($mode)){
		$sql = "SELECT * FROM ".$wpdb->prefix .'AFF_AMZ_T ORDER BY ID_C';
		
		$lDt = get_option( 'chcLastDate');
		$cDt = current_time('mysql');
		
		if(!empty($lDt)){

			$time1 = new DateTime($cDt);
			$time2 = new DateTime($lDt);

			$diff = $time1->diff($time2);
			
			if($diff->format('%H') >= 3){
				
				update_option('chcLastDate', $cDt);
				
			}else{
				
				$fg = false;
				
			}
		}else{
			
			update_option('chcLastDate', $cDt);
			
		}
		
	}elseif($mode=='add'){
		$sql = "SELECT * FROM ".$wpdb->prefix .'AFF_AMZ_T ORDER BY ID_C DESC LIMIT 1';
	}else{
		$sql = $wpdb->prepare("SELECT * FROM ".$wpdb->prefix .'AFF_AMZ_T WHERE ID_C = %d',$mode);
	}
	
	//テーブルのデータを取得
	$results = $wpdb->get_results( $sql );
	
	if(count($results) != 0 && $fg){
		
		//マルチハンドル初期化
		$mh = curl_multi_init();
		$ch_array = array();
		
		//レコード毎にCurlの設定を行う
		foreach($results as $r){
			
			$URL = 'https://www.amazon.co.jp/dp/' .$r->DP_C.'/';
			$ch = curl_init();
			$ch_array[] = $ch;
			curl_setopt_array($ch, array(
				CURLOPT_URL               => $URL,
				//CURLOPT_NOBODY            => TRUE,
				CURLOPT_FOLLOWLOCATION    => FALSE,
				CURLOPT_RETURNTRANSFER    => 1,
				CURLOPT_USERAGENT         => '',
			));
			curl_multi_add_handle($mh, $ch);
		}
		
		//curl_multiを実行
		do {
			curl_multi_exec($mh, $running);
			curl_multi_select($mh);
		} while ($running > 0);
		
		//結果を配列に保存
		foreach($ch_array as $ch){

			$linkstatus[] = curl_getinfo($ch,CURLINFO_HTTP_CODE);
			curl_multi_remove_handle($mh, $ch);
			curl_close($ch);
		}
		
		//配列を元にテーブルを更新
		$i = 0;
		foreach($results as $r){
			$wpdb->update( 
				$wpdb->prefix .'AFF_AMZ_T', 
				array( 
					'R_CODE_C' => $linkstatus[$i]
				), 
				array( 'ID_C' => $r->ID_C ), 
				array('%d') , 
				array('%d') 
			);
			$i++;
		}
	}
}
?>

この関数の大まかな流れは

  1. 追加、修正、それ以外かを判定
  2. テーブルのデータを取得
  3. レコード毎にCurlの設定を行う
  4. curl_multiを実行
  5. 結果を配列に保存
  6. 配列を元にテーブルを更新

追加、修正、それ以外かを判定

今回は登録商品一覧のリンク切れチェックを行うことが主な目的だけど、商品の追加や修正をしたときには入力ミスを考えるとその商品だけチェックしたい。

引数$modeには’add’またはレコードIDがセットされている。’add’なら新規追加、レコードIDなら修正。emptyならそれ以外なので登録商品一覧に対しての処理となる。

登録商品一覧に対しての処理の場合は前述したように一定以上時間が経過した場合のみチェックを行いたいので、前回チェック時の日時をワードプレスのオプション値として「chcLastDate」に保存しておき現在日時と比較する処理をいれてある。

AmzonはBodyの取得が必須?

57行目のcurl_setopt_arrayが対象URLから情報を取得する際の様々なオプションを設定する部分。

今回必要なのはレスポンスコードなのでヘッダーさえ取得すればよい。つまりCURLOPT_NOBODYはTrueで良い。しかしAmazonの場合はこれを入れると200が返ってこない。コメントアウトしたら動作した

他にも正しく取得するための条件があるっぽくて、例えばCURLOPT_USERAGENTは空欄でもいいから必須のようだ。

MEMO

Amazonの情報をCurlで取得する場合は、USERAGENTを指定した上で設定をシンプルにすることがポイント

BODYを取ってくると結局時間がかかる・・・。

ちたまん

管理ページのコードを修正

<?php

function add_aff_submenu_page_1(){
	
	global $wpdb;

	if(!empty($_POST['mode'])){
		if( $_POST['mode'] == 'add' && !empty($_POST['dp']) ){
		
			$wpdb->insert( 
				$wpdb->prefix .'AFF_AMZ_T', 
				array(
					'DP_C' => $_POST['dp'],
					'BRAND_C' => $_POST['brand'],
					'TITLE_C' => $_POST['title'],
					'SEARCH_C' => $_POST['search'],
					'URL_C' => $_POST['url']
				), 
				array('%s','%s','%s','%s','%s') 
			);
		}else{
			$wpdb->update( 
				$wpdb->prefix .'AFF_AMZ_T', 
				array( 
					'DP_C' => $_POST['dp'],
					'BRAND_C' => $_POST['brand'],
					'TITLE_C' => $_POST['title'],
					'SEARCH_C' => $_POST['search'],
					'URL_C' => $_POST['url']
				), 
				array( 'ID_C' => $_POST['mode'] ), 
				array('%s','%s','%s','%s','%s') , 
				array( '%d' ) 
			);
		}
	}
	
	checkHttpCode($_POST['mode']);
	
	$sql = "SELECT * FROM ".$wpdb->prefix .'AFF_AMZ_T';

	$results = $wpdb->get_results( $sql );
	
	?>
	
	<div class="wrap myMenu" >

		<h2>Amazon商品管理</h2>
		<h3>新規商品追加</h3>
		<form method="post" action="" enctype="multipart/form-data" encoding="multipart/form-data">
			
			<table border="1" style="border-collapse: collapse;">
				<tr>
					
					<th>DP / ブランド</th>
					<th>タイトル</th>
					<th>検索ワード</th>
					<th>画像URL</th>
					<th>操作</th>

				</tr>
				<tr>
					
					<td>
						<input type="text" name="dp" value="" style="width:12em;">
						<input type="text" name="brand" value="" style="width:12em;">
					</td>
					<td><textarea name="title" style="width:15em;height:4.8em;"></textarea></td>
					<td><textarea name="search" style="width:15em;height:4.8em;"></textarea></td>
					<td><textarea name="url" style="width:20em;height:4.8em;"></textarea></td>
					<td>
						<input type="hidden" name="mode" value="add" />
						<input type="submit" value="登録する">
					</td>
				</tr>
			</table>
			
		</form>
		
		<h3>登録済み商品</h3>
		
		<p>リンク切れチェック:<?php echo get_option( 'chcLastDate'); ?></p>
		
		<table border="1" style="border-collapse: collapse;">
			<tr>
				<th>画像</th>
				<th>DP / ブランド</th>
				<th>タイトル</th>
				<th>検索ワード</th>
				<th>画像URL</th>
				<th>操作</th>

			</tr>
			<?php foreach($results as $r):?>
			<?php
				
				if(!empty($r->URL_C)){
					$imgUrl = $r->URL_C;
				}else{
					$imgUrl = 'https://images-fe.ssl-images-amazon.com/images/P/' . $r->DP_C . '.09.MZZZZZZZ.jpg';
				}
				
				$errCalss = '';
				
				if($r->R_CODE_C != 200){
					$errCalss = 'err';
				}else{
					
				}
			?>
			<tr class="<?php echo $errCalss ?>">
				<form method="post" action="" enctype="multipart/form-data" encoding="multipart/form-data">
					
					<td><img src="<?php echo $imgUrl?>" /></td>
					<td>
						<input type="text" name="dp" value="<?php echo $r->DP_C; ?>" style="width:12em;" />
						<input type="text" name="brand" value="<?php echo $r->BRAND_C; ?>" style="width:12em;" />
					</td>
					<td><textarea name="title" style="width:15em;height:4.8em;"><?php echo $r->TITLE_C; ?></textarea></td>
					<td><textarea name="search" style="width:15em;height:4.8em;"><?php echo $r->SEARCH_C; ?></textarea></td>
					<td><textarea name="url" style="width:15em;height:4.8em;"><?php echo $r->URL_C; ?></textarea></td>
					<td>
						<input type="hidden" name="mode" value="<?php echo $r->ID_C; ?>" />
						<button type="submit">修正する</button>
						<button type="button" onclick="window.open('https://www.amazon.co.jp/dp/<?php echo $r->DP_C; ?>', '_blank')">商品ページ</button>
						<button type="button" onClick="copyClip(this)" value="

該当する商品がみつかりません

">ショートコード</button> </td> </form> </tr> <?php endforeach; ?> </table> </div> <script> function copyClip(obj){ navigator.clipboard.writeText(obj.value); }; </script> <?php } ?>

こちらはさほど変更はない。38行目で先ほど説明したcheckHttpCode関数を実行することでレスポンスコードを取得している。

あとは105行目からの部分でレスポンスコードが200でない場合にクラスを付与している。このクラスに対してCSSを記述すれば強調表示できる。

ショートコード修正

<?php

// Amazon商品ページ
function createAmzLink($atts){
	
	global $wpdb;
	
	$id = get_option('amzID');
	
	$sql = "SELECT * FROM ".$wpdb->prefix .'AFF_AMZ_T';

	$results = $wpdb->get_results( $wpdb->prepare(
		"SELECT * FROM ".$wpdb->prefix .'AFF_AMZ_T WHERE ID_C = %d',
		$atts['id']
	));
	
	if(count($results) > 0){
		if($results[0]->R_CODE_C == 200){
			$url = 'https://www.amazon.co.jp/exec/obidos/ASIN/' .$results[0]->DP_C.'/'.$id;
			
			$label = 'Amazonで見る';
			
			if(!empty($results[0]->URL_C)){
				$imgUrl = $results[0]->URL_C;
			}else{
				$imgUrl = 'https://images-fe.ssl-images-amazon.com/images/P/' . $results[0]->DP_C . '.09.MZZZZZZZ.jpg';
			}
			
		}else{
			
			$url = 'https://www.amazon.co.jp/s?k=' .$results[0]->SEARCH_C . '&tag='.$id;
			$label = 'Amazonで探す';
			
			$imgUrl = get_template_directory_uri().'/img/osusume.jpg';
		}
		
		$rUrl = 'https://search.rakuten.co.jp/search/mall/' .$results[0]->SEARCH_C;
		
		$html = '<ul class="amzLink fPadding1x">'."\n";
		$html .= '	<li class="imgArea"><a href="' .$url . '" target="_blank"><img src="'. $imgUrl .'" /></a></li>'."\n";
		$html .= '	<li class="bodyArea">';
		$html .= '		<h4>'. $results[0]->BRAND_C .'</h4>'."\n";
		$html .= '		<h5>'. $results[0]->TITLE_C .'</h5>'."\n";
		$html .= '		<ul class="linkList">'."\n";
		$html .= '			<li class="amz"><a class="linkBtn" href="' .$url. '" target="_blank">' . $label . '</a></li>'."\n";
		$html .= '			<li class="raku"><a class="linkBtn" href="' .$rUrl. '" target="_blank">楽天で探す</a></li>'."\n";
		$html .= '		</ul>'."\n";
		$html .= '	</li>'."\n";
		$html .= '</ul>'."\n";
	}else{
		$html = '<p>該当する商品がみつかりません</p>';
	}
	return $html;
	
}

add_shortcode('amzLink','createAmzLink');
?>

ショートコードもレスポンスコードに応じて処理を分岐するだけなので簡単な修正だけ

ただAmazonの検索ページのURLが問題でこちらに関しては情報が見つからなかった

www.amazon.co.jp/s?k=キーワード&tag=アソシエイトID

そこでアソシエイトツールバーのURLを元にこのようにしてみた

動作確認ツールで検証

リンクの動作確認ツール結果

Amazonアソシエイトにはリンクの動作を確認するツールがある。

上図の通りツールではどちらのURLも正しいと出たので、とりあえずこれで良しとした。

ただ動作は保証できないのであしからず。

MEMO

上記コードの楽天のURLは通常のリンクなので、アフィリエイトリンクが良い場合は修正が必要

一応これで動くけど

とりあえずこれで目的は達成できた。

ただ今回紹介したコードはあくまでもとりあえず動く状態でしかない。実運用にはエラーチェックなどを追加する必要があると思うので実際には今後もブラッシュアップを続けていくつもり。

URLエンコードすらしてないしね!

ちたまん

そうやって後回しにして放置→あとでテンパる
までがテンプレw

ツン子