一、用户行为日志概述
1)用户行为日志
用户每次访问网站时所有的行为数据:访问、浏览、搜索、点击… 用户行为轨迹、流量
2)为什么要记录用户访问行为日志
- 进行网站页面的访问量的统计
- 分析网站的黏性
- 推荐
3)用户行为日志生成渠道
- Nginx:web服务器记录的web访问日志
- Ajax:记录的访问日志以及其他相关的日志
4)用户行为日志大致内容
- 访问时间
- 访问者所使用的客户端(UserAgent)
- 访问者的IP地址
- 访问者账号某个页面的停留时间
- 访问的时间与地点跳转的链接地址(referer)
- 访问信息,例如:session_id模块AppID
5)日志数据内容
- 访问的系统属性:操作系统、浏览器等
- 访问特征:点击的url、从哪个url跳转过来的(referer)、页面上的停留时间等
- 访问信息:session_id、访问ip(访问城市)等
6) 用户行为日志分析的意义
- 网站的眼睛
- eg.能够看到用户的主要来源、喜好网站上的哪些内容,以及用户的忠诚度等
- 网站的神经
- eg.通过分析用户行为日志,我们能对网站的布局、功能进一步的优化,以提高用户的体验等
- 网站的大脑
- eg.通过分析结果,进行推广预算的划分,以及重点优化用户群体的倾向点
二、离线处理架构##### 数据处理流程
1)数据采集:Flume:Web日志写入到HDFS
当访问某个网站时,每一个访问请求都会发送到后台的服务器上,面对高并发的情况下,大多使用的是Njexl来接受请求,然后再进行高并发的访问均衡,即使用N已经可以输出数据了,再通过Flume采集,它是将数据从一个地方搬运到另一个地方的框架
2)数据清洗
脏数据 eg.不符合日志规范的数据,要先清除技术:Hive Spark MapReduce或其他一些分布式计算框架清洗完的数据可以存放在HDFS(Hive/Spark SQL)
3)数据处理
按照我们的需要进行相应业务的统计和分析Hive Spark MapReduce或其他一些分布式计算框架
4)处理结果入库
可以存放到RDBMS(关系型数据库)、NoSQL(非关系型数据库)
5)数据的可视化
通过图形化的方式展现出来:饼图、柱状图、地图、折线图等ECharts、HUE、Zeppelin
三、项目需求统计imooc主站访问日志的浏览器访问次数
- 根据日志信息抽取出浏览器信息
- 针对不同的浏览器进行统计操作
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
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
2)实现UserAgent解析类测试
工具类下载githu地址:https://github.com/LeeKemp/UserAgentParser
通过git clone或者浏览器下载到本地后,使用命令行进入到其主目录下,然后通过maven命令对其进行打包并安装到本地仓库里:
mvn clean package -DskipTest
mvn clean install -DskipTest
安装完成后,在工程中添加依赖:
<!-- 添加UserAgent解析的依赖 -->
<dependency>
<groupId>com.kumkee</groupId>
<artifactId>UserAgentParser</artifactId>
<version>0.0.1</version>
</dependency>
编写一个测试用例来测试一下这个解析类
**
* UserAgentTest测试类
*/
public class UserAgentTest {
/**
* 单元测试:UserAgent工具类的使用
*/
@Test
public void testUserAgentParser(){
//信息
String source="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\n" +
"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";
//解析日志头部的工具类
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+", "+engine+", "+engineVersion+", "+os+", "+platform+", "+isMobile);
}
结果:Unknown, Unknown, null, Linux, Android, true
四、基于Hash的本地浏览器数量代码
package com.immoc.hadoop.project;
import com.kumkee.userAgent.UserAgent;
import com.kumkee.userAgent.UserAgentParser;
import org.apache.commons.lang.StringUtils;
import org.junit.Test;
import java.io.*;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* UserAgentTest测试类
*/
public class UserAgentTest {
/**
* 单元测试:UserAgent工具类的使用
*/
@Test
public void testUserAgentParser(){
//信息
String source="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\n" +
"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";
//解析日志头部的工具类
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+", "+engine+", "+engineVersion+", "+os+", "+platform+", "+isMobile);
}
/**
* 自定义方法
* 获取指定字符串中指定标识符的字符串出现的索引位置
* @param value 字符串值
* @param operator 指定标识符
* @param index 索引位置
* @return
*/
private int getCharacterPosition(String value,String operator,int index){
//对子字符串进行匹配
Matcher slashMatcher= Pattern.compile(operator).matcher(value);
int mInx=0;//计数
//matcher.find();尝试查找与该模式匹配的输入序列的下一个子序列
while (slashMatcher.find()){
mInx++;
if (mInx==index){
break;
}
}
//slashMatcher.start()返回上一个匹配的起始索引
return slashMatcher.start();
}
/**
* 测试自定义方法
*/
@Test
public void testGtCharacterPosition(){
String value="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\n" +
"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";
int index=getCharacterPosition(value,"\"",7);
System.out.println(index);
}
/**
* 测试读取文件
*/
@Test
public void testReadFile() throws Exception{
String path="";
//读取path路径下的文件内容
BufferedReader reader=new BufferedReader
(new InputStreamReader(new FileInputStream(new File(path))));
String line="";
//计数
int count=0;
//解析日志头部的工具类
UserAgentParser userAgentParser=new UserAgentParser();
//String 浏览器类型 Integer 浏览次数
Map<String,Integer> browserMap=new HashMap<String,Integer>();
while(line!=null){
line=reader.readLine();//一次读取一行数据
count++;//记录数据量
if (StringUtils.isNotBlank(line)){
//得到第7个"后的字符串
String source=line.substring(getCharacterPosition(line,"\"",7)+1);
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();
Integer broswerValue=browserMap.get(browser);
if(broswerValue!=null){
//put(K key,V value)
browserMap.put(browser,broswerValue+1);
}else{
browserMap.put(browser,1);
}
//输出结果
System.out.println(browser+", "+engine+", "+engineVersion+", "+os+", "+platform+", "+isMobile);
}
}
System.out.println("records count:"+count);
System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~");
//entrySet将map里的键值对取出来封装成一个Entry对象再存到一个Set中
for(Map.Entry<String,Integer> entry : browserMap.entrySet()){
System.out.println(entry.getKey()+": "+entry.getValue());
}
}
public static void main(String []args){
}
}