java(版)数据分析--网站访问日志session数据分析
数据
数据中的字段分别为:
访客ip地址
访客访问时间
访客请求的url及协议
网站响应码
网站返回数据量
访客的referral url
访客的客户端操作系统及浏览器信息
附带数据文件:
需求:
1) 需要为从访问日志中梳理出每一个session(如果一个用户两次相邻请求之间的时间差<30分钟,则该两次请求都属于同一个session,否则分属不同的session),并为session中的历次请求打上序号,示意如下:
session号 |
ip地址 |
请求时间 |
请求url |
请求次序 |
其他字段...... |
session1 |
ip1 |
2017-10-11 08:10:30 |
/a |
1 |
...... |
session1 |
ip1 |
2017-10-11 08:11:20 |
/b |
2 |
...... |
session2 |
ip1 |
2017-10-11 09:10:30 |
/c |
1 |
...... |
步骤分析:
1 使用正则获取数据文件中的数据
2 对数据ip分组 map<ip,List<Bean>> list并按时间排序照序
3 判断两个相邻的url的时间间隔来判断是否是同一个session,并生成对应的sessionID和序号
4 对结果集处理 获取想要的结果 输出
String ipRegex = "(\\d}+\\.){3}\\d+";
String dateRegex = "\\[.+\\d\\]";
String urlRegex = "(GET|POST){1}\\s(\\S)*\\s";
new SimpleDateFormat("dd/MMM/yyyy:HH:mm:ss", Locale.US);
技术分析:
时间格式转换,时间间隔,LIst集合和Map集合,集合的排序,正则表达式
2) 将每次session进行汇总,得出用户每次session的浏览起、止页面,每次session会话总时长等,示意如下:
session号 |
ip地址 |
起始请求时间 |
结束请求时间 |
起始页面 |
跳出页面 |
访问时长 |
session1 |
ip1 |
2017-10-11 08:10:30 |
2017-10-11 08:11:20 |
/a |
/b |
50秒 |
session2 |
ip1 |
2017-10-11 09:10:30 |
2017-10-11 09:10:30 |
/c |
/c |
默认值 |
session3 |
ip2 |
2017-10-11 07:15:10 |
2017-10-11 07:30:10 |
/h |
/x |
750秒 |
步骤分析:
根据第一步的基础上获取同一个sessionID对应的第一个编号的URL和最后一个编号的URL
计算两个url的时间间隔
1)代码实现
1 读取文件数据,使用正则获取匹配的字符串
/*
* 由于读取的数据是不完全规则的数据,使用正则表达式去匹配更为简单
*/
String ipRegex = "(\\d+\\.){3}\\d+";
String dateRegex = "\\[.+\\d\\]";
String urlRegex = "(GET|POST){1}\\s(\\S)*\\s";
while ((line = reader.readLine()) != null) {
//调用正则匹配数据的方法来获取 指定的IP URL DATE
String ip = findSubStrngByRegex(ipRegex, line);
String dateStr = findSubStrngByRegex(dateRegex, line);
String url = findSubStrngByRegex(urlRegex, line);
获取匹配子串的方法
/**
* 使用正则表达式获取匹配的子串
* @param rex
* @param line
* @return
*/
private static String findSubStrngByRegex(String rex, String line) {
String res = null;
Pattern pattern = Pattern.compile(rex);
Matcher matcher = pattern.matcher(line);
while (matcher.find()) {
res = matcher.group();
}
return res;
}
2将获取的数据封装到SessionBean中 ,需要将字符串转换成时间对象
字符串转时间的方法
/**
* 将字符串转换成时间
* @param dateStr
* @return
*/
private static Date parseDate(String dateStr) {
Date date = null;
try {
String newStr = dateStr.substring(1, dateStr.length() - 1);
SimpleDateFormat format = new SimpleDateFormat("dd/MMM/yyyy:HH:mm:ss", Locale.US);
date = format.parse(newStr);
} catch (ParseException e) {
e.printStackTrace();
}
return date;
}
if (ip != null && dateStr != null && url != null) {
Date date = parseDate(dateStr);
// System.out.println(url);
SessionBean bean = new SessionBean();
bean.setIp(ip);
bean.setDate(date);
//去除url中的请求方式
bean.setUrl(url.replace("GET ", "").replace("POST ", ""));
3将数据封装在map中 ip作为key,List<SessionBean>作为value
list中的数据需要按照时间排序,两种方式解决,
第一种: 可以在放入map之前对List排序
第二种: 可以多map的value指定字段排序进行排序 √
//根据ip获取数据 getOrDefault()方法会有默认值不需要判断
List<SessionBean> default1 = map.getOrDefault(ip, new ArrayList<>());
default1.add(bean);
map.put(ip, default1);
4 对结果集的map的value按照时间排序
/**
* 对map数据按时间排序
* @param map
*/
private static void sortMapByDate(Map<String, List<SessionBean>> map) {
Set<Entry<String, List<SessionBean>>> entrySet = map.entrySet();
for (Entry<String, List<SessionBean>> entry : entrySet) {
List<SessionBean> list = entry.getValue();
Collections.sort(list, new Comparator<SessionBean>() {
@Override
public int compare(SessionBean o1, SessionBean o2) {
// TODO Auto-generated method stub
return o1.getDate().compareTo(o2.getDate());
}
});
map.put(entry.getKey(), list);
}
}
5 结果集生成sessionID和序号
主要是对结果集map中的list数据操作
1 如果一个ip对应的list中只有一个SessionBean说明只有一个请求,那么这个ip只有一个URL对应的一个session和序号为1
2 如果一个ip对应的list中的长度大于1,则要判断两个相邻的url的事件差值是否在30分钟内,如果在30分钟内说明是一个session,响应的Order序号加1 ,
如果不在30分钟内,说明是两个不同的session
private static void mkSessionId(Map<String, List<SessionBean>> map) {
Set<Entry<String, List<SessionBean>>> entrySet = map.entrySet();
for (Entry<String, List<SessionBean>> entry : entrySet) {
List<SessionBean> list = entry.getValue();
if (list.size() == 1) {//只有一个数据,说明只有一个sessionID
SessionBean bean = list.get(0);
bean.setSessionID(getSesionId(bean.getIp()));
bean.setOrder(1);
}
如果list中有多个数据
for (int i = 0; i < list.size() - 1; i++) {
if (list.size() > 1) {// 两个url
//根据时间判断是否是一个sessionID
if (isSameSesison(list.get(i), list.get(i + 1))) {// 同一个sesison
if (list.get(i).getSessionID() != null) {
list.get(i + 1).setSessionID(list.get(i).getSessionID());
} else {
list.get(i).setSessionID(getSesionId(list.get(i).getIp()));
list.get(i + 1).setSessionID(list.get(i).getSessionID());
}
if (list.get(i).getOrder() != null) {
list.get(i + 1).setOrder(list.get(i).getOrder() + 1);
} else {
list.get(i).setOrder(1);
list.get(i + 1).setOrder(list.get(i).getOrder() + 1);
}
} else {// 两个相邻的url不是同一个session
if (list.get(i).getSessionID() == null) {
list.get(i).setSessionID(getSesionId(list.get(i).getIp()));
}
list.get(i + 1).setSessionID(getSesionId(list.get(i + 1).getIp()));
if (list.get(i).getOrder() == null) {
list.get(i).setOrder(1);
}
list.get(i + 1).setOrder(1);
}
}
}
map.put(entry.getKey(), list);
}
}
生成sessionId的方法
public static String getSesionId(String ip) {
long l = IpUtil.ipStrToLongIp(ip);
return l + ":" + System.nanoTime();
}
判断是否是同一个session的方法
private static boolean isSameSesison(SessionBean s1, SessionBean s2) {
boolean flag = false;
long l1 = s1.getDate().getTime();
long l2 = s2.getDate().getTime();
long l3 = l2 - l1;
if (l3 >= 0 && l3 <= (1000 * 30 * 60)) {
flag = true;
}
return flag;
}
处理结果集 打印或者输出结果
更多内容 qq:598196583 附带视频