利用PHP如何统计Nginx日志的User Agent数据


Posted in PHP onMarch 06, 2019

前言

即将用到爬虫,于是打算收集一下User Agent(UA)数据。接着马上想到自己网站的访问日志不就是现成的优质数据源吗?于是愉快的决定写个脚本统计一下Nginx访问日志中的UA信息。

这类简单操作,用脚本语言就足够,毫无疑问肯定要用最熟悉的PHP。打开vim就开撸,十几分钟下来,功能简单的统计脚本就搞定了。

脚本目前有三个功能:

1. 找出所有的UA信息并排序; 2. 统计操作系统数据; 3. 统计浏览器数据。

程序运行截图如下:

1、UA信息

利用PHP如何统计Nginx日志的User Agent数据

2、操作系统信息

利用PHP如何统计Nginx日志的User Agent数据

3、浏览器

利用PHP如何统计Nginx日志的User Agent数据

用脚本统计最近一个月的访问日志,得到以下结果:

  • 搜索引擎爬虫比较频繁,每天有好几千次数据访问;
  • Windows仍是份额最大的操作系统,Linux桌面依然份额很小;
  • Chrome目前是浏览器领域的霸主,其次是Firefox,Opera已经很小众了。

最后附上PHP脚本的代码,也可以从本人的Github里找到:https://github.com/tlanyan/Scripts/blob/master/statUA.php

#!/usr/bin/php
<?php
/**
 * @brief stat UA in access log
 *
 * @author tlanyan<tlanyan@hotmail.com>
 * @link http://tlanyan.me
 */
/* vim: set ts=4; set sw=4; set ss=4; set expandtab; */
function getFileList(string $path) : array {
 return glob(rtrim($path, "/") . "/*access.log*");
}
function statFiles(array $files) : array {
 $stat = [];
 echo PHP_EOL, "start to read files...", PHP_EOL;
 foreach ($files as $file) {
  echo "read file: $file ...", PHP_EOL;
  $contents = getFileContent($file);
  foreach ($contents as $line) {
   $ua = getUA($line);
   if (isset($stat[$ua])) {
    $stat[$ua] += 1;
   } else {
    $stat[$ua] = 1;
   }
  }
 }
 echo "stat all files done!", PHP_EOL, PHP_EOL;
 return $stat;
}
function getFileContent(string $file) : array {
 if (substr($file, -3, 3) === ".gz") {
  return gzfile($file);
 }
 return file($file);
}
function getUA(string $line) : ?string {
 // important! Nginx log format determins the UA location in the line!
 // You may have to refactor following codes to get the right result
 // UA starts from fifth double quote 
 $count = 0; $offset = 0;
 while ($count < 5) {
  $pos = strpos($line, '"', $offset);
  if ($pos === false) {
   echo "Error! Unknown line: $line", PHP_EOL;
   return null;
  }
  $count ++;
  $offset = $pos + 1;
 }
 $end = strpos($line, '"', $offset);
 return substr($line, $offset, $end - $offset);
}
function usage() {
 echo "Usage: php statUA.php [option] [dir]", PHP_EOL;
 echo " options:", PHP_EOL;
 echo " -h: show this help", PHP_EOL;
 echo " -v: verbose mode", PHP_EOL;
 echo "-n NUM: UA list number", PHP_EOL;
 echo " dir: directory to the log files", PHP_EOL;
 echo PHP_EOL;
}
function filterUA(array& $stat, array $UAFilters) {
 $filterCount = 0;
 foreach ($UAFilters as $filter) {
  foreach ($stat as $ua => $count) {
   if (stripos($ua, $filter) !== false) {
    $filterCount += $count;
    unset($stat[$ua]);
   }
  }
 }
 echo "filter $filterCount records!", PHP_EOL;
}
function printCount(array $stat) {
 $sum = array_sum($stat);
 foreach ($stat as $key => $count) {
  echo $key, " : ", $count, ", percent: ", sprintf("%.2f", 100*$count/$sum), PHP_EOL;
 }
}
function statOS(array $UAs) : array {
 global $debug;
 echo PHP_EOL, "stat OS...", PHP_EOL;
 $os = ["Windows", "MacOS", "Linux", "Android", "iOS", "other"];
 $stat = array_fill_keys($os, 0);
 foreach ($UAs as $key => $count) {
  if (strpos($key, "Windows") !== false) {
   $stat["Windows"] += $count;
  } else if (strpos($key, "Macintosh") !== false) {
   $stat["MacOS"] += $count;
  // must deal Android first, then Linux
  } else if (strpos($key, "Android") !== false) {
   $stat["Android"] += $count;
  } else if (strpos($key, "Linux") !== false) {
   $stat["Linux"] += $count;
  } else if (strpos($key, "iPhone") !== false || strpos($key, "iOS") !== false || strpos($key, "like Mac OS") !== false || strpos($key, "Darwin") !== false) {
   $stat["iOS"] += $count;
  } else {
   if ($debug) {
    echo "other: $key, count: $count", PHP_EOL;
   }
   $stat["other"] += $count;
  }
 }
 return $stat;
}
function statBrowser(array $UAs) : array {
 global $debug;
 echo PHP_EOL, "stat brwoser...", PHP_EOL;
 $browsers = ["Chrome", "Firefox", "IE", "Safari", "Edge", "Opera", "other"];
 $stat = array_fill_keys($browsers, 0);
 foreach ($UAs as $key => $count) {
  if (strpos($key, "MSIE") !== false) {
   $stat["IE"] += $count;
  } else if (strpos($key, "Edge") !== false) {
   $stat["Edge"] += $count;
  } else if (strpos($key, "Firefox") !== false) {
   $stat["Firefox"] += $count;
  } else if (strpos($key, "OPR") !== false) {
   $stat["Opera"] += $count;
  // first Chrome, then Safari
  } else if (strpos($key, "Chrome") !== false) {
   $stat["Chrome"] += $count;
  } else if (strpos($key, "Safari") !== false) {
   $stat["Safari"] += $count;
  } else {
   if ($debug) {
    echo "other: $key, count: $count", PHP_EOL;
   }
   $stat["other"] += $count;
  }
 }
 return $stat;
}
function parseCmd() {
 global $debug, $num, $path, $argc, $argv;
 $optind = null;
 $options = getopt("hvn:", [], $optind);
 if ($argc > 2 && empty($options)) {
  usage();
  exit(1);
 }
 if (isset($options['h'])) {
  usage();
  exit(0);
 }
 if (isset($options['v'])) {
  $debug = true;
 }
 if (isset($options['n'])) {
  $num = intval($options['n']);
  if ($num <= 0) {
   $num = 10;
  }
 }
 if ($argc === 2 && empty($options)) {
  $path = $argv[1];
 }
 if ($argc > $optind) {
  $path = $argv[$optind];
 }
 if (!is_dir($path)) {
  echo "invalid directory: $path", PHP_EOL;
  exit(1);
 }
 if ($debug) {
  echo "num: $num", PHP_EOL;
  echo "verbose: ", var_export($debug, true), PHP_EOL;
  echo "path: $path", PHP_EOL;
 }
}
if (version_compare(PHP_VERSION, "7.1") < 0) {
 exit("scripts require PHP >=7.1");
}
$path = ".";
$debug = false;
$num = 10;
$UAFilters = [
 "spider",
 "bot",
 "wget",
 "curl",
];
parseCmd();
$files = getFileList($path);
if (empty($files)) {
 echo '"' . realpath($path) . '" does not contain access log files.', PHP_EOL;
 exit(0);
}
$allUA = statFiles($files);
if (empty($allUA)) {
 echo "no data", PHP_EOL;
 exit(0);
}
filterUA($allUA, $UAFilters);
// sort array with count
uasort($allUA, function ($a, $b) {
 return $b - $a;
});
if ($debug) {
 print_r($allUA);
}
echo PHP_EOL, "---- top $num UA ----", PHP_EOL;
printCount(array_slice($allUA, 0, $num));
echo "-------------------", PHP_EOL;
$os = statOS($allUA);
echo PHP_EOL, "os count:", PHP_EOL;
printCount($os);
$browser = statBrowser($allUA);
echo PHP_EOL, "browser count:", PHP_EOL;
printCount($browser);

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对三水点靠木的支持。

PHP 相关文章推荐
如何在PHP中使用Oracle数据库(1)
Oct 09 PHP
php md5下16位和32位的实现代码
Apr 09 PHP
php中hashtable实现示例分享
Feb 13 PHP
php cookie名使用点号(句号)会被转换
Oct 23 PHP
Zend Framework教程之Bootstrap类用法概述
Mar 14 PHP
微信公众号开发之语音消息识别php代码
Aug 08 PHP
PHP检测数据类型的几种方法(总结)
Mar 04 PHP
PHP使用第三方即时获取物流动态实例详解
Apr 27 PHP
Nginx下ThinkPHP5的配置方法详解
Aug 01 PHP
Laravel框架实现超简单的分页效果示例
Feb 08 PHP
Laravel解决nesting level错误和隐藏index.php的问题
Oct 12 PHP
PHP7 新增常量
Mar 09 PHP
浅谈php://filter的妙用
Mar 05 #PHP
PHP实现微信小程序用户授权的工具类示例
Mar 05 #PHP
统计PHP目录中的文件数方法
Mar 05 #PHP
PHP常见字符串操作函数与用法总结
Mar 04 #PHP
php+Ajax处理xml与json格式数据的方法示例
Mar 04 #PHP
php+Ajax无刷新验证用户名操作实例详解
Mar 04 #PHP
实例介绍PHP删除数组中的重复元素
Mar 03 #PHP
You might like
第1次亲密接触PHP5(2)
2006/10/09 PHP
PHP编程之高级技巧——利用Mysql函数
2006/10/09 PHP
网友原创的PHP模板类代码
2008/09/07 PHP
php echo, print, print_r, sprintf, var_dump, var_expor的使用区别
2013/06/20 PHP
smarty获得当前url的方法分享
2014/02/14 PHP
php实现随机显示图片方法汇总
2015/05/21 PHP
php中文字符串截取多种方法汇总
2016/10/06 PHP
php str_getcsv把字符串解析为数组的实现方法
2017/04/05 PHP
php实现数组纵向转横向并过滤重复值的方法分析
2017/05/29 PHP
jQuery操作Select选择的Text和Value(获取/设置/添加/删除)
2013/03/06 Javascript
javascript中自定义对象的属性方法分享
2013/07/12 Javascript
使用百度地图api实现根据地址查询经纬度
2014/12/11 Javascript
js实现键盘控制DIV移动的方法
2015/01/10 Javascript
浅谈jQuery中height与width
2015/07/06 Javascript
js确认框confirm()用法实例详解
2016/01/07 Javascript
React快速入门教程
2017/01/17 Javascript
详解vue.js2.0父组件点击触发子组件方法
2017/05/10 Javascript
基于angular2 的 http服务封装的实例代码
2017/06/29 Javascript
自定义vue组件发布到npm的方法
2018/05/09 Javascript
JS动态插入脚本和插入引用外部链接脚本的方法
2018/05/21 Javascript
Vue.js自定义指令学习使用详解
2019/10/19 Javascript
[01:20:38]完美世界DOTA2联赛 GXR vs IO 第一场 11.07
2020/11/09 DOTA
Python两个整数相除得到浮点数值的方法
2015/03/18 Python
Python运算符重载用法实例
2015/05/28 Python
Python基础学习之基本数据结构详解【数字、字符串、列表、元组、集合、字典】
2019/06/18 Python
python中如何实现将数据分成训练集与测试集的方法
2019/09/13 Python
Python+Opencv身份证号码区域提取及识别实现
2020/08/25 Python
伦敦剧院门票:London Theatre Direct
2018/11/21 全球购物
MAC Cosmetics官方网站:魅可专业艺术彩妆
2019/04/10 全球购物
为什么使用接口?
2014/08/13 面试题
关键字final的用法
2013/10/02 面试题
在C语言中"指针和数组等价"到底是什么意思?
2014/03/24 面试题
电子商务个人自荐信
2013/12/12 职场文书
开会迟到检讨书
2014/01/08 职场文书
个人自我评价和职业目标
2014/01/24 职场文书
应届毕业生自荐信例文
2014/02/26 职场文书