利用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 相关文章推荐
PHP4和PHP5性能测试和对比 测试代码与环境
Aug 17 PHP
火车采集器 免费版使出收费版本功能实现原理
Sep 17 PHP
几个实用的PHP内置函数使用指南
Nov 27 PHP
PHP实现获取客户端IP并获取IP信息
Mar 17 PHP
使用phpstorm和xdebug实现远程调试的方法
Dec 29 PHP
PHP根据session与cookie用户登录状态操作类的代码
May 13 PHP
php 获取文件行数的方法总结
Oct 11 PHP
PHP输出图像imagegif、imagejpeg与imagepng函数用法分析
Nov 14 PHP
Laravel 中使用 Vue.js 实现基于 Ajax 的表单提交错误验证操作
Jun 30 PHP
PHP并发查询MySQL的实例代码
Aug 09 PHP
gearman中worker常驻后台,导致MySQL server has gone away的解决方法
Feb 27 PHP
TP5框架model常见操作示例小结【增删改查、聚合、时间戳、软删除等】
Apr 05 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
PHP Class&amp;Object -- PHP 自排序二叉树的深入解析
2013/06/25 PHP
php class类的用法详细总结
2013/10/17 PHP
php二分查找二种实现示例
2014/03/12 PHP
php基于openssl的rsa加密解密示例
2016/07/11 PHP
php array_values 返回数组的所有值详解及实例
2016/11/12 PHP
php实现将数组或对象写入到文件的方法小结【三种方法】
2020/04/22 PHP
兼容多浏览器的iframe自适应高度(ie8 、谷歌浏览器4.0和 firefox3.5.3)
2009/11/04 Javascript
Js注册协议倒计时的小例子
2013/06/24 Javascript
jquery网页元素拖拽插件效果及实现
2013/08/05 Javascript
php,js,css字符串截取的办法集锦
2014/09/26 Javascript
学习JavaScript设计模式之装饰者模式
2016/01/19 Javascript
基于jQuery实现二级下拉菜单效果
2016/02/01 Javascript
浅谈js中的延迟执行和定时执行
2016/05/31 Javascript
正则表达式,替换所有HTML标签的简单实例
2016/11/28 Javascript
JS验证字符串功能
2017/02/22 Javascript
Angularjs使用指令做表单校验的方法
2017/03/31 Javascript
vue-cli项目中使用echarts图表实例
2018/10/22 Javascript
js 计算图片内点个数的示例代码
2019/04/04 Javascript
Vue中computed、methods与watch的区别总结
2019/04/10 Javascript
解决vue中使用proxy配置不同端口和ip接口问题
2019/08/14 Javascript
Python中的引用和拷贝浅析
2014/11/22 Python
Python的Flask框架应用程序实现使用QQ账号登录的方法
2016/06/07 Python
学习Python3 Dlib19.7进行人脸面部识别
2018/01/24 Python
浅谈pandas中Dataframe的查询方法([], loc, iloc, at, iat, ix)
2018/04/10 Python
python pandas dataframe 按列或者按行合并的方法
2018/04/12 Python
Python中输入和输出(打印)数据实例方法
2019/10/13 Python
python GUI库图形界面开发之PyQt5浏览器控件QWebEngineView详细使用方法
2020/02/26 Python
Python基于network模块制作电影人物关系图
2020/06/19 Python
pycharm 使用tab跳出正在编辑的括号(){}{}等问题
2021/02/26 Python
Traffic People官网:女式花裙、上衣和连身裤
2020/10/12 全球购物
毕业生找工作的自我评价
2013/10/18 职场文书
会计自我鉴定
2014/02/04 职场文书
教育基金募捐倡议书
2014/05/14 职场文书
师德师风事迹材料
2014/12/20 职场文书
承兑汇票延期证明
2015/06/23 职场文书
交通事故责任认定书
2015/08/06 职场文书