大数据离线web网站日志分析

1、背景

1.1 黑马论坛日志,数据分为两部分组成,原来是一个大文件,是56GB;以后每天生成一个文件,大约是150-200MB之间;

1.2 日志格式是apache common日志格式;

1.3 分析一些核心指标,供运营决策者使用;

1.4 开发该系统的目的是分了获取一些业务相关的指标,这些指标在第三方工具中无法获得的;

 

2、开发大致流程:

2.1 把日志数据上传到HDFS中进行处理

如果是日志服务器数据较小、压力较小,可以直接使用shell命令把数据上传到HDFS中;

    如果是日志服务器数据较大、压力较大,使用NFS在另一台服务器上上传数据;

    如果日志服务器非常多、数据量大,使用flume进行数据处理;

2.2 使用MapReduce对HDFS中的原始数据进行清洗;

2.3 使用Hive对清洗后的数据进行统计分析;

2.4 使用Sqoop把Hive产生的统计结果导出到mysql中;

2.5 如果用户需要查看详细数据的话,可以使用HBase进行展现;

 

3、数据准备:

 

完整日志测试数据下载地址:https://download.csdn.net/download/MrZhangBaby/14027892

 

每行记录有5部分组成: 
1.访问ip 
2.访问时间 
3.访问资源【跟着两个访问的Url】 
4.访问状态 
5.本次流量 


截取部分数据如下:

27.19.74.143 - - [30/May/2013:17:38:21 +0800] "GET /static/image/smiley/default/shy.gif HTTP/1.1" 200 2663

8.35.201.163 - - [30/May/2013:17:38:21 +0800] "GET /static/image/common/nv_a.png HTTP/1.1" 200 2076

27.19.74.143 - - [30/May/2013:17:38:21 +0800] "GET /static/image/smiley/default/titter.gif HTTP/1.1" 200 1398

27.19.74.143 - - [30/May/2013:17:38:21 +0800] "GET /static/image/smiley/default/sweat.gif HTTP/1.1" 200 1879

27.19.74.143 - - [30/May/2013:17:38:21 +0800] "GET /static/image/smiley/default/mad.gif HTTP/1.1" 200 2423

27.19.74.143 - - [30/May/2013:17:38:21 +0800] "GET /static/image/smiley/default/hug.gif HTTP/1.1" 200 1054

27.19.74.143 - - [30/May/2013:17:38:21 +0800] "GET /static/image/smiley/default/lol.gif HTTP/1.1" 200 1443

27.19.74.143 - - [30/May/2013:17:38:21 +0800] "GET /static/image/smiley/default/victory.gif HTTP/1.1" 200 1275

27.19.74.143 - - [30/May/2013:17:38:21 +0800] "GET /static/image/smiley/default/time.gif HTTP/1.1" 200 687

27.19.74.143 - - [30/May/2013:17:38:21 +0800] "GET /static/image/smiley/default/kiss.gif HTTP/1.1" 200 987

27.19.74.143 - - [30/May/2013:17:38:21 +0800] "GET /static/image/smiley/default/handshake.gif HTTP/1.1" 200 1322

27.19.74.143 - - [30/May/2013:17:38:21 +0800] "GET /static/image/smiley/default/loveliness.gif HTTP/1.1" 200 1579

27.19.74.143 - - [30/May/2013:17:38:21 +0800] "GET /static/image/smiley/default/call.gif HTTP/1.1" 200 603

27.19.74.143 - - [30/May/2013:17:38:21 +0800] "GET /static/image/smiley/default/funk.gif HTTP/1.1" 200 2928

27.19.74.143 - - [30/May/2013:17:38:21 +0800] "GET /static/image/smiley/default/curse.gif HTTP/1.1" 200 1543

27.19.74.143 - - [30/May/2013:17:38:21 +0800] "GET /static/image/smiley/default/dizzy.gif HTTP/1.1" 200 1859

27.19.74.143 - - [30/May/2013:17:38:21 +0800] "GET /static/image/smiley/default/shutup.gif HTTP/1.1" 200 2500

27.19.74.143 - - [30/May/2013:17:38:21 +0800] "GET /static/image/smiley/default/sleepy.gif HTTP/1.1" 200 2375

8.35.201.164 - - [30/May/2013:17:38:21 +0800] "GET /static/image/common/pn.png HTTP/1.1" 200 592

8.35.201.165 - - [30/May/2013:17:38:21 +0800] "GET /uc_server/avatar.php?uid=56212&size=middle HTTP/1.1" 301 -

27.19.74.143 - - [30/May/2013:17:38:21 +0800] "GET /static/image/common/uploadbutton_small.png HTTP/1.1" 200 690

8.35.201.160 - - [30/May/2013:17:38:21 +0800] "GET /static/image/common/fastreply.gif HTTP/1.1" 200 608

8.35.201.160 - - [30/May/2013:17:38:21 +0800] "GET /uc_server/avatar.php?uid=21212&size=middle HTTP/1.1" 301

4、相关关键指标及计算逻辑 
⊙浏览量PV

定义:页面浏览量即为PV(Page View),是指所有用户浏览页面的总和,一个独立用户每打开一个页面就被记录1 次。

分析:网站总浏览量,可以考核用户对于网站的兴趣,就像收视率对于电视剧一样。但是对于网站运营者来说,更重要的是,每个栏目下的浏览量。

计算公式:记录计数

⊙注册用户数

计算公式:对访问member.php?mod=register的url,计数

⊙IP数

定义:一天之内,访问网站的不同独立IP 个数加和。其中同一IP无论访问了几个页面,独立IP 数均为1。

分析:这是我们最熟悉的一个概念,无论同一个IP上有多少电脑,或者其他用户,从某种程度上来说,独立IP的多少,是衡量网站推广活动好坏最直接的数据。

公式:对不同ip,计数

⊙跳出率

定义:只浏览了一个页面便离开了网站的访问次数占总的访问次数的百分比,即只浏览了一个页面的访问次数 / 全部的访问次数汇总。

分析:跳出率是非常重要的访客黏性指标,它显示了访客对网站的兴趣程度:跳出率越低说明流量质量越好,访客对网站的内容越感兴趣,这些访客越可能是网站的有效用户、忠实用户。

该指标也可以衡量网络营销的效果,指出有多少访客被网络营销吸引到宣传产品页或网站上之后,又流失掉了,可以说就是煮熟的鸭子飞了。比如,网站在某媒体上打广告推广,分析从这个推广来源进入的访客指标,其跳出率可以反映出选择这个媒体是否合适,广告语的撰写是否优秀,以及网站入口页的设计是否用户体验良好。

计算公式:(1)统计一天内只出现一条记录的ip,称为跳出数 
(2)跳出数/PV

⊙版块热度排行榜

定义:版块的访问情况排行。

分析:巩固热点版块成绩,加强冷清版块建设。同时对学科建设也有影响。

计算公式:按访问次数统计排序

 

5、具体开发实现:

5-1、把日志数据上传到HDFS中进行处理 
如果是日志服务器数据较小、压力较小,可以直接使用shell命令把数据上传到HDFS中; 
如果是日志服务器数据较大、压力较答,使用NFS在另一台服务器上上传数据; 
如果日志服务器数据非常多、数据量大,使用flume进行数据处理;

[root@hadoop11 mydata]# hadoop fs -put access_2013_05_30.log /hmbbs_logs/

[root@hadoop11 mydata]# hadoop fs -lsr /hmbbs_logs/

lsr: DEPRECATED: Please use 'ls -R' instead.

-rw-r--r-- 3 root supergroup 61084192 2016-07-22 14:42 /hmbbs_logs/access_2013_05_30.log

5-2、使用MapReduce对数据进行清洗,清洗后的数据易于我们的使用 
清洗标准:将Apache log日志清洗成我们熟悉的日志格式。 
① 
访问状态和本次流量字段去掉 
② 
过滤掉静态记录,只要动态记录; 
GET /static 或者 GET /uc_server 开头的行文本舍弃 
过滤掉了开头和结尾的标志信息; 
GET / POST / 
HTTP/1.1 HTTP/1.0 
清洗日志的MapReduce源代码:

package Hmbbs;
 
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
 
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.NullWritable;
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.input.TextInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.hadoop.mapreduce.lib.output.TextOutputFormat;
import org.apache.hadoop.mapreduce.lib.partition.HashPartitioner;
;
 
//本程序的目的是通过MapReduce对Hmbbs中的日志数据进行清洗时
public class HmbbsCleaner  
{
        public static String path1="";//指定文件的输入路径
        public static String path2="";//指定日志的输出路径
        public static void main(String[] args) throws Exception
        {
            path1 = args[0];
            path2 = args[1];
 
            Configuration conf = new Configuration();
            conf.set("fs.defaultFS", "hdfs://hadoop11:9000/");
            FileSystem fileSystem = FileSystem.get(conf);
            if(fileSystem.exists(new Path(path2)))
            {
                fileSystem.delete(new Path(path2), true);
            }
            Job job = Job.getInstance(conf, "HmbbsCleaner");
            job.setJarByClass(HmbbsCleaner.class);//jar包
            //编写驱动
            FileInputFormat.setInputPaths(job, new Path(path1));
            job.setInputFormatClass(TextInputFormat.class);
            job.setMapperClass(MyMapper.class);
            job.setMapOutputKeyClass(Text.class);
            job.setMapOutputValueClass(NullWritable.class);
            job.setNumReduceTasks(1);//指定Reducer的任务数量为1
            job.setPartitionerClass(HashPartitioner.class);
            job.setReducerClass(MyReducer.class);
            job.setOutputKeyClass(Text.class);
            job.setOutputValueClass(NullWritable.class);
            FileOutputFormat.setOutputPath(job, new Path(path2));
            job.setOutputFormatClass(TextOutputFormat.class);
 
            //提交任务
            job.waitForCompletion(true);
        }
      public static class MyMapper extends Mapper<LongWritable, Text, Text, NullWritable>
      {
            protected void map(LongWritable k1, Text v1,Context context)throws IOException, InterruptedException
            {
                 String string = v1.toString();//获取待记录
                 Parselogs parselogs = new Parselogs();
                try
                {
                     String[] sub = parselogs.parseString(string);
                     if(sub[2].startsWith("GET /static")||sub[2].startsWith("GET /uc_server"))
                            return ;//对于静态的记录直接过滤掉,不进行任何处理
 
                     if(sub[2].startsWith("GET /"))
                     {
                            sub[2] = sub[2].substring("GET /".length());
                     }
                     if(sub[2].startsWith("POST /"))
                     {
                            sub[2] = sub[2].substring("POST /".length());
                     }//过滤掉了开头和结尾的标志信息
                     if(sub[2].endsWith(" HTTP/1.1"))
                     {
                            sub[2] = sub[2].substring(0, sub[2].length()-" HTTP/1.1".length());
                     }
                     if(sub[2].endsWith(" HTTP/1.0"))
                     {
                            sub[2] = sub[2].substring(0, sub[2].length()-" HTTP/1.0".length());
                     }
                     Text k2 = new Text();
                     k2.set(sub[0]+"\t"+sub[1]+"\t"+sub[2]);//三个字段之间以制表符进行分开
                     context.write(k2, NullWritable.get()); 
                } 
 
                catch (ParseException e)
                {
                    e.printStackTrace();
                }
            }
      }
      public static class MyReducer extends Reducer<Text, NullWritable, Text, NullWritable>
      {
                protected void reduce(Text k2, Iterable<NullWritable> v2s,Context context)throws IOException, InterruptedException
                {
                     for (NullWritable v2 : v2s)
                    {
                         Text k3 = k2;
                         context.write(k3, NullWritable.get());
                    }
                }
      }
}
class Parselogs //Parselogs这个类用来对字符串进行解析
{
     public String[]  parseString(String str) throws ParseException
     {
        String  str1 = parseIp(str);
        String  str2 = parseDate(str);
        String  str3 = parseUrl(str);
        //String  str4 = parseStatus(str);
        //String  str5 = parseFlow(str);
        //String[] str66 = new String[]{str1,str2,str3,str4,str5};
        String[] str66 = new String[]{str1,str2,str3};//在这里只获取与本次项目有关的数据
        return str66;
     }
     public String parseIp(String str)//对ip地址进行解析的方法
     {
          String[] splited = str.split(" - - ");//用指定的正则表达式进行切分,获取我们需要的字段
          return splited[0];
     }
     public String parseDate(String str) throws ParseException
     {
         String[] splited = str.split(" - - ");//用指定的正则表达式进行切分,获取我们需要的字段
         int index1 = splited[1].indexOf("[");
         int index2 = splited[1].indexOf("]");
         String substring = splited[1].substring(index1+1, index2);//到此获取了时间字段30/May/2013:17:38:20 +0800
         SimpleDateFormat simple1 = new SimpleDateFormat("dd/MMM/yyyy:HH:mm:ss", Locale.ENGLISH);//匹配我们给定的字符串,并将其解析成对应的时间
         SimpleDateFormat simple2 = new SimpleDateFormat("yyyyMMddHHmmss");//匹配我们给定的字符串
         Date parse = simple1.parse(substring);
         String format = simple2.format(parse);
         return format;
     }
     public String parseUrl(String str)//获取访问的url
     {
          int index1 = str.indexOf("]");
          int index2= str.lastIndexOf("\"");
          String substring = str.substring(index1+3, index2);
          return substring;
     }
     public String parseStatus(String str)//获取访问的状态
     {
         int index1= str.lastIndexOf("\"");
         String str2 = str.substring(index1+1).trim();
         String[] splited = str2.split(" ");
         return splited[0];
     }
     public String parseFlow(String str)//获取访问的状态
     {
         int index1= str.lastIndexOf("\"");
         String str2 = str.substring(index1+1).trim();
         String[] splited = str2.split(" ");
         return splited[1];
     }
}

5-3、使用hive对清洗后的数据进行多维分析 
(1)统计每日的pv(浏览量)

hive> create table hmbbs_pv as select count(1) as pv from hmbbs_table;

(2)统计每日的register(注册用户数)

hive> create table hmbbs_register as select count(1) as register from hmbbs_table where instr(urllog,'member.php?mod=register') > 0;

(3)统计每日的独立的ip

hive> create table hmbbs_ip as select count(distinct iplog) as ip from hmbbs_table;

(4)统计每日的独立的跳出率
 

hive> CREATE TABLE hmbbs_jumper AS SELECT COUNT(1) AS jumper FROM (SELECT COUNT(iplog) AS times FROM   hmbbs_table  GROUP BY iplog  HAVING times=1) e ;

到此获得了各个参数的结果:

hive> show tables;

OK

hmbbs_ip

hmbbs_jumper

hmbbs_pv

hmbbs_register

hmbbs_table

Time taken: 0.081 seconds

hive> select * from hmbbs_ip;

OK

10411

Time taken: 0.111 seconds

hive> select * from hmbbs_jumper;

OK

3749

Time taken: 0.107 seconds

hive> select * from hmbbs_pv;

OK

169857

Time taken: 0.108 seconds

hive> select * from hmbbs_register;

OK

28

Time taken: 0.107 seconds

(5) 将结果汇总:

CREATE TABLE hmbbs_2013_05_30 AS SELECT '2013_05_30', a.pv, b.reguser, c.ip, d.jumper FROM hmbbs_pv a JOIN hmbbs_register b ON 1=1 JOIN hmbbs_ip c ON 1=1 JOIN hmbbs_jumper d ON 1=1 ;

4、将hive分析的结果使用sqoop导出到mysql中

sqoop export --connect jdbc:mysql://hadoop0:3306/mydata --username root --password admin --table hmresult --fields-terminated-by '\001' --export-dir '/hive/hmbbs_2013_05_30'

接下来我们在mysql中查看数据: 

è¿éåå¾çæè¿°

  • 4
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 25
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 25
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

MrZhangBaby

请博主喝杯奶茶

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值