利用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 Smarty初体验二 获取配置信息
Aug 08 PHP
Yii把CGridView文本框换成下拉框的方法
Dec 03 PHP
php+ajax实现文章自动保存的方法
Dec 30 PHP
Codeigniter通过SimpleXML将xml转换成对象的方法
Mar 19 PHP
PHP永久登录、记住我功能实现方法和安全做法
Apr 27 PHP
PHP实现的简单网络硬盘
Jul 29 PHP
最准确的php截取字符串长度函数
Oct 29 PHP
浅析Yii2缓存的使用
May 10 PHP
微信支付开发告警通知实例
Jul 12 PHP
Yii框架数据模型的验证规则rules()被执行的方法
Dec 02 PHP
PHP + plupload.js实现多图上传并显示进度条加删除实例代码
Mar 06 PHP
php PDO判断连接是否可用的实现方法
Apr 03 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中global和$GLOBALS[]的分析之一
2012/02/02 PHP
json的键名为数字时的调用方式(示例代码)
2013/11/15 PHP
使用php语句将数据库*.sql文件导入数据库
2014/05/05 PHP
ThinkPHP调用common/common.php函数提示错误function undefined的解决方法
2014/08/25 PHP
PHP使用array_multisort对多个数组或多维数组进行排序
2014/12/16 PHP
php生成excel列名超过26列大于Z时的解决方法
2014/12/29 PHP
php基于GD库画五星红旗的方法
2015/02/24 PHP
php无限级分类实现方法分析
2016/10/19 PHP
PHP空值检测函数与方法汇总
2017/11/19 PHP
Prototype使用指南之selector.js
2007/01/10 Javascript
神奇的代码 通杀各种网站-可随意修改复制页面内容
2008/07/17 Javascript
js倒计时小程序
2013/11/05 Javascript
JS动态添加Table的TR,TD实现方法
2015/01/28 Javascript
javascript实现多栏闭合展开式广告位菜单效果实例
2015/08/05 Javascript
基于javascript实现浏览器滚动条快到底部时自动加载数据
2015/11/30 Javascript
jQuery实现拖拽页面元素并将其保存到cookie的方法
2016/06/12 Javascript
详谈jQuery.load()和Jsp的include的区别
2017/04/12 jQuery
layer关闭当前窗口页面以及确认取消按钮的方法
2019/09/09 Javascript
js实现窗口全屏示例详解
2019/09/17 Javascript
使用IPython下的Net-SNMP来管理类UNIX系统的教程
2015/04/15 Python
Python IDLE入门简介
2017/12/08 Python
五分钟带你搞懂python 迭代器与生成器
2020/08/30 Python
matplotlib事件处理基础(事件绑定、事件属性)
2021/02/03 Python
Python实现疫情地图可视化
2021/02/05 Python
HTML5中5个简单实用的API
2014/04/28 HTML / CSS
日本无添加化妆品:HABA
2016/08/18 全球购物
欧舒丹比利时官网:L’OCCITANE比利时
2017/04/25 全球购物
印度尼西亚电子产品购物网站:Kliknklik
2018/06/05 全球购物
英国川宁茶官方网站:Twinings茶
2019/05/21 全球购物
数控技术学生的自我评价
2014/02/15 职场文书
公证委托书大全
2014/04/04 职场文书
责任担保书范文
2014/05/21 职场文书
宪法宣传周工作方案
2014/05/26 职场文书
小学生读书笔记
2015/07/01 职场文书
学生会工作感言
2015/08/07 职场文书
2007年老电脑安装win11会怎么样? 网友实测win11在老电脑运行良好
2021/11/21 数码科技