行き着く先はあんこ

WordPressにプラグインを使わず定期的に更新される人気記事を表示する

2016.7.2

WordPressには便利なプラグインがたくさんあり、人気記事を表示する「WordPress Popular Posts」も非常に便利です。しかし、なんでもかんでもプラグインを使っているとサイトが重くなってしまうこともあります。

そこで今回はプラグインを使わずにWordPressで人気記事を表示する方法を備忘録もかねてまとめておきます。

スポンサーリンク

WordPressに人気記事を表示する

「WordPress プラグイン使わずに 人気記事」などで検索するとカスタムフィールドを使った方法がヒットします。今回は以下のサイトで紹介されているコードを改良して使います。

参考[WordPress]プラグイン無しで記事のアクセス数をカウントする関数と、アクセスランキングの表示を行うスニペット|increment Log

紹介されているコードをそのまま使うとアクセスの集計期間が過去全てになってまうので、今回は集計期間を1日にするために少し改良します。また、アクセス数は1時間ごとに分けて保存し、1時間ごとに1日前のアクセス数を削除するようにします。

人気記事を表示するfunctions.phpコード

まずfunctions.phpに書くコード全体はこのようになります。

//アクセス数をカウントする
function set_post_views() {
$postID = get_the_ID();
$num = (int)date_i18n('H'); // 現在時間で番号取得
$key = 'pv_count';
$count_key = '_pv_count';
$count_array = get_post_meta( $postID, $count_key, true );
$sum_count = get_post_meta( $postID, $key, true );
if( !is_array($count_array) ) { //配列ではない
$count_array = array();
$count_array[$num] = 1;
} else { //配列である
if ( isset( $count_array[$num] ) ) { //カウント配列[n]が存在する
$count_array[$num] += 1;
} else { //カウント配列[n]が存在しない
$count_array[$num] = 1;
}
}
//アクセス数を更新する
update_post_meta( $postID, $count_key, $count_array );
update_post_meta( $postID, $key, $sum_count + 1 );
}
//アクセス数をリセットする
function reset_post_views() {
$num = (int)date_i18n('H');
$key = 'pv_count';
$reset_key = '_pv_count';
$args = array(
'posts_per_page'   => -1,
'post_type' => 'post',
'post_status'=>'publish',
'meta_key' => $reset_key,
);
$reset_posts = get_posts($args);
if($reset_posts):
foreach($reset_posts as $reset_post):
$postID = $reset_post->ID;
$count_array = get_post_meta( $postID , $reset_key, true );
if ( isset( $count_array[$num] ) ) { //カウント配列[n]が存在する
$count_array[$num] = 0;
}
//アクセス数をリセットする
update_post_meta( $postID, $reset_key, $count_array );
update_post_meta( $postID, $key, array_sum( $count_array ) );
endforeach;
endif;
}
//リセット関数を実行するアクションフックを追加
add_action( 'set_hours_event', 'reset_post_views' );
//実行間隔の追加
function my_interval( $schedules ) {
// 1時間ごとを追加
$schedules['1hours'] = array(
'interval' => 3600,
'display' => 'every 1 hours'
);
return $schedules;
}
add_filter( 'cron_schedules', 'my_interval' );
//アクションフックを定期的に実行するスケジュールイベントの追加
function my_activation() {
if ( ! wp_next_scheduled( 'set_hours_event' ) ) {
wp_schedule_event( 1451574000, '1hours', 'set_hours_event' );
}
}
add_action('wp', 'my_activation');
//ボットの判別
function isBot() {
$bot_list = array (
'Googlebot',
'Yahoo! Slurp',
'Mediapartners-Google',
'msnbot',
'bingbot',
'MJ12bot',
'Ezooms',
'pirst; MSIE 8.0;',
'Google Web Preview',
'ia_archiver',
'Sogou web spider',
'Googlebot-Mobile',
'AhrefsBot',
'YandexBot',
'Purebot',
'Baiduspider',
'UnwindFetchor',
'TweetmemeBot',
'MetaURI',
'PaperLiBot',
'Showyoubot',
'JS-Kit',
'PostRank',
'Crowsnest',
'PycURL',
'bitlybot',
'Hatena',
'facebookexternalhit',
'NINJA bot',
'YahooCacheSystem',
'NHN Corp.',
'Steeler',
'DoCoMo',
);
$is_bot = false;
foreach ($bot_list as $bot) {
if (stripos($_SERVER['HTTP_USER_AGENT'], $bot) !== false) {
$is_bot = true;
break;
}
}
return $is_bot;
}

それぞれの関数を簡単に見ていきます。

アクセス数をカウント

function set_post_views()

この関数は呼ばれるごとにカスタムフィールドの保存してあるデータにアクセス数をカウントしていきます。

基本的には1時間ごとのアクセス数を配列_pv_countに保存して、合計値をpv_countに保存します。つまり過去1日のアクセス数はカスタムフィールドpv_countに保存されている値になります。

アクセス数をリセット

function reset_post_views()

この関数では、すべての記事においてアクセス数が保存されている配列_pv_count[現在時間H]の値を0にリセットします。これと以下のコードが定期的に人気記事ランキングを更新する方法のキモになります。

定期的にreset_post_viewを実行する

function my_interval( $schedules ) 
function my_activation()

これは先程のreset_post_viewを定期的に実行するためのコードです。1時間ごとに動作するようにスケジュールイベントを追加してreset_post_viewsを実行させます。

WordPressでイベントを定期的に動作させる方法はLIGさんのサイトで詳しく紹介されています。

参考ルーティンを効率化。WordPressで定期的な処理をさせる方法|株式会社LIG

ボットの判別


function isBot() {
$bot_list = array (
//除外したいボットのユーザーエージェントをここに追加
);
}

これはアクセスしているのがボットなのかを判別するためのコードです。ボットならtrueを、ボットでなければfalseを返します。ちなみにこれがないとアクセス数が異常に多くなるため、正確なランキング表示ができなくなる可能性があります。また、アクセスから除外したいボットが別途あれば「bot_list」にユーザーエージェントを追加することで対応できます。

参考【PHP】アクセスがBOTかどうか判断する|PHP初心者コピペTIPS

以上でfunctions.phpのコードは全てです。あとはサイドバーなり記事下なりに人気記事を表示するコードを書いていきます。

人気記事を表示する

実際に人気記事を表示するコードの一例はこんな感じです。これをsidebar.phpの表示したいところに追加します。

<?php if( is_single() && !is_user_logged_in() && !isBot() ): //個別記事 かつ ログインしていない かつ 非ボット
set_post_views(); //アクセスをカウントする
endif; ?>
<section class="popular-box">
<h4>人気記事</h4>
<?php
$args = array(
'post_type'     => 'post',
'numberposts'   => 8,       //表示数
'meta_key'      => 'pv_count',
'orderby'       => 'meta_value_num',
'order'         => 'DESC',
);
$posts = get_posts( $args );
if( $posts ): ?>
<ul>
<?php foreach( $posts as $post ) : setup_postdata( $post ); ?>
<li>
<a href="<?php the_permalink(); ?>" >
<div><?php the_post_thumbnail( 'thumbnail' ); ?></div>
<div><h5><?php the_title(); ?></h5></div>
</a>
</li>
<?php endforeach;
wp_reset_postdata(); ?>
</ul>
<?php else : ?>
<p>アクセスランキングはまだ集計されていません。</p>
<?php endif; ?>
</section>

問題がなければこれで人気記事が表示されるはずです。個別の記事を見てみるとカスタムフィールドにアクセス数が保存されていることが分かります。

問題点

この人気記事を表示する方法の問題点は、大きく3つあります。

1つ目は、1時間ごとにreset_post_viewを実行するには1時間ごとにサイトにアクセスされる必要があることです。WordPressには擬似cronを使って定期的にイベントを実行させることができますが、これが実行されるタイミングはサイトへのアクセスが確認された時です。

今回の場合だと、1時間たってもアクセスが無ければ1日前のその時間におけるアクセスがリセットされない、ということになります。

これを解決するには、指定時間内に最低1回はアクセスされるサイトにすることが重要です。

2つ目は、ボットのアクセス除去です。今回のコードでもボットによるアクセスはできるだけ除去するようになっていますがどうしても除外できていないボットもあります。そのためAnalyticsなどと比べると若干アクセスが多くなってしまいます。

これを解決するにはボットのユーザーエージェントをひとつひとつ追加するしかないのかな?

3つ目は、記事が多くなると処理が重くなることです。今回の人気記事を表示する方法には、ページが表示されるごとに全ての記事のカスタムフィールドを検索するようになっています。そのため数万記事くらいになるともしかすると処理が重くなる可能性もあります。この問題に関しては、今後対処する予定です。

また、(いつになるか分かりませんが)集計期間やリセット間隔の変更が柔軟に行えるようにしたいとも思っています。

さいごに

とりあえずこれでWordPressでプラグインを使わずに定期的にランキングを更新する人気記事を表示することができるようになりました。

問題点はありますが、使えないほどの影響が出るわけではありません。

更新内容

2016年6月29日
アクセス数をリセットする関数の改善。

2016年7月2日
無駄にカスタムフィールドが生成・削除を繰り返す問題を解決。

スポンサーリンク

スポンサーリンク

コメント

  1. 2016.11.30 16:33

    […] →WordPressにプラグインを使わず定期的に更新される人気記事を表示する […]

  2. げんちゃん

    2017.07.18 22:52

    かずらさん

    こんばんは、度々のコメント失礼します。

    先程、AMPの適用/非適用の記事にコメントさせて頂いたものです。

    今回の記事も大変参考になりました、最近勉強を始めたばかりの素人ですので、コードを載せて頂けるのは本当に有り難いです。

    そんな、かずらさんに可能であればお願いしたことが・・・

    実は、AMP化したのですが、WordPress Popular Postsで出力される記事のサムネイルがimgタグで出力されてしまい、AMPエラーが出てしまいます。

    今回ご紹介頂いている「プラグインを使わず定期的に更新される人気記事を表示する」をAMP対応させた場合のコードを教えて頂けないでしょうか。

    まだ私にはhtmlとCSSしかわからず、色々と試したものの、情けない話ですが自分ではどうにも出来ません。

    お忙しいとは思いますが、気が向いたら是非記事にして頂けると助かります。

    差し出がましいお願いとは存じておりますが、何卒ご検討頂けますようお願い致します。

    • kzr(管理人)

      2017.07.20 20:11

      結論をいいますと、いまのところAMP対応コードを紹介する予定はありません。

      理由はいくつかあるのですが、そのうちの1つとして、このコード自体が汚く処理性能がいいものではないことがあります。(改善するといいながらはや1年……)
      他にも、AMP表示自体は問題ないのですがAMPそのものが特殊なので、この方法ではページカウントが思い通りに動きません。(試してないけど恐らく)

      人気記事の表示方法は、げんちゃんさんの環境にもよりますが、AMP対応している人気記事プラグインをつかうのが確実な方法だと思います。

      • kzr(管理人)

        2017.07.20 20:25

        PHPのコードを書く必要はありますが、「Jetpack」プラグインだとAMPページでも上手くカウントされます。たぶん。

        • げんちゃん

          2017.07.22 00:51

          お忙しい中返信頂きありがとうございました!

          JETPACKは使っていなかったので、早速試してみます(*^^*)

          またお邪魔させていただきます。

          取り急ぎ御礼デス

コメントを残す