您的每一次访问wuguangnuo.cn,都会在本站留下足迹:我会获取用户的 USER_AGENT 并将其进行分析应用于我的网站统计中。


网站访客统计

1.数据的获取及存储

除了管理页面中的几个页面外,其余的页面均会在控制器中进行迁至操作

protected function _initialize() {
    $Vistor = M('vistor');
    $Vistor->create();
    $Vistor->lk = $_SERVER['PHP_SELF'];
    $Vistor->ip = ip2long(get_client_ip());
    $Vistor->ag = $_SERVER['HTTP_USER_AGENT'];
    $Vistor->tm = date('Y-m-d H:i:s');
    $Vistor->add();
}

采集当前页面链接、用户IP、agent、时间。并将这些信息储存在数据表 wu_vistor 中:

CREATE TABLE IF NOT EXISTS `wu_vistor` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `lk` varchar(255) DEFAULT NULL COMMENT 'link',
  `ip` int(4) DEFAULT NULL COMMENT 'ip',
  `ag` varchar(255) DEFAULT NULL COMMENT 'agent',
  `tm` datetime DEFAULT NULL COMMENT 'datetime',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COMMENT='访客统计' AUTO_INCREMENT=1;

2.数据的分析

获取到了用户的信息,当然要对这些信息进行分析和统计。在这里用了table表格统计和echert图表统计,其中图表统计中又详细的进行了访客类型、受访页面、操作系统、客户端的分类统计。本文只讨论表格统计的实现。



操作系统统计图

表格中展示了编号、受访页面、IP、地区、ISP、操作系统、浏览器、时间等信息。其中编号、受访页面、IP、时间都在数据库中单独存储,同时也方便读取。

地区和ISP的判定使用了 UTFWry.dat 数据包。入参IP就可以读取更多信息

$Location = new \Org\Net\IpLocation('UTFWry.dat');
foreach($list as &$v) {
  $v['ip'] = long2ip($v['ip']);
  $v['os'] = get_os($v['ag']);
  $v['br'] = get_br($v['ag']);
  $v = array_merge($v, $Location->getlocation($v['ip']));
}

get_os 和 get_br 分别是获取操作系统和获取浏览器类型的函数。

这两个函数使用正则表达式来获取系统类型和浏览器类型,例如

if (preg_match('/win/i', $agent) && preg_match('/nt\s*6.1/i', $agent)) {
	$os = 'Windows 7';
} else if (preg_match('/win/i', $agent) && preg_match('/nt\s*6.2/i', $agent)) {
	$os = 'Windows 8';
} else if (preg_match('/win/i', $agent) && preg_match('/nt\s*10.0/i', $agent)) {
	$os = 'Windows 10';


if (stripos($agent, 'Firefox/')) {
	preg_match('/Firefox\/([^;)]+)+/i', $agent, $b);
	$exp[0] = 'Firefox';
	$exp[1] = $b[1]; //获取火狐浏览器的版本号
} else if (stripos($agent, 'Chrome')) {
	preg_match('/Chrome\/([\d\.]+)/i', $agent, $google);
	$exp[0] = 'Chrome';
	$exp[1] = $google[1]; //获取google chrome的版本号
}

这样就获取到了我们所需的全部信息。

这样,再合并分页信息返回就可以展示了

$data['list'] = $list;
$data['pages'] = $p->show();
$this->ajaxReturn($data);

(分页的实现将来会讲)

3.数据的展示

数据展示提供了受访页面筛选,时间区间筛选,展示全部/仅访客/仅蜘蛛,分页等快捷操作。

时间的部分最简单,将用户输入的时间规范化就可以加入到 SQL 语句中查询了。 datetimepicker 插件可以参考我的 demo ,我对其进行了优化。展示范围也比较简单,展示全部就不过滤,仅展示网络蜘蛛就限定ag like '%spider%' or ag like '%bot%',反之则not like。 


受访页面部分为了规范化和将来的扩展性,我没有将其写死,而是新建了一张字典表。


配合 bootstrap-select 插件,就可以正确的获取受访页面,展示文字,同时也兼顾拓展性了。

loadTable 全部代码如下:

public function loadTable($link = null, $date1 = null, $date2 = null, $check = 'all') {
    if(!empty($link)){
        $arr = explode(',', $link);
        $where['lk'] = array('like', $arr, 'or');
    }
    if(!empty($date1) && !empty($date2)){
        if($date1 > $date2){
            $date1 = $date1 ^ $date2;
            $date2 = $date1 ^ $date2;
            $date1 = $date1 ^ $date2;
        }
        $where['tm'] = array('between',"$date1,$date2");
    } else if (!empty($date1)){
        $where['tm'] = array('gt',"$date1");
    } else if (!empty($date2)){
        $where['tm'] = array('lt',"$date2");
    }        
    switch ($check){
    case 'bot':
        $where['ag'] = array('like',array('%spider%','%bot%'),'OR');
        break;
    case 'vis':
        $where['ag'] = array('notlike',array('%spider%','%bot%'),'AND');
        break;
    default:
        $where['ag'] = array('neq','null');
    }
    $Vistor = M('vistor');
    $count = $Vistor->where($where)->count();
    $p = getpage($count, 20);
    $list = $Vistor->field(true)->where($where)->order('id desc')->limit($p->firstRow, $p->listRows)->select();
    $Location = new \Org\Net\IpLocation('UTFWry.dat');
    foreach($list as &$v) {
        $v['ip'] = long2ip($v['ip']);
        $v['os'] = get_os($v['ag']);
        $v['br'] = get_br($v['ag']);
        $v = array_merge($v, $Location->getlocation($v['ip']));
    }
    $data['list'] = $list;
    $data['pages'] = $p->show();
    $this->ajaxReturn($data);
}