利用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入门学习知识点五 关于php数组的几个基本操作
Jul 14 PHP
windows7下安装php的imagick和imagemagick扩展教程
Jul 04 PHP
qq登录,新浪微博登录接口申请过程中遇到的问题
Jul 22 PHP
PHP之autoload运行机制实例分析
Aug 28 PHP
phpnow php探针环境检测代码
Nov 04 PHP
php上传图片客户端和服务器端实现方法
Mar 30 PHP
YII中Ueditor富文本编辑器文件和图片上传的配置图文教程
Mar 15 PHP
PHP实现的大文件切割与合并功能示例
Apr 10 PHP
PHP闭包定义与使用简单示例
Apr 13 PHP
PHP lcfirst()函数定义与用法
Mar 08 PHP
PHP读取Excel内的图片(phpspreadsheet和PHPExcel扩展库)
Nov 19 PHP
Laravel等框架模型关联的可用性浅析
Dec 15 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的类 功能齐全的发送邮件类
2006/10/09 PHP
PHP Directory 函数的详解
2013/03/07 PHP
php使用正则表达式提取字符串中尖括号、小括号、中括号、大括号中的字符串
2020/04/05 PHP
PHP5.5新特性之yield理解与用法实例分析
2019/01/11 PHP
PHP实现一个按钮点击上传多个图片操作示例
2020/01/23 PHP
Laravel配合jwt使用的方法实例
2020/10/25 PHP
JS event使用方法详解
2008/04/28 Javascript
JavaScript的document对象和window对象详解
2010/12/30 Javascript
js解析json读取List中的实体对象示例
2014/03/11 Javascript
Javascript 实现图片无缝滚动
2014/12/19 Javascript
JavaScript实现cookie的写入、读取、删除功能
2015/11/05 Javascript
jquery调整表格行tr上下顺序实例讲解
2016/01/09 Javascript
jQuery中借助deferred来请求及判断AJAX加载的实例讲解
2016/05/24 Javascript
AngularJS 应用身份认证的技巧总结
2016/11/07 Javascript
基于touch.js手势库+zepto.js插件开发图片查看器(滑动、缩放、双击缩放)
2016/11/17 Javascript
js + css实现标签内容切换功能(实例讲解)
2017/10/09 Javascript
JS实现去除数组中重复json的方法示例
2017/12/21 Javascript
微信小程序实现pdf、word等格式文件上传的方法
2019/09/10 Javascript
ElementUI多个子组件表单的校验管理实现
2019/11/07 Javascript
JS实现贪吃蛇游戏
2019/11/15 Javascript
[01:43]深扒TI7聊天轮盘语音出处4
2017/05/11 DOTA
[01:33:30]DOTA2-DPC中国联赛 正赛 RNG vs Phoenix BO3 第二场 2月5日
2021/03/11 DOTA
python基础教程之获取本机ip数据包示例
2014/02/10 Python
python中使用sys模板和logging模块获取行号和函数名的方法
2014/04/15 Python
python多线程方式执行多个bat代码
2016/06/07 Python
django ajax json的实例代码
2018/05/29 Python
python调用百度REST API实现语音识别
2018/08/30 Python
Python识别快递条形码及Tesseract-OCR使用详解
2019/07/15 Python
tensorflow使用range_input_producer多线程读取数据实例
2020/01/20 Python
Pyecharts地图显示不完成问题解决方案
2020/05/11 Python
python tqdm库的使用
2020/11/30 Python
HTML5的自定义属性data-*详细介绍和JS操作实例
2014/04/10 HTML / CSS
俄罗斯金苹果网上化妆品和香水商店:Goldapple
2019/12/01 全球购物
英语专业自荐书
2014/06/13 职场文书
大学生学习面向未来的赶考思想汇报
2014/09/12 职场文书
pd.DataFrame中的几种索引变换的实现
2022/06/16 Python