Jetpack by WordPress.comの情報を取得して人気記事を表示する

Jetpack by WordPress.comの情報を取得して人気記事を表示する

WordPress.comの統計情報データって日付で区切れるし結構有用性高いよなー、このデータって取得できないのかなーと思っていたら出来ました!ヽ(゚∀゚)ノ それを使って関数とか作ってみたのでメモがてら。


これまでの紆余曲折

だいぶ前から人気記事表示には手を焼いておりまして、2012年末あたりに初めて導入したのがWordpress Popular Posts。調べてみたら一番情報が多かったという理由でした。それが2013年7月頃、サムネイル表示に指定した大きさの画像が自動生成されるという仕様になったことによりWP-PostViewsへ移行。同じレイアウトになるようにごねごね。

問題はなかったのですが、WP-PostViewsはプラグインを有効にしてから全期間の統計しか取得できないので、どうしてもランキングが固定されてしまい、いつ見ても同じ記事しか表示されないのがちょっと気になってくる。週間や月間などで範囲を区切れたほうが変動が見えて良いなぁと、2014年3月頃またWordpress Popular Postsに戻って画像生成がされないようカスタマイズして使用。

しばらく問題なく動いていたように見えたものの、明らかに人気のなさそうな記事が上位にきたり、精度がイマイチな気が…。前使ってたときはあまり気にならなかったんだけどなぁ。記事数が増えて、botとかも増えたのかもしれない。

WordPress.com 統計情報のデータは使えないのか

なんか良い方法ないかなーと思ってふと気づいたのが、以前から入れていたこちらのプラグイン。

盛りだくさんの機能が使えるプラグインですが、この中の「WordPress.com 統計情報」を非常に重宝しておりまして。特にこのリファラー。

140527-1

GoogleAnalyticsよりも簡単に見れて、リンクも貼ってあるのでとっても見易いんです。「おお、こんなところからアクセスが!」っていうのが楽しいので、よく見てます。

この統計情報、人気記事や検索キーワード、クリックしたリンクなども詳細に見ることができます。

140527-2

人気記事もこのように表示できて、日付で区切る機能がある…ッ!精度も問題ないし、このデータ使いたいー!!

しかしながら、このプラグインはWordPress.comと連携しているので、データはそっちのデータベースに格納されてるようです。そのおかげでブログに負荷をかけずに使えてとっても良いのですが、どうにかそのデータは持ってこれないものかなぁと調べてみたら、

(( ゚д゚)ガタッ  で き る ぞ ッ !!!

試してみる

<?php $top_posts = stats_get_csv( 'postviews', 'days=7&limit=10' ); ?>

参考にさせていただいたコードで、期間は1週間(7日間)、10件の人気記事を取得してみる。

<?php var_dump($top_posts); ?>

試しに取得した配列の中身を見てみると、

140527-3

でたー!(*゚Д゚*)

投稿ID、タイトル、パーマリンク、閲覧数が入ってる!なんて素敵なんだ!!トップページは投稿IDがゼロになるみたいだし、これを除外してやれば十分使えるぞ…!

というわけで、これを使って、パラメータを変更して使える関数を作ってみました。

日数と件数を指定して取得

functions.php

function my_pop_list( $target_days, $n ) {
	$i = 0;
	$args = array( 'days'=>$target_days, 'limit'=>$n+2 );
	$top_posts = stats_get_csv( 'postviews', $args );
	echo "<ol>\n";
	foreach ( $top_posts as $value ) {
		$my_id = $value['post_id']; //投稿ID取得
		if ( $my_id != 0 && $my_id != get_the_ID() && get_post_type($my_id) == 'post' ) { //homeと現在のページは除外で、投稿のみ
			if( has_post_thumbnail($my_id) ) { //サムネイルの有無
				$pop_img = get_the_post_thumbnail( $my_id, array(100, 100), array('alt'=>get_the_title($my_id)) );
			} else {
				$pop_img = '<img src="noimage.png" width="100" height="100" />';
			}
			echo '<li><a href="'.$value['post_permalink'].'">'.$pop_img.$value['post_title'].'</a>('.$value['views']."views)</li>\n";
			$i++;
			if ( $i >= $n ) { break; } //指定数を超えたら終了
		}	
	}
	echo "</ol>\n";
}

変数で指定された期間と件数分の、サムネイル、リンク付きタイトル、閲覧数をolリスト形式で出力します。

3行目で件数($n)+2 件になっているのは、除外するページを考慮しているためです。例えば5件表示したいのに、5位以内に除外ページ(現在のページやトップページなど)が入っていたら、5件分に足りなくなってしまうからです。除外するページがなければ +2 は不要です。

8行目に「homeと現在のページは除外、投稿記事のみ」という条件が書いてありますので、お好みで調整してください。

追記:取得できない場合はメンテナンス中表示

function my_pop_list( $target_days, $n ) {
	$i = 0;
	$args = array( 'days'=>$target_days, 'limit'=>$n+2 );
	$top_posts = stats_get_csv( 'postviews', $args );
	if ( !$top_posts ) {
		echo "<p>只今メンテナンス中です。</p>\n";
	} else {
		echo "<ol>\n";
		foreach ( $top_posts as $value ) {
			$my_id = $value['post_id']; //投稿ID取得
			if ( $my_id != 0 && $my_id != get_the_ID() && get_post_type($my_id) == 'post' ) { //homeと現在のページは除外で、投稿のみ
				if( has_post_thumbnail($my_id) ) { //サムネイルの有無
					$pop_img = get_the_post_thumbnail( $my_id, array(100, 100), array('alt'=>get_the_title($my_id)) );
				} else {
					$pop_img = '<img src="noimage.png" width="100" height="100" />';
				}
				echo '<li><a href="'.$value['post_permalink'].'">'.$pop_img.$value['post_title'].'</a>('.$value['views']."views)</li>\n";
				$i++;
				if ( $i >= $n ) { break; } //指定数を超えたら終了
			}	
		}
		echo "</ol>\n";
	}
}

たまに jetpack 側の不具合で一時的にデータを取ってこれないこともあるので、こうやって書いておくと親切かもです。

任意のファイル(single.phpなど)

<?php my_pop_list( 7, 5 ); ?>

上記の関数を呼び出したい場所でこう書きます。期間、件数の順で指定します。この例だと7日間の人気記事を5件取得します。

更に特定のカテゴリで絞り込みたい場合

functions.php

function my_pop_cat_list( $target_days, $target_cat, $n ) {
	$i = 0;
	$args = array( 'days'=>$target_days, 'limit'=>100 );
	$top_posts = stats_get_csv( 'postviews', $args );
	echo "<ol>\n";
	foreach ( $top_posts as $value ) {
		$my_id = $value['post_id']; //投稿ID取得
		if ( $my_id != 0 && $my_id != get_the_ID() && get_post_type($my_id) == 'post' ) { //homeと現在のページは除外で、投稿のみ
			$cat_ids = wp_get_post_categories( $my_id, 'fields=ids' ); //カテゴリ-ID配列
			$cat_flg = false; //フラグリセット
			foreach ( $cat_ids as $cat_id ) { //該当カテゴリIDが対象カテゴリIDに含まれるか
				if ( in_array($cat_id, $target_cat) ) { $cat_flg = true; }
			}
			if ( $cat_flg ) { //対象のものだけ
				if( has_post_thumbnail($my_id) ) { //サムネイルの有無
					$pop_img = get_the_post_thumbnail( $my_id, array(100, 100), array('alt'=>get_the_title($my_id)) );
				} else {
					$pop_img = '<img src="noimage.png" width="100" height="100" />';
				}
				echo '<li><a href="'.$value['post_permalink'].'">'.$pop_img.$value['post_title'].'</a>('.$value['views']."views)</li>\n";
				$i++;
				if ( $i >= $n ) { break; } //指定数を超えたら終了
			}
		}	
	}
	echo "</ol>\n";
}

3行目、先ほどと同じく統計情報から取得する件数ですが、カテゴリで絞る場合は多めにしておかねばなりません。総記事数が多く、かつマイナーカテゴリが指定された場合は、100件取得してもそのカテゴリが必要数出てこない可能性もありますので、サイトの規模を考慮して設定する必要があります。

全投稿数を取得して、その数値を指定するのも良いかもしれません。

追記:と、思って試してみたら上限は200みたいです…。それ以上の数を指定しても200しかとってこれませんでした。 -1に指定すると無制限となるらしいですが、これも上限は200でした。

あとはだいたい同じですが、ランキングの記事IDからカテゴリ配列を取得し、それが指定のカテゴリに含まれる場合はフラグを立てる(12行目)ということをして、フラグが立っているものだけ出力していく、という方法をとっています。

データを取得できない場合の書き方は先ほどの追記部分をご参考ください。

single.php

<?php my_pop_cat_list( 30, array(1, 2, 3), 5 ); ?>

関数を呼び出したい場所で、期間、カテゴリID配列、件数の順に指定します。この例だと30日間のカテゴリID1, 2, 3 に属する人気記事を5件取得します。

追記:最初ここに$array()と書いてしまっていましたが、正しくはarray()です、すみません(;´Д`)
//現在のカテゴリIDを配列で取得
$now_cat_id = wp_get_post_categories( $post->ID, 'fields=ids' );
//現在以外のカテゴリIDを配列で取得
$args = array( 'exclude'=>$now_cat_id, 'fields'=>'ids' );
$ext_cat_id = get_terms( 'category', $args );

ちなみに、現在表示されている記事のカテゴリID配列と、それ以外のカテゴリ配列はこのように書いて取得できるので、

//現在表示のカテゴリ月間人気5件
my_pop_cat_list( 30, $now_cat_id, 5 );
//現在以外のカテゴリ月間人気5件
my_pop_cat_list( 30, $ext_cat_id, 5 );

取得したカテゴリIDをそれぞれ指定してさっきの関数を呼び出してやれば実装できます。

人気記事と被らない関連記事を表示したい

かなりマニア向けな内容かもしれないのですがw、このブログの場合、記事終了後に、関連記事、現在カテゴリ人気記事、現在以外カテゴリ人気記事、の順番で表示させてるんですが、たまーに関連記事とカテゴリ人気記事に、同じ記事が表示されたりすることがあったんです。これが気になっちゃって((└(:3」┌)┘))

なんかこう、もったいないというか気持ち悪いというか。。関連記事は同じタグを持つ記事をランダムで出してるんですが、確かに関連してるんだから被ってしまう可能性も高いわけで。

というわけで、関連記事も含めて1件も記事が被らないように実装してみました。

functions.php

function my_pop_array( $target_days, $target_cat, $n ) {
	$i = 0;
	$args = array( 'days'=>$target_days, 'limit'=>100 );
	$top_posts = stats_get_csv( 'postviews', $args );
	foreach ( $top_posts as $value ) {
		$my_id = $value['post_id'];
		if ( $my_id != 0 && $my_id != get_the_ID() && get_post_type($my_id) == 'post' ) { //homeと現在のページは除外で、投稿のみ
			$cat_ids = wp_get_post_categories( $id, 'fields=ids' ); //カテゴリ-ID配列
			$cat_flg = false; //フラグリセット
			foreach ( $cat_ids as $cat_id ) { //該当カテゴリIDが対象カテゴリIDに含まれるか
				if ( in_array($cat_id, $target_cat) ) { $cat_flg = true; }
			}
			if ( $cat_flg ) { //対象のものだけ
				$my_array[$i] = $my_id;
				$i++;
				if ( $i >= $n ) { break; } //指定数を超えたら終了
			}
		}	
	}
	return $my_array;
}

さっきは対象をリスト形式で出力していたのですが、ここでは出力しないで記事IDだけ配列に格納して返す、という内容の関数にします。

single.php

<?php
//現在のカテゴリIDを配列で取得
$now_cat_id = wp_get_post_categories( $post->ID, 'fields=ids' );
//他カテゴリIDを配列で取得
$args = array( 'exclude'=>$now_cat_id, 'fields'=>'ids' );
$ext_cat_id = get_terms( 'category', $args );

//現在カテゴリの人気記事IDを配列で取得
$now_cat_pop = my_pop_array( 30, $now_cat_id, 5 );
//他カテゴリの人気記事IDを配列で取得
$ext_cat_pop = my_pop_array( 30, $ext_cat_id, 5 );

//人気記事ID配列を結合
$add = array_merge( $now_cat_pop, $ext_cat_pop );
//人気記事 + 現在記事のID配列
array_push( $add, get_the_ID() );
?>

それぞれのカテゴリIDを取得して(~6行)期間と件数を指定した人気記事の記事IDの配列を取得(~11行)した後、このふたつの配列を合成して(14行)、更に現在表示している記事IDもその配列に追加します(16行)。

これで、$addという配列には、カテゴリ人気記事ID、他カテゴリ人気記事、現在表示されている記事、全ての記事IDが格納されていることになります。(例では、人気記事IDそれぞれ5つ + 現在記事IDで、全部で11個の記事IDが格納されます)

これを除外する、という条件で関連記事を出してやります。

<h3>関連記事</h3>
<ul class="relation">
<?php
$tags = wp_get_post_tags( $post->ID, 'fields=ids' ); //現在のタグIDを配列で取得
global $post;
$args = array(
	'posts_per_page' => 5,
	'tag__in' => $tags,
	'post__not_in' => $add,
	'orderby' => 'rand'
);
$myposts = get_posts( $args );
if ( !$myposts ) {
	echo "<li>記事がありません。</li>";
} else {
	foreach( $myposts as $post ) {
		setup_postdata($post);
		if( has_post_thumbnail() ) { //サムネイルの有無
			$rl_img = get_the_post_thumbnail( $post->ID, array(100, 100), array('alt'=>get_the_title()) );
		} else {
			$rl_img = '<img src="noimage.png" width="100" height="100" />';
		}
		?>
		<li><a href="<?php the_permalink();?>"><?php echo $rl_img; ?><?php the_title(); ?></a></li>
		<?php
	}
}
wp_reset_postdata();
?>
</ul>

ハイライト部分にて、さっき合成した配列を指定して、これらの記事IDを除外します。

関連記事についての過去記事はこちら

で、この下に人気記事IDが入ってる配列を使って希望のhtmlを出力します。

<h3>人気記事</h3>
<ol>
<?php
foreach ( $now_cat_pop as $value ) {
	if( has_post_thumbnail($value) ) { //サムネイルの有無
		$pop_img = get_the_post_thumbnail( $value, array(100, 100), array('alt'=>get_the_title($value)) );
	} else {
		$pop_img = '<img src="noimage.png" width="100" height="100" />';
	}
	$permalink = esc_url( apply_filters( 'the_permalink', get_permalink($value) ) ); //パーマリンクを取得
	echo '<li><a href="' . $permalink . '">' . $pop_img . get_the_title($value) . "</a></li>\n";
}
?>
</ol>

これは現在カテゴリのほうですが、ハイライト部の$now_cat_pop$ext_cat_popに変えてやれば他カテゴリのものが出力されます。

パーマリンクの取得(10行目)は、

  • WordPressでget_*()を使うときは念のためソースを確認して適切に処理すべし! | firegoby

こちらのとおり、get_permalink()だけだとフィルターフックやエスケープを通っていないので、このあたりの処理を加えてthe_permalink()と同じ値になるように取得すると安心です。

追記:取得できない場合はメンテナンス中表示

<h3>人気記事</h3>
<?php
if ( !$now_cat_pop ) {
	echo "<p>只今メンテナンス中です。</p>\n";
} else {
	echo "<ol>\n";
	foreach ( $now_cat_pop as $value ) {
		if( has_post_thumbnail($value) ) { //サムネイルの有無
			$pop_img = get_the_post_thumbnail( $value, array(100, 100), array('alt'=>get_the_title($value)) );
		} else {
			$pop_img = '<img src="noimage.png" width="100" height="100" />';
		}
		$permalink = esc_url( apply_filters( 'the_permalink', get_permalink($value) ) ); //パーマリンクを取得
		echo '<li><a href="' . $permalink . '">' . $pop_img . get_the_title($value) . "</a></li>\n";
	}
	echo "</ol>\n";
}
?>

こちらも、jetpack 側の不具合で読み込めないときのために、このようにしておくと良いかもです。

以上です。非常ーーーにマニアックな情報でしたが、どなたかのお役に立てたら光栄です!

公開日:2014/05/27
更新日:2015/03/19

1件のピンバック

10件のコメント

  1. makoto より:

    とても参考になりました。
    ありがとうございます。

    早々自分のサイトに導入してみました。

    • *you より:

      makotoさん、コメントありがとうございます。
      お役に立てて光栄ですー!(*´ω`*)

  2. ケイタ より:

    何てピンポイントで素晴らしい記事なんだ~(;´Д`)b

    Jetpackは「WordPress.com 統計情報」と「モニター」の2つしか使っていませんで、統計情報をイイ感じに使えないのかなと思っていました。

    ※「モニター」はサイトが落ちていないかチェックしてくれてるようです。

    ちなみに有効にしている人が多い「Photon」ですが、頻繁に更新するサイトでは、キャッシュが邪魔して画像表示が上手くいかない場合があるみたいです。Google Chromeのデベロッパー・ツールがエラーを検地したり。

    しかしJetpackは、やり方次第でかなり使えるプラグインですね。

    • *you より:

      ケイタさん、コメントありがとうございます!

      Jetpackほんとに多機能でスゴイですよねー!わたしは統計情報しか使ってないのでかなり持ち腐れな気がしていますw

  3. ケイタ より:

    1箇所、訂正が必要かと思いましたのでコメントします。

    「更に特定のカテゴリで絞り込みたい場合」のコード。

    誤 $array
    正 array

    $が不要ですね。

    • *you より:

      ;`;:゙;`(;゚;ж;゚; )ブフォッ!!!

      ほ…、ほんとだ…!すみません、何故いままで気が付かなかったのかお恥ずかしい(;´Д`) ご指摘ありがとうございますー!早速訂正させていただきました!

  4. ケイタ より:

    もしご存知でしたらと思い、コメントさせて頂きます。
    何度もすみません・・・(´Д`)

    プラグイン「Advanced Custom Fields」を使い、投稿記事に「発売日」「販売価格」を掲載しています。

    発売日 → release
    販売価格 → price

    このmeta情報を、ランキングでも表示できないものかと苦戦中です。(ノД`)

    echo部分に、パーマリンクやサムネイルなどに加えて、

    ‘発売日:’, $value[‘post_meta(release)’]
    ‘販売価格:’, $value[‘post_meta(price)’]

    と追記してみたのですが、表示できず。。
    get_post_meta() を使うのだろうと思うのですが、解決策をご存知ありませんでしょうか?

    よろしければ、お手すきのときにご確認くださると幸いです。

    • *you より:

      ケイタさん、こんにちは。わたしはAdvanced Custom Fieldsというプラグインは使ったことがなかったのですが、

      こちらの記事を参考にさせてもらうと、メインループ内ではthe_field('フィールド名')で表示、get_field('フィールド名')で取得が出来るようです。ループ外ではthe_field('フィールド名', 記事ID)のように第二引数にIDを指定できるそうなので、

      foreach ( $top_posts as $value ) {
      	$my_id = $value['post_id']; //記事ID
      	if ( 略 ) { //いろいろ条件
      		$str = "<li>\n";
      		$str .= '<a href="'.$value['post_permalink'].'">'.$value['post_title']."</a>\n";
      		$str .= '発売日:'.get_field('release', $my_id)."\n";
      		$str .= '販売価格:'.get_field('price', $my_id)."\n";
      		$str .= "</li>\n";
      		echo $str;
      	}
      }
      

      このように書けば出力できるのではないでしょうか。

      ちゃんと検証できたわけではないので確実性がなくて申し訳ないですが、ご参考になれば幸いです!

  5. ケイタ より:

    おかげさまで問題が解決しました!(`・ω・´)

    ご提示くださったコードを見て、難しく考えていたことに気づきました。
    素直に値を指定してやれば良かったですね。。

    この度はご多忙の中、参考サイトまで探してくださってありがとうございました。
    もう大好きです(´・ω・`)b

    • *you より:

      解決できて良かったですー!こちらこそ勉強になりました!ヽ(*´∀`)ノ


コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください

コメントは承認制ですので、反映までしばらくお待ち下さい。(稀にスパムの誤判定にて届かないこともあるようですので、必要な際はお問い合わせからお願い致します。)

YouTubeでQ&Aコンテンツを企画しています

運営しているYouTubeチャンネルで、ご相談やご質問を募集しています。動画のコメントやお問い合わせページからお気軽にご相談をお寄せください。