PHPによる自前のWebアクセス解析ツール作成を検討

January 8, 2009 – 12:18 pm

我がブログへのアクセスを解析するツールを作ってみようと考えた。世の中には無料のアクセス解析ツールがかなりの数出回っている。このブログにも、SiteMeterとeXTReMeのふたつの商用ツールの無料配布版を活用させてもらっている。そんなとき、敢えて自前の解析ツールを新たに作ってみようとするのは、「還暦から始める動的Webサイト構築」としてやっているLAMP学習を効果的に進めようとしたからだ。それなりのWebプログラミングの素養を身につけるには、具体的な課題を設定して実際にプログラミングしてみるのが近道だ。うまくいけば、今使っている無料ツールを凌駕するようなツールを作れるかもしれないなんて、多少は考えた(無理かな?)。

何を解析する?: まず、アクセス解析と称して何をしようとするのか?このあたりを明確にしておかないとツール作成作業が始まらない。仕様を明確にというところだ。

作成しようとするツールの仕様を一言で言うと、我がブログにアクセスしてくれた読者が、いつ、何を手がかりに、どのページにアクセスし、アクセスした後、どのように我がブログのなかをナビゲートしながら記事を読んでくれたかを知ることができれば良いといったところだ。これに加え、アクセス数などが一定程度、まとめて知ることができれば良いだろう。

こうした情報を取得することによって、我がブログの構成などが多少なりとも改善されればいいのではと思う。

PHPでアクセス解析はどこまで可能?: アクセス解析ツールを作ろうとする場合、Javaスクリプトを活用するというのが順当なところだと思う。残念ながら、いまのところ、僕にはJavaを自在に扱うだけの能力を持ち合わせていない。ということで、PHPのみで可能な範囲で解析ツールを作ってみようと考えた。

PHP(のみ)でアクセス解析しようとする場合の唯一(?)の方法はアクセス時にクライアント側から送られてくるHTTPのヘッダー情報を活用するということだろう。ヘッダー情報は、phpの予約変数 $_SERVERを活用すれば取得できる。どんな情報が取得できるのかまとめてみると:

$_SERVER[‘HTTP_REFERER’]: 現在のページに遷移する間にUser Agentが参照していたページのアドレス
$_SERVER[‘HTTP_USER_AGENT’]: WebサーバーにリクエストしてきたUser Agentの情報(印)。これには、WebへのVisitorが用いているブラウザ情報などがふくまれる。
$_SERVER[‘REMOTE_ADDR’]: VisitorのIPアドレス。
$_SERVER[‘REMOTE_HOST’]: Visitorのホスト名。IPアドレスから逆引きでもとめたもの。Apacheの設定で”HostnameLookups ON”とすることが必要。
$_SERVER[‘REMOTE_URI’]: ページにアクセスするために指定されたURI。例えば、/index.html。

簡単なアクセス解析では、大体、上記の情報、もしくはこれら情報を加工すると得ることができる。ただ、HTTP_REFEREとHTTP_USER_AGENTは、UserAgentがセットするものだ。なんらかのかたちで吟味したのちに使用する必要がある。

解析ツール作成はどのように進めるのか?: アクセス解析を行うために具体的にどのようにするのか。まず、上記の$_SERVER変数で得られる情報をWebへのアクセス(というかリクエスト)が発生する度に、適当な加工をしたのちData Base上に書き込む仕組み(スクリプト)を作らなければならない。そして、作成したスクリプトはブログのヘッダーあるいはサイドバー上に配置すればよい。

次に、DB上に書き込まれた情報を、必要に応じて、読み出す仕組みが必要だ。情報の読み出す形式は、最終的には、ブラウザ上で適当なユーザーインタフェースを作ることも必要になる。

とりあえず、アクセス情報の取得とこれをDB上に書き込むphpスクリプトを書き、情報読み取り用のphpコマンドを作成してみることにしよう。

スクリプトの作成とWebサーバー上への配置: 今回の作業では、データ読み取り用のphp関数get-access()を作成し、これを以下の要領で、ブログのサイドバーに配置した:

<?php
require_once(‘DIRECTORY/get-access.php’);
get-access();
?>
        

ここで、DIRECTORYは、関数get-access()を書いたget-access.phpが置かれているディレクトリ名を指している。get-access.phpのソースは、この記事の最後にDB作成用sql文、関連プログラムととに掲げることにする。

アクセス解析ツールを作成してみて見えてきたもの: 実際にアクセス解析用ツールを作成しよとすると、想定していなかった様々な問題が見えてきた。これらの問題には、ブログへのアクセスのうち、「ユニークアクセス」をどのように定義すればよいのか?我がブログ記事を読んでくれる読者とクローラなど機械的なアクセスをどのように区別すればいいのか?などなど、具体的にアクセス解析をしようとすると、結構、やっかいな問題がでてくる。こうした問題については、次回以降の別エントリで議論することにしよう。

なお、以下のsourceは、今後のエントリで議論を進めるうえでの素材であることに注意してほしい。

 

=== get-access.php source ========================

<?php
function get_access() {
       require_once(dirname(__FILE__) . "/Access.php");
       require_once(dirname(__FILE__) . "/AccessDao.php");

       $time         = date('Y-m-d H:i:s');
       $ip_address   = $_SERVER['REMOTE_ADDR'];
       $host_name    = $_SERVER['REMOTE_HOST'];
       $user_agent   = $_SERVER['HTTP_USER_AGENT'];
       $reff_uri     = $_SERVER['HTTP_REFERER'];
       $page_visited = $_SERVER['REQUEST_URI'];
       if ( $host_name == '' ) $host_name = $ip_address;
       if ( strpos($page_visited, '/favicon.ico') !== false ) return;
       if ( strncmp( $host_name, '150.70.84', 9 ) == 0 )      return;
       if ( strcmp(  $host_name, 'ci-b21.u-aizu.ac.jp') ==0)  return;
       $browser = get_browser( null, true );
       if ( $browser['crawler'] ) return;
       if ( $browser['platform'] == 'unknown' ) return;
//
//  referrer ---> host/path query
//      set search engine
//
//
       if ( $reff_uri !== '' ) {
                $url      = parse_url( $reff_uri );
                $ref_host = $url['host'];
                if ( $ref_host == 'memorandum.yamasnet.com' ) {
                     $uniqueness = 'NON_UNIQUE';
                }
                else {
                     $uniqueness = 'UNIQUE';
                }
                $url_host = $url['host'] . $url['path'];
                $query = $url['query'];
                if ( $query !=='' ) {
                        parse_str( $query, $output );
                        if ((strpos($url_host,'google')!==false) &&(strpos( $url_host,'/search')!==false)) {
                            $key = $output['q'];
                            $search_engine = 'google';
                        }
                        else if ((strpos($url_host,'google')!==false) &&(strpos( $url_host,'/blogsearch')!==false)) {
                            $key = $output['q'];
                            $search_engine = 'google-blogsearch';
                        }
                        else if ( strpos( $url_host, 'search.biglobe' ) !==false ) {
                            $key = $output['q'];
                            $search_engine = 'biglobe';
                        }
                        else if ( strpos( $url_host, 'search.live.com' ) !==false ) {
                            $key = $output['q'];
                            $search_engine = 'msn';
                        }
                        else if ( strpos ( $url_host, 'search.auone.jp' ) !==false ) {
                            $key = $output['q'];
                            $search_engine = 'auone';
                        }
                        else if ( strpos( $url_host, 'search.goo.ne.jp' ) !==false ) {
                            $key = $output['MT'];
                            $search_engine = 'goo';
                        }
                        else if ( strpos( $url_host, 'search.yahoo' ) !==false ) {
                            $key = $output['p'];
                            $search_engine = 'yahoo';
                        }
                        else if ( strpos( $url_host, 'search.www.infoseek' ) !==false ) {
                            $key = $output['qt'];
                            $search_engine = 'infoseek';
                        }
                        else {
                            $search_engine = '';
                            if (isset($output['q']))       $key=$output['q'];
                            else if (isset($output['p']))  $key=$output['p'];
                            else if (isset($output['MT'])) $key=$output['qt'];
                            else if (isset($output['qt'])) $key=$output['MT'];
                            else                           $key='';
                        }
                }
                else {
                        $key = '';
                        $search_engine = '';
                }
       }
       else {
            $uniqueness = 'UNIQUE';
       }
// In the case where the same visitor exists within 20 minutes, NON_UNIQUE otherwise UNIQUE
       $visitor =  mysql_escape_string($host_name);
       $interval= '20';
       $accessdao = New AccessDao();
       $access_array = $accessdao->getLastAccess($visitor, $interval);
// Get number of records of $access_array
       $no = sizeof( $access_array );
       if ( $no > 0 ) $uniqueness = 'NON_UNIQUE';
       else           $uniqueness = 'UNIQUE';
       $access_array = NULL;
       $accessdao    = NULL;
//
       $access = new Access();
       $time = mysql_escape_string($time);
       $access->setTime($time);
       if ($host_name )       $host_name = mysql_escape_string($host_name);
       else                   $host_name = mysql_escape_string($ip_address);
       $access->setVisitor($host_name);
       $ip_address = mysql_escape_string($ip_address);
       $access->setIpAddress($ip_address);
       $uniqueness = mysql_escape_string($uniqueness);
       $access->setUniqueness($uniqueness);
       $reff_uri = mysql_escape_string($reff_uri);
       $access->setReffUri($reff_uri);
       $ref_host = mysql_escape_string($ref_host);
       $access->setRefferer($ref_host);
       $search_engine = mysql_escape_string($search_engine);
       $access->setSearchEngine($search_engine);
       $keywords = mysql_escape_string($key);
       $access->setKeyWords($keywords);
       $page_visited = mysql_escape_string($page_visited);
       $access->setPageVisited($page_visited);
       $user_agent = mysql_escape_string($user_agent);
       $access->setUserAgent($user_agent);
//
       $accessdao = new AccessDao();
       $accessdao->insertAccess($access);
//
       $access = NULL;
       $accessdao = NULL;
}
?>
        

==== DB Table 作成用 SQL文: access.sql ============================

DROP TABLE IF EXISTS access;
DROP TABLE IF EXISTS mypage;
CREATE TABLE mypage (
    id INTEGER(8) PRIMARY KEY AUTO_INCREMENT,
    page_visited VARCHAR(128) NOT NULL,
    title VARCHAR(256) NOT NULL
);
CREATE TABLE access (
    id INTEGER(8) PRIMARY KEY AUTO_INCREMENT,
    time TIMESTAMP,
    visitor VARCHAR(128) NOT NULL,
    ip_address VARCHAR(16) NOT NULL,
    uniqueness VARCHAR(16) NOT NULL,
    reff_uri  VARCHAR(512) NOT NULL,
    refferer  VARCHAR(128) NOT NULL,
    search_engine VARCHAR(32) NOT NULL,
    keywords VARCHAR(128) NOT NULL,
    page_id INTEGER(8),
    user_agent VARCHAR(128) NOT NULL
);
ALTER TABLE access ADD FOREIGN KEY (page_id)
  REFERENCES mypage (id);
        

========= Access.php =======source==========================

<?php
class Access {
        private $id           = "";
        private $time         = "";
        private $visitor      = "";
        private $ip_address   = "";
        private $uniqueness   = "";
        private $reff_uri     = "";
        private $refferer     = "";
        private $search_enging= "";
        private $keywords     = "";
        private $page_visited = "";
        private $page_title   = "";
        private $user_agent   = "";
//
        public function toString() {
           return (string) ($this->id . "\t" . $this->time . "\t" . $this->visitor . "\t" .
                $this->ip_address . "\t" . $this->uniqueness . "\t" . $this->reff_uri . "\t" .
                $this->refferer . "\t" . $this->search_engine . "\t" . $this->keywords . "\t" .
                $this->page_visited . "\t" . $this->page_title . "\t" . $this->user_agent );
        }
//
        public function getId() {
           return $this->id;
        }
        public function setId($id) {
           $this->id = $id;
        }
        public function getTime() {
           return $this->time;
        }
        public function setTime($time) {
           $this->time = $time;
        }
        public function getVisitor() {
           return $this->visitor;
        }
        public function setVisitor($visitor) {
           $this->visitor = $visitor;
        }
        public function getIpAddress() {
           return $this->ip_address;
        }
        public function setIpAddress($ip_address) {
           $this->ip_address = $ip_address;
        }
        public function getUniqueness() {
           return $this->uniqueness;
        }
        public function setUniqueness($uniqueness) {
           $this->uniqueness = $uniqueness;
        }
        public function getReffUri() {
           return $this->reff_uri;
        }
        public function setReffUri($reff_uri) {
           $this->reff_uri = $reff_uri;
        }
        public function getRefferer() {
           return $this->refferer;
        }
        public function setRefferer($refferer) {
           $this->refferer = $refferer;
        }
        public function getSearchEngine() {
           return $this->search_engine;
        }
        public function setSearchEngine($search_engine) {
           $this->search_engine = $search_engine;
        }
        public function getKeyWords() {
           return $this->keywords;
        }
        public function setKeyWords($keywords) {
           $this->keywords = $keywords;
        }
        public function getPageVisited() {
           return $this->page_visited;
        }
        public function setPageVisited($page_visited) {
           $this->page_visited = $page_visited;
        }
        public function getPageTitle() {
           return $this->page_title;
        }
        public function setPageTitle($page_title) {
           $this->page_title = $page_title;
        }
        public function getUserAgent() {
           return $this->user_agent;
        }
        public function setUserAgent($user_agent) {
           $this->user_agent = $user_agent;
        }
}
?>
        

======= AccessDao.php ===========source==================

<?php
Class AccessDao {
        private $mysqli = null;
        //Constructor
        function __constructor() {
           $this->connect();
        }
        //Destructor
        function __destruct() {
           $this->disconnect();
        }
        //Connect to MySQL server
        private function connect() {
           if(is_null($this->mysqli)) {
               $this->mysqli = new mysqli("localhost", "user_name", "password", "DataBase");
               $this->mysqli->query("SET NAMES utf8");
               if(mysqli_connect_errno()) {
                    die("Failed to connect to MySQL server<br></br> Reason:" . mysqli_connect_error());
               }
           }
        }
        //Disconnect from MySQL server
        private function disconnect() {
           is_null($this->mysqli) or $this->mysqli->close();
        }
//
        public function insertAccess ( $access ) {
            is_null($this->mysqli) and $this->connect();
        // page_visited to page_id in table mypage;
            $sql = "SELECT id FROM mypage WHERE page_visited='" . $access->getPageVisited() . "'";
            $result = $this->mysqli->query($sql);
            if ( $result->num_rows == 0 ) {
               if ( strpos( $access->getPageVisited(), '.html' ) ) {
                    if (have_posts()) : while (have_posts()) : the_post();
                    $new_title = get_the_title();
                    endwhile; endif;
               }
               else {
                    $new_title = $access->getPageVisited();
               }
               $sql = "INSERT INTO mypage values('',
                       '" . $access->getPageVisited() . "', '" . $new_title . "')";
               if (!$this->mysqli->query($sql)) {
                       print "Failed to register(1010) " . $this->mysqli->error . "\n";
               }
               else {
                       $page_id    = $this->mysqli->insert_id;
                       $page_title = '$new_title';
               }
            }
            else {
                  $row = $result->fetch_array(MYSQLI_ASSOC);
                  $page_id = $row["id"];
                  $sql = "SELECT title FROM mypage WHERE id =' " . $page_id . "'";
                  if(!$this->mysqli->query($sql)) {
                       print "Failed to register(1011) " . $this->mysqli->error . "\n";
                  }
                  else {
                      $page_title = $row["title"];
                  }
            }
//
        // Set access table for new data
            $sql = "INSERT INTO access values('',
                           '" . $access->getTime() . "',
                           '" . $access->getVisitor() . "',
                           '" . $access->getIpAddress() . "',
                           '" . $access->getUniqueness() . "',
                           '" . $access->getReffUri()   . "',
                           '" . $access->getRefferer() . "',
                           '" . $access->getSearchEngine() . "',
                           '" . $access->getKeyWords() . "',
                           '" . $page_id . "',
                           '" . $access->getUserAgent() . "')";

            if(!$this->mysqli->query($sql) ) {
                 print "FAILED TO SET DATA(1012) " . $this->mysqli->error . "\n";
            }
        }
//
        public function getLastAccess($visitor, $interval) {
            is_null($this->mysqli) and $this->connect();
       // Get last data record for the visitor($visitor, $inteval)
            $sql = "SELECT access.id, time, visitor, ip_address, uniqueness, reff_uri, refferer,"
                 . "search_engine, keywords, page_visited, title, user_agent "
                 . "FROM access, mypage "
                 . "WHERE visitor= '" . $visitor . "' AND "
                 . "      access.page_id = mypage.id  AND "
                 . "      time > SUBDATE( NOW(), INTERVAL " . $interval . " MINUTE ) "
                 . "ORDER BY time DESC";
           //
           $result = $this->mysqli->query($sql);
           $access_array = array();
//
           while($row = $result->fetch_array(MYSQLI_ASSOC)) {
                $access = new Access();
                $access->setId($row["id"]);
                $access->setTime($row["time"]);
                $access->setVisitor($row["visitor"]);
                $access->setIpAddress($row["ip_address"]);
                $access->setUniqueness($row["uniqueness"]);
                $access->setReffUri($row["reff_uri"]);
                $access->setRefferer($row["refferer"]);
                $access->setSearchEngine($row["search_engine"]);
                $access->setKeyWords($row["keywords"]);
                $access->setPageVisited($row["page_visited"]);
                $access->setPageTitle($row["title"]);
                $access->setUserAgent($row["user_agent"]);
//      
                $access_array[] = $access;
           }
           $result->close();

           return $access_array;
        }
//
        public function getUniqueAccess($interval,$max_record) {
            is_null($this->mysqli) and $this->connect();
       // Get unique access
            $sql = "SELECT access.id, time, visitor, ip_address, uniqueness, reff_uri, refferer,"
                 . "search_engine, keywords, page_visited, title, user_agent "
                 . "FROM access, mypage "
                 . "WHERE uniqueness= 'UNIQUE' AND "
                 . "      access.page_id = mypage.id AND "
                 . "       time > SUBDATE( NOW(), INTERVAL " . $interval . " DAY) "
                 . "ORDER BY time DESC";
        //
           $result = $this->mysqli->query($sql);
           $access_array = array();
        //
           for ( $i=0; $i<$max_record; $i++ ) {
                if(!$row=$result->fetch_array(MYSQLI_ASSOC)) break;
                $access = new Access();
                $access->setId($row["id"]);
                $access->setTime($row["time"]);
                $access->setVisitor($row["visitor"]);
                $access->setIpAddress($row["ip_address"]);
                $access->setUniqueness($row["uniqueness"]);
                $access->setReffUri($row["reff_uri"]);
                $access->setRefferer($row["refferer"]);
                $access->setSearchEngine($row["search_engine"]);
                $access->setKeyWords($row["keywords"]);
                $access->setPageVisited($row["page_visited"]);
                $access->setPageTitle($row["title"]);
                $access->setUserAgent($row["user_agent"]);
                $access_array[] = $access;
           }
           $result->close();
        //
           return $access_array;
        }
        public function getPageViews($visitor,$time) {
            is_null($this->mysqli) and $this->connect();
       // Get unique access
            $sql = "SELECT access.id, time, visitor, ip_address, uniqueness, reff_uri, refferer,"
                 . "search_engine, keywords, page_visited, title, user_agent "
                 . "FROM access, mypage "
                 . "WHERE visitor='" . $visitor . "' AND "
                 . "      access.page_id = mypage.id AND "
                 . "       time >='" . $time    . "' "
                 . "ORDER BY time";
      //
           $result = $this->mysqli->query($sql);
           $access_array = array();
      //
           while($row = $result->fetch_array(MYSQLI_ASSOC)) {
                if( $row["time"]>$time && $row["uniqueness"]=='UNIQUE' ) break;
                $access = new Access();
                $access->setId($row["id"]);
                $access->setTime($row["time"]);
                $access->setVisitor($row["visitor"]);
                $access->setIpAddress($row["ip_address"]);
                $access->setUniqueness($row["uniqueness"]);
                $access->setReffUri($row["reff_uri"]);
                $access->setRefferer($row["refferer"]);
                $access->setSearchEngine($row["search_engine"]);
                $access->setKeyWords($row["keywords"]);
                $access->setPageVisited($row["page_visited"]);
                $access->setPageTitle($row["title"]);
                $access->setUserAgent($row["user_agent"]);
                $access_array[] = $access;
           }
           $result->close();
     //
           return $access_array;
        }
//
        public function getAccessRank( $intv_month ) {
        // This function return access object in the form of access_ranking within interval from now
            is_null($this->mysqli) and $this->connect();

            $sql = "SELECT time, page_id, COUNT(*) AS num_access "
                 . "FROM access "
                 . "GROUP BY page_id "
                 . "HAVING time>SUBDATE(NOW(),INTERVAL " . $intv_month . " MONTH ) "
                 . "ORDER BY num_access DESC";

           // echo $sql . "\n";

           $result= $this->mysqli->query($sql);
           $rank_array = array();

           for ( $i=0; $i<20; $i++ ) {
               $row = $result->fetch_array(MYSQL_ASSOC);

                    $sql = "SELECT title, page_visited FROM mypage WHERE id=" .  $row["page_id"] ;
                    // echo $sql . "\n";
                    if( $res=$this->mysqli->query($sql) ) {
                         $row01   = $res->fetch_array(MYSQLI_ASSOC);
                    }
                    else {
                         break;
                    }
         //
               $rank = new Rank();
               $rank->setPageVisited($row01["page_visited"]);
               $rank->setPageTitle($row01["title"]);
               $rank->setNumAccess($row["num_access"]);
               $rank_array[] = $rank;
          }
          $result->close();

          return $rank_array;
      }
}
        

==========Rank.php======source==============

<?php
class Rank {
        private $page_visited = "";
        private $page_title   = "";
        private $num_access   = "";

        public function toString() {
           return (string) ($this->page_visited . "\t" . $this->page_title . "\t" . $this->num_access );
        }
        public function getPageVisited() {
           return $this->page_visited;
        }
        public function setPageVisited($page_visited) {
           $this->page_visited = $page_visited;
        }
        public function getPageTitle() {
           return $this->page_title;
        }
        public function setPageTitle($page_title) {
           $this->page_title = $page_title;
        }

        public function getNumAccess() {
           return $this->num_access;
        }
        public function setNumAccess($num_access) {
           $this->num_access = $num_access;
        }
}
?>
        

========command.php=====================

<?php
//
require_once("AccessDao.php");
require_once("Access.php");
//
        $interval='1';
        $max_record = 100;
//
        $accessdao = new AccessDao();
        $access_array = $accessdao->getUniqueAccess($interval,$max_record);
//
        $no = sizeof( $access_array );
//
        echo "No. of Record = " . $no . "\n";
//
        if ( $access_array ) {
             for ($i=0; $i<sizeof($access_array); $i++) {
                 echo "Time    = " . $access_array[$i]->getTime() . "\n";
                 echo "Visitor = " . $access_array[$i]->getVisitor() . "\n";
                 echo "Refferer= " . $access_array[$i]->getRefferer() . "\n";
                 echo "Keywords= " . $access_array[$i]->getKeyWords() . "\n";
                 echo "User_Agent=" . $access_array[$i]->getUserAgent() . "\n";
//
                 $visitor = $access_array[$i]->getVisitor();
                 $time    = $access_array[$i]->getTime();
                 $page_array = $accessdao->getPageViews($visitor, $time);
                 $no_page    = sizeof($page_array);
                     echo " No of Pages Visited=" . $no_page . "\n";
                 for ($j=0; $j<sizeof($page_array); $j++) {
                     echo "      Time         = " . $page_array[$j]->getTime() . "\n";
                     echo "      Page_title   = " . $page_array[$j]->getPageTitle() . "\n";
                 }
                 echo "\n\n";
             }
        }
        else {
             echo "NO Return \n";
        }
//
        $access_array = NULL;
        $accessdao    = NULL;
?>
        

=========== ranktest.php =======================

<?php
require_once("AccessDao.php");
require_once("Rank.php");
//
        $interval='1';
//
        $accessdao = new AccessDao();
        $rank_array = $accessdao->getAccessRank($interval);

        $no = sizeof( $rank_array );

        echo "No. of Record = " . $no . "\n";

        if ( $rank_array ) {
             for ($i=0; $i<sizeof($rank_array); $i++) {
                 echo "Page Title   = " . $rank_array[$i]->getPageTitle() . "\n";
                 echo "NumAccess    = " . $rank_array[$i]->getNumAccess() . "\n";
             }
        }
        else {
             echo "NO Return \n";
        }
//
        $rank_array = NULL;
        $accessdao  = NULL;
?>