行き着く先はあんこ

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

スポンサーリンク

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

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

WordPress に人気記事を表示する

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

紹介されているコードをそのまま使うとアクセスの集計期間が過去全てになってまうので、今回は集計期間を 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 さんのサイトで詳しく紹介されています。

ボットの判別

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

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

以上で 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><h5><?php the_title(); ?></h5>
        </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 日

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

スポンサーリンク

スポンサーリンク