用户行为日志概述
用户行为日志:
用户每次访问网站时所有的行为数据
访问浏览搜索点击...
用户行为轨迹流量日志 (用户行为日志的其他名称)
为什么要记录用户访问行为日志:
进行网站页面的访问量的统计
分析网站的黏性
训练推荐系统
用户行为日志生成渠道:
web 服务器记录的 web 访问日志
ajax 记录的访问日志以及其他相关的日志
用户行为日志大致内容:
访问时间
访问者所使用的客户端 (UserAgent)
访问者的 IP 地址
访问者账号
某个页面的停留时间
访问的时间与地点
跳转的链接地址 (referer)
访问信息, 例如: session_id
模块 AppID
用户行为日志分析的意义:
网站的眼睛, 能够看到用户的主要来源喜好网站上的哪些内容, 以及用户的忠诚度等
网站的神经, 通过分析用户行为日志, 我们能对网站的布局功能进一步的优化, 以提高用户的体验等
网站的大脑, 通过分析结果, 进行推广预算的划分, 以及重点优化用户群体的倾向点等
离线数据处理架构
离线数据处理流程:
数据采集
例如可以使用 Flume 进行数据的采集: 将 web 日志写入到 HDFS
数据清洗
可以使用 SparkHiveMapReduce 等框架进行数据的清洗, 清洗完之后的数据可以存放在 HDFS 或者 HiveSpark SQL 里
数据处理
按照我们的需求进行相应业务的统计和分析
数据处理结果入库
结果可以存放到 RDBMSNoSQL 数据库
数据的可视化展示
通过图形化展示的方式展现出来: 饼图柱状图地图折线图等等
工具: EChartsHUEZeppelin
流程示意图:
项目需求
需求:
统计网站访问日志中每个浏览器的访问次数
日志片段如下:
- 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
- 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 命令对其进行打包并安装到本地仓库里:
- $ mvn clean package -DskipTest
- $ mvn clean install -DskipTest
安装完成后, 在工程中添加依赖以及插件:
- <repositories>
- <repository>
- <id>cloudera</id>
- <url>https://repository.cloudera.com/artifactory/cloudera-repos/</url>
- <releases>
- <enabled>true</enabled>
- </releases>
- <snapshots>
- <enabled>false</enabled>
- </snapshots>
- </repository>
- </repositories>
- <properties>
- <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
- <hadoop.version>2.6.0-cdh5.7.0</hadoop.version>
- </properties>
- <dependencies>
- <dependency>
- <groupId>org.apache.hadoop</groupId>
- <artifactId>hadoop-client</artifactId>
- <version>${hadoop.version}</version>
- <scope>provided</scope>
- </dependency>
- <!-- 添加 UserAgent 解析的依赖 -->
- <dependency>
- <groupId>com.kumkee</groupId>
- <artifactId>UserAgentParser</artifactId>
- <version>0.0.1</version>
- </dependency>
- <dependency>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
- <version>4.10</version>
- <scope>test</scope>
- </dependency>
- </dependencies>
- <!-- mvn assembly:assembly -->
- <build>
- <plugins>
- <plugin>
- <artifactId>maven-assembly-plugin</artifactId>
- <configuration>
- <archive>
- <manifest>
- <mainClass></mainClass>
- </manifest>
- </archive>
- <descriptorRefs>
- <descriptorRef>jar-with-dependencies</descriptorRef>
- </descriptorRefs>
- </configuration>
- </plugin>
- </plugins>
- </build>
然后我们编写一个测试用例来测试一下这个解析类, 因为之前并没有使用过这个工具, 所以对于一个未使用过的工具, 要养成在工程中使用之前对其进行测试的好习惯:
- package org.zero01.project;
- import com.kumkee.userAgent.UserAgent;
- import com.kumkee.userAgent.UserAgentParser;
- /**
- * @program: hadoop-train
- * @description: UserAgent 解析测试类
- * @author: 01
- * @create: 2018-04-01 22:43
- **/
- public class UserAgentTest {
- public static void main(String[] args) {
- String source = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (Khtml, like Gecko) Chrome/54.0.2840.71 Safari/537.36";
- UserAgentParser userAgentParser = new UserAgentParser();
- UserAgent agent = userAgentParser.parse(source);
- String browser = agent.getBrowser();
- String engine = agent.getEngine();
- String engineVersion = agent.getEngineVersion();
- String os = agent.getOs();
- String platform = agent.getPlatform();
- boolean isMobile = agent.isMobile();
- System.out.println("浏览器:" + browser);
- System.out.println("引擎:" + engine);
- System.out.println("引擎版本:" + engineVersion);
- System.out.println("操作系统:" + os);
- System.out.println("平台:" + platform);
- System.out.println("是否是移动设备:" + isMobile);
- }
- }
控制台输出结果如下:
浏览器: Chrome
引擎: Webkit
引擎版本: 537.36
操作系统: Windows 7
平台: Windows
是否是移动设备: false
从打印结果可以看到, UserAgent 的相关信息都正常获取到了, 我们就可以在工程中进行使用这个工具了
使用 MapReduce 完成需求统计
创建一个类, 编写代码如下:
- package org.zero01.hadoop.project;
- import com.kumkee.userAgent.UserAgent;
- import com.kumkee.userAgent.UserAgentParser;
- import org.apache.hadoop.conf.Configuration;
- import org.apache.hadoop.fs.FileSystem;
- import org.apache.hadoop.fs.Path;
- import org.apache.hadoop.io.LongWritable;
- import org.apache.hadoop.io.Text;
- import org.apache.hadoop.mapreduce.Job;
- import org.apache.hadoop.mapreduce.Mapper;
- import org.apache.hadoop.mapreduce.Reducer;
- import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
- import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
- import java.io.IOException;
- import java.util.regex.Matcher;
- import java.util.regex.Pattern;
- /**
- * @program: hadoop-train
- * @description: 使用 MapReduce 来完成统计浏览器的访问次数
- * @author: 01
- * @create: 2018-04-02 14:20
- **/
- public class LogApp {
- /**
- * Map: 读取输入的文件内容
- */
- public static class MyMapper extends Mapper <LongWritable,
- Text,
- Text,
- LongWritable> {
- LongWritable one = new LongWritable(1);
- private UserAgentParser userAgentParser;
- protected void setup(Context context) throws IOException,
- InterruptedException {
- userAgentParser = new UserAgentParser();
- }
- protected void map(LongWritable key, Text value, Context context) throws IOException,
- InterruptedException {
- // 接收到的每一行日志信息
- String line = value.toString();
- String source = line.substring(getCharacterPosition(line, "\"", 7) + 1);
- UserAgent agent = userAgentParser.parse(source);
- String browser = agent.getBrowser();
- // 通过上下文把 map 的处理结果输出
- context.write(new Text(browser), one);
- }
- protected void cleanup(Context context) throws IOException,
- InterruptedException {
- userAgentParser = null;
- }
- }
- /**
- * Reduce: 归并操作
- */
- public static class MyReducer extends Reducer <Text,
- LongWritable,
- Text,
- LongWritable> {
- protected void reduce(Text key, Iterable <LongWritable> values, Context context) throws IOException,
- InterruptedException {
- long sum = 0;
- for (LongWritable value: values) {
- // 求 key 出现的次数总和
- sum += value.get();
- }
- // 将最终的统计结果输出
- context.write(key, new LongWritable(sum));
- }
- }
- /**
- * 获取指定字符串中指定标识的字符串出现的索引位置
- *
- * @param value
- * @param operator
- * @param index
- * @return
- */
- private static int getCharacterPosition(String value, String operator, int index) {
- Matcher slashMatcher = Pattern.compile(operator).matcher(value);
- int mIdex = 0;
- while (slashMatcher.find()) {
- mIdex++;
- if (mIdex == index) {
- break;
- }
- }
- return slashMatcher.start();
- }
- /**
- * 定义 Driver: 封装了 MapReduce 作业的所有信息
- */
- public static void main(String[] args) throws IOException,
- ClassNotFoundException,
- InterruptedException {
- Configuration configuration = new Configuration();
- // 准备清理已存在的输出目录
- Path outputPath = new Path(args[1]);
- FileSystem fileSystem = FileSystem.get(configuration);
- if (fileSystem.exists(outputPath)) {
- fileSystem.delete(outputPath, true);
- System.out.println("output file exists, but is has deleted");
- }
- // 创建 Job, 通过参数设置 Job 的名称
- Job job = Job.getInstance(configuration, "LogApp");
- // 设置 Job 的处理类
- job.setJarByClass(LogApp.class);
- // 设置作业处理的输入路径
- FileInputFormat.setInputPaths(job, new Path(args[0]));
- // 设置 map 相关参数
- job.setMapperClass(LogApp.MyMapper.class);
- job.setMapOutputKeyClass(Text.class);
- job.setMapOutputValueClass(LongWritable.class);
- // 设置 reduce 相关参数
- job.setReducerClass(LogApp.MyReducer.class);
- job.setOutputKeyClass(Text.class);
- job.setOutputValueClass(LongWritable.class);
- // 设置作业处理完成后的输出路径
- FileOutputFormat.setOutputPath(job, new Path(args[1]));
- System.exit(job.waitForCompletion(true) ? 0 : 1);
- }
- }
在工程目录下打开控制台, 输入如下命令进行打包:
mvn assembly:assembly
打包成功:
将这个 jar 包上传到服务器上:
- [root@localhost ~]# rz # 使用的是 Xshell 工具, 所以直接使用 rz 命令即可上传文件
- [root@localhost ~]# ls |grep hadoop-train-1.0-jar-with-dependencies.jar # 查看是否上传成功
- hadoop-train-1.0-jar-with-dependencies.jar
- [root@localhost ~]#
把事先准备好的日志文件上传到 HDFS 文件系统中:
- [root@localhost ~]# hdfs dfs -put ./10000_access.log /
- [root@localhost ~]# hdfs dfs -ls /10000_access.log
- -rw-r--r-- 1 root supergroup 2769741 2018-04-02 22:33 /10000_access.log
- [root@localhost ~]#
执行如下命令
[root@localhost ~]# hadoop jar ./hadoop-train-1.0-jar-with-dependencies.jar org.zero01.hadoop.project.LogApp /10000_access.log /browserout
执行成功:
查看处理结果:
- [root@localhost ~]# hdfs dfs -ls /browserout
- Found 2 items
- -rw-r--r-- 1 root supergroup 0 2018-04-02 22:42 /browserout/_SUCCESS
- -rw-r--r-- 1 root supergroup 56 2018-04-02 22:42 /browserout/part-r-00000
- [root@localhost ~]# hdfs dfs -text /browserout/part-r-00000
- Chrome 2775
- Firefox 327
- MSIE 78
- Safari 115
- Unknown 6705
- [root@localhost ~]#
来源: http://blog.51cto.com/zero01/2093820