使用Hadoop统计日志数据

用户行为日志概述

用户行为日志:

  • 用户每次访问网站时所有的行为数据
    • 访问、浏览、搜索、点击...
  • 用户行为轨迹、流量日志(用户行为日志的其他名称)

为什么要记录用户访问行为日志:

  • 进行网站页面的访问量的统计
  • 分析网站的黏性
  • 训练推荐系统

用户行为日志生成渠道:

  • web服务器记录的web访问日志
  • ajax记录的访问日志以及其他相关的日志

用户行为日志大致内容:

  • 访问时间
  • 访问者所使用的客户端(UserAgent)
  • 访问者的IP地址
  • 访问者账号
  • 某个页面的停留时间
  • 访问的时间与地点
  • 跳转的链接地址(referer)
  • 访问信息,例如:session_id
  • 模块AppID

用户行为日志分析的意义:

  • 网站的眼睛,能够看到用户的主要来源、喜好网站上的哪些内容,以及用户的忠诚度等
  • 网站的神经,通过分析用户行为日志,我们能对网站的布局、功能进一步的优化,以提高用户的体验等
  • 网站的大脑,通过分析结果,进行推广预算的划分,以及重点优化用户群体的倾向点等

离线数据处理架构

离线数据处理流程:

  • 数据采集
    • 例如可以使用Flume进行数据的采集:将web日志写入到HDFS
  • 数据清洗
    • 可以使用Spark、Hive、MapReduce等框架进行数据的清洗,清洗完之后的数据可以存放在HDFS或者Hive、Spark SQL里
  • 数据处理
    • 按照我们的需求进行相应业务的统计和分析
  • 数据处理结果入库
    • 结果可以存放到RDBMS、NoSQL数据库
  • 数据的可视化展示
    • 通过图形化展示的方式展现出来:饼图、柱状图、地图、折线图等等
    • 工具:ECharts、HUE、Zeppelin

流程示意图:
使用Hadoop统计日志数据


项目需求

需求:

  • 统计网站访问日志中每个浏览器的访问次数

日志片段如下:


   
   
  1. 183.162.52.7 - - [10/Nov/2016:00:01:02 +0800] "POST /api3/getadv HTTP/1.1" 200 813 "www.xxx.com" "-" cid=0×tamp=1478707261865&uid=2871142&marking=androidbanner&secrect=a6e8e14701ffe9f6063934780d9e2e6d&token=f51e97d1cb1a9caac669ea8acc162b96 "mukewang/5.0.0 (Android 5.1.1; Xiaomi Redmi 3 Build/LMY47V),Network 2G/3G" "-" 10.100.134.244:80 200 0.027 0.027
  2. 10.100.0.1 - - [10/Nov/2016:00:01:02 +0800] "HEAD / HTTP/1.1" 301 0 "117.121.101.40" "-" - "curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.16.2.3 Basic ECC zlib/1.2.3 libidn/1.18 libssh2/1.4.2" "-" - - - 0.000

功能实现之UserAgent解析类测试

首先我们需要根据日志信息抽取出浏览器信息,针对不同的浏览器进行统计操作。虽然可以自己实现这个功能,但是懒得再造轮子了,所以我在GitHub找到了一个小工具可以完成这个功能,GitHub地址如下:

https://github.com/LeeKemp/UserAgentParser

通过git clone或者浏览器下载到本地后,使用命令行进入到其主目录下,然后通过maven命令对其进行打包并安装到本地仓库里:


   
   
  1. $ mvn clean package -DskipTest
  2. $ mvn clean install -DskipTest

安装完成后,在工程中添加依赖以及插件:


   
   
  1. <repositories>
  2. <repository>
  3. <id>cloudera </id>
  4. <url>https://repository.cloudera.com/artifactory/cloudera-repos/ </url>
  5. <releases>
  6. <enabled>true </enabled>
  7. </releases>
  8. <snapshots>
  9. <enabled>false </enabled>
  10. </snapshots>
  11. </repository>
  12. </repositories>
  13. <properties>
  14. <project.build.sourceEncoding>UTF-8 </project.build.sourceEncoding>
  15. <hadoop.version>2.6.0-cdh5.7.0 </hadoop.version>
  16. </properties>
  17. <dependencies>
  18. <dependency>
  19. <groupId>org.apache.hadoop </groupId>
  20. <artifactId>hadoop-client </artifactId>
  21. <version>${hadoop.version} </version>
  22. <scope>provided </scope>
  23. </dependency>
  24. <!-- 添加UserAgent解析的依赖 -->
  25. <dependency>
  26. <groupId>com.kumkee </groupId>
  27. <artifactId>UserAgentParser </artifactId>
  28. <version>0.0.1 </version>
  29. </dependency>
  30. <dependency>
  31. <groupId>junit </groupId>
  32. <artifactId>junit </artifactId>
  33. <version>4.10 </version>
  34. <scope>test </scope>
  35. </dependency>
  36. </dependencies>
  37. <!-- mvn assembly:assembly -->
  38. <build>
  39. <plugins>
  40. <plugin>
  41. <artifactId>maven-assembly-plugin </artifactId>
  42. <configuration>
  43. <archive>
  44. <manifest>
  45. <mainClass> </mainClass>
  46. </manifest>
  47. </archive>
  48. <descriptorRefs>
  49. <descriptorRef>jar-with-dependencies </descriptorRef>
  50. </descriptorRefs>
  51. </configuration>
  52. </plugin>
  53. </plugins>
  54. </build>

然后我们编写一个测试用例来测试一下这个解析类,因为之前并没有使用过这个工具,所以对于一个未使用过的工具,要养成在工程中使用之前对其进行测试的好习惯:


   
   
  1. package org.zero01.project;
  2. import com.kumkee.userAgent.UserAgent;
  3. import com.kumkee.userAgent.UserAgentParser;
  4. /**
  5. * @program: hadoop-train
  6. * @description: UserAgent解析测试类
  7. * @author: 01
  8. * @create: 2018-04-01 22:43
  9. **/
  10. public class UserAgentTest {
  11. public static void main (String[] args) {
  12. String source = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36";
  13. UserAgentParser userAgentParser = new UserAgentParser();
  14. UserAgent agent = userAgentParser.parse(source);
  15. String browser = agent.getBrowser();
  16. String engine = agent.getEngine();
  17. String engineVersion = agent.getEngineVersion();
  18. String os = agent.getOs();
  19. String platform = agent.getPlatform();
  20. boolean isMobile = agent.isMobile();
  21. System.out.println( "浏览器:" + browser);
  22. System.out.println( "引擎:" + engine);
  23. System.out.println( "引擎版本:" + engineVersion);
  24. System.out.println( "操作系统:" + os);
  25. System.out.println( "平台:" + platform);
  26. System.out.println( "是否是移动设备:" + isMobile);
  27. }
  28. }

控制台输出结果如下:


   
   
  1. 浏览器:Chrome
  2. 引擎:Webkit
  3. 引擎版本:537.36
  4. 操作系统:Windows 7
  5. 平台:Windows
  6. 是否是移动设备: false

从打印结果可以看到,UserAgent的相关信息都正常获取到了,我们就可以在工程中进行使用这个工具了。


使用MapReduce完成需求统计

创建一个类,编写代码如下:


   
   
  1. package org.zero01.hadoop.project;
  2. import com.kumkee.userAgent.UserAgent;
  3. import com.kumkee.userAgent.UserAgentParser;
  4. import org.apache.hadoop.conf.Configuration;
  5. import org.apache.hadoop.fs.FileSystem;
  6. import org.apache.hadoop.fs.Path;
  7. import org.apache.hadoop.io.LongWritable;
  8. import org.apache.hadoop.io.Text;
  9. import org.apache.hadoop.mapreduce.Job;
  10. import org.apache.hadoop.mapreduce.Mapper;
  11. import org.apache.hadoop.mapreduce.Reducer;
  12. import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
  13. import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
  14. import java.io.IOException;
  15. import java.util.regex.Matcher;
  16. import java.util.regex.Pattern;
  17. /**
  18. * @program: hadoop-train
  19. * @description: 使用MapReduce来完成统计浏览器的访问次数
  20. * @author: 01
  21. * @create: 2018-04-02 14:20
  22. **/
  23. public class LogApp {
  24. /**
  25. * Map: 读取输入的文件内容
  26. */
  27. public static class MyMapper extends Mapper<LongWritable, Text, Text, LongWritable> {
  28. LongWritable one = new LongWritable( 1);
  29. private UserAgentParser userAgentParser;
  30. protected void setup (Context context) throws IOException, InterruptedException {
  31. userAgentParser = new UserAgentParser();
  32. }
  33. protected void map (LongWritable key, Text value, Context context) throws IOException, InterruptedException {
  34. // 接收到的每一行日志信息
  35. String line = value.toString();
  36. String source = line.substring(getCharacterPosition(line, "\"", 7) + 1);
  37. UserAgent agent = userAgentParser.parse(source);
  38. String browser = agent.getBrowser();
  39. // 通过上下文把map的处理结果输出
  40. context.write( new Text(browser), one);
  41. }
  42. protected void cleanup (Context context) throws IOException, InterruptedException {
  43. userAgentParser = null;
  44. }
  45. }
  46. /**
  47. * Reduce: 归并操作
  48. */
  49. public static class MyReducer extends Reducer<Text, LongWritable, Text, LongWritable> {
  50. protected void reduce (Text key, Iterable<LongWritable> values, Context context) throws IOException, InterruptedException {
  51. long sum = 0;
  52. for (LongWritable value : values) {
  53. // 求key出现的次数总和
  54. sum += value.get();
  55. }
  56. // 将最终的统计结果输出
  57. context.write(key, new LongWritable(sum));
  58. }
  59. }
  60. /**
  61. * 获取指定字符串中指定标识的字符串出现的索引位置
  62. *
  63. * @param value
  64. * @param operator
  65. * @param index
  66. * @return
  67. */
  68. private static int getCharacterPosition (String value, String operator, int index) {
  69. Matcher slashMatcher = Pattern.compile(operator).matcher(value);
  70. int mIdex = 0;
  71. while (slashMatcher.find()) {
  72. mIdex++;
  73. if (mIdex == index) {
  74. break;
  75. }
  76. }
  77. return slashMatcher.start();
  78. }
  79. /**
  80. * 定义Driver:封装了MapReduce作业的所有信息
  81. */
  82. public static void main (String[] args) throws IOException, ClassNotFoundException, InterruptedException {
  83. Configuration configuration = new Configuration();
  84. // 准备清理已存在的输出目录
  85. Path outputPath = new Path(args[ 1]);
  86. FileSystem fileSystem = FileSystem.get(configuration);
  87. if (fileSystem.exists(outputPath)) {
  88. fileSystem.delete(outputPath, true);
  89. System.out.println( "output file exists, but is has deleted");
  90. }
  91. // 创建Job,通过参数设置Job的名称
  92. Job job = Job.getInstance(configuration, "LogApp");
  93. // 设置Job的处理类
  94. job.setJarByClass(LogApp.class);
  95. // 设置作业处理的输入路径
  96. FileInputFormat.setInputPaths(job, new Path(args[ 0]));
  97. // 设置map相关参数
  98. job.setMapperClass(LogApp.MyMapper.class);
  99. job.setMapOutputKeyClass(Text.class);
  100. job.setMapOutputValueClass(LongWritable.class);
  101. // 设置reduce相关参数
  102. job.setReducerClass(LogApp.MyReducer.class);
  103. job.setOutputKeyClass(Text.class);
  104. job.setOutputValueClass(LongWritable.class);
  105. // 设置作业处理完成后的输出路径
  106. FileOutputFormat.setOutputPath(job, new Path(args[ 1]));
  107. System.exit(job.waitForCompletion( true) ? 0 : 1);
  108. }
  109. }

在工程目录下打开控制台,输入如下命令进行打包:

mvn assembly:assembly
   
   

打包成功:
使用Hadoop统计日志数据

将这个jar包上传到服务器上:


   
   
  1. [root@localhost ~] # rz # 使用的是Xshell工具,所以直接使用rz命令即可上传文件
  2. [root@localhost ~] # ls |grep hadoop-train-1.0-jar-with-dependencies.jar # 查看是否上传成功
  3. hadoop-train-1.0-jar-with-dependencies.jar
  4. [root@localhost ~] #

把事先准备好的日志文件上传到HDFS文件系统中:


   
   
  1. [root@localhost ~] # hdfs dfs -put ./10000_access.log /
  2. [root@localhost ~] # hdfs dfs -ls /10000_access.log
  3. -rw-r--r-- 1 root supergroup 2769741 2018-04-02 22:33 /10000_access.log
  4. [root@localhost ~] #

执行如下命令

[root@localhost ~]# hadoop jar ./hadoop-train-1.0-jar-with-dependencies.jar org.zero01.hadoop.project.LogApp /10000_access.log /browserout
   
   

执行成功:
使用Hadoop统计日志数据

查看处理结果:


   
   
  1. [root@localhost ~] # hdfs dfs -ls /browserout
  2. Found 2 items
  3. -rw-r--r-- 1 root supergroup 0 2018-04-02 22:42 /browserout/_SUCCESS
  4. -rw-r--r-- 1 root supergroup 56 2018-04-02 22:42 /browserout/part-r-00000
  5. [root@localhost ~] # hdfs dfs -text /browserout/part-r-00000
  6. Chrome 2775
  7. Firefox 327
  8. MSIE 78
  9. Safari 115
  10. Unknown 6705
  11. [root@localhost ~] #

转载于:https://blog.51cto.com/zero01/2093820

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值