hive日志分析案例

1.1 項目來源

本次實踐的目的就在于通過對該技術(shù)論壇網(wǎng)站的tomcat access log日志進行分析,計算該論壇的一些關(guān)鍵指標属愤,供運營者進行決策時參考。

PS:開發(fā)該系統(tǒng)的目的是為了獲取一些業(yè)務(wù)相關(guān)的指標酸役,這些指標在第三方工具中無法獲得的住诸;

1.2 數(shù)據(jù)情況

該論壇數(shù)據(jù)有兩部分:

(1)歷史數(shù)據(jù)約56GB,統(tǒng)計到2012-05-29涣澡。這也說明贱呐,在2012-05-29之前,日志文件都在一個文件里邊入桂,采用了追加寫入的方式奄薇。

(2)自2013-05-30起,每天生成一個數(shù)據(jù)文件抗愁,約150MB左右馁蒂。這也說明呵晚,從2013-05-30之后,日志文件不再是在一個文件里邊远搪。

圖2展示了該日志數(shù)據(jù)的記錄格式劣纲,其中每行記錄有5部分組成:訪問者IP、訪問時間谁鳍、訪問資源癞季、訪問狀態(tài)(HTTP狀態(tài)碼)、本次訪問流量倘潜。

image.png

圖2 日志記錄數(shù)據(jù)格式

二绷柒、關(guān)鍵指標KPI

2.1 瀏覽量PV

image

(1)定義:頁面瀏覽量即為PV(Page View),是指所有用戶瀏覽頁面的總和涮因,一個獨立用戶每打開一個頁面就被記錄1 次废睦。

(2)分析:網(wǎng)站總瀏覽量,可以考核用戶對于網(wǎng)站的興趣养泡,就像收視率對于電視劇一樣嗜湃。

計算公式:記錄計數(shù),從日志中獲取訪問次數(shù)澜掩。

2.2 注冊用戶數(shù)

image

該論壇的用戶注冊頁面為member.php购披,而當用戶點擊注冊時請求的又是member.php?mod=register的url。

計算公式:對訪問member.php?mod=register的url肩榕,計數(shù)刚陡。

2.3 IP數(shù)

image

(1)定義:一天之內(nèi),訪問網(wǎng)站的不同獨立 IP 個數(shù)加和株汉。其中同一IP無論訪問了幾個頁面筐乳,獨立IP 數(shù)均為1。

(2)分析:這是我們最熟悉的一個概念乔妈,無論同一個IP上有多少電腦蝙云,或者其他用戶,從某種程度上來說路召,獨立IP的多少贮懈,是衡量網(wǎng)站推廣活動好壞最直接的數(shù)據(jù)。

計算公式:對不同的訪問者ip优训,計數(shù)

2.4 跳出率

image

(1)定義:只瀏覽了一個頁面便離開了網(wǎng)站的訪問次數(shù)占總的訪問次數(shù)的百分比,即只瀏覽了一個頁面的訪問次數(shù) / 全部的訪問次數(shù)匯總各聘。

(2)分析:跳出率是非常重要的訪客黏性指標揣非,它顯示了訪客對網(wǎng)站的興趣程度:跳出率越低說明流量質(zhì)量越好,訪客對網(wǎng)站的內(nèi)容越感興趣躲因,這些訪客越可能是網(wǎng)站的有效用戶早敬、忠實用戶忌傻。

PS:該指標也可以衡量網(wǎng)絡(luò)營銷的效果舆瘪,指出有多少訪客被網(wǎng)絡(luò)營銷吸引到宣傳產(chǎn)品頁或網(wǎng)站上之后崭别,又流失掉了价认,可以說就是煮熟的鴨子飛了儒喊。比如只嚣,網(wǎng)站在某媒體上打廣告推廣龙巨,分析從這個推廣來源進入的訪客指標使鹅,其跳出率可以反映出選擇這個媒體是否合適篇亭,廣告語的撰寫是否優(yōu)秀绝淡,以及網(wǎng)站入口頁的設(shè)計是否用戶體驗良好宙刘。

計算公式:①統(tǒng)計一天內(nèi)只出現(xiàn)一條記錄的ip,稱為跳出數(shù)牢酵;②跳出數(shù)/PV悬包;

處理步驟

1 數(shù)據(jù)清洗
  使用MapReduce對HDFS中的原始數(shù)據(jù)進行清洗,以便后續(xù)進行統(tǒng)計分析馍乙;

2 統(tǒng)計分析
  使用Hive對清洗后的數(shù)據(jù)進行統(tǒng)計分析布近;

數(shù)據(jù)清洗

一、數(shù)據(jù)情況分析

1.1 數(shù)據(jù)情況回顧

該論壇數(shù)據(jù)有兩部分:

(1)歷史數(shù)據(jù)約56GB丝格,統(tǒng)計到2012-05-29撑瞧。這也說明,在2012-05-29之前铁追,日志文件都在一個文件里邊季蚂,采用了追加寫入的方式。

(2)自2013-05-30起琅束,每天生成一個數(shù)據(jù)文件扭屁,約150MB左右。這也說明涩禀,從2013-05-30之后料滥,日志文件不再是在一個文件里邊。

圖1展示了該日志數(shù)據(jù)的記錄格式艾船,其中每行記錄有5部分組成:訪問者IP葵腹、訪問時間、訪問資源屿岂、訪問狀態(tài)(HTTP狀態(tài)碼)践宴、本次訪問流量。

log

圖1 日志記錄數(shù)據(jù)格式

本次使用數(shù)據(jù)來自于兩個2013年的日志文件爷怀,分別為access_2013_05_30.log與access_2013_05_31.log阻肩,下載地址為:http://pan.baidu.com/s/1pJE7XR9

1.2 要清理的數(shù)據(jù)

(1)根據(jù)前一篇的關(guān)鍵指標的分析,我們所要統(tǒng)計分析的均不涉及到訪問狀態(tài)(HTTP狀態(tài)碼)以及本次訪問的流量运授,于是我們首先可以將這兩項記錄清理掉烤惊;

(2)根據(jù)日志記錄的數(shù)據(jù)格式乔煞,我們需要將日期格式轉(zhuǎn)換為平常所見的普通格式如20150426這種,于是我們可以寫一個類將日志記錄的日期進行轉(zhuǎn)換柒室;

(3)由于靜態(tài)資源的訪問請求對我們的數(shù)據(jù)分析沒有意義渡贾,于是我們可以將"GET /staticsource/"開頭的訪問記錄過濾掉,又因為GET和POST字符串對我們也沒有意義雄右,因此也可以將其省略掉空骚;

二、數(shù)據(jù)清洗過程

2.1 定期上傳日志至HDFS

首先不脯,把日志數(shù)據(jù)上傳到HDFS中進行處理府怯,可以分為以下幾種情況:

(1)如果是日志服務(wù)器數(shù)據(jù)較小、壓力較小防楷,可以直接使用shell命令把數(shù)據(jù)上傳到HDFS中牺丙;

(2)如果日志服務(wù)器非常多、數(shù)據(jù)量大复局,使用flume進行數(shù)據(jù)處理冲簿;

這里我們的實驗數(shù)據(jù)文件較小,因此直接采用第一種Shell命令方式亿昏。

清洗之前

27.19.74.143 - - [30/May/2013:17:38:20 +0800] "GET /static/image/common/faq.gif HTTP/1.1" 200 1127
110.52.250.126 - - [30/May/2013:17:38:20 +0800] "GET /data/cache/style_1_widthauto.css?y7a HTTP/1.1" 200 1292
27.19.74.143 - - [30/May/2013:17:38:20 +0800] "GET /static/image/common/hot_1.gif HTTP/1.1" 200 680
27.19.74.143 - - [30/May/2013:17:38:20 +0800] "GET /static/image/common/hot_2.gif HTTP/1.1" 200 682

清洗之后

110.52.250.126  20130530173820  data/cache/style_1_widthauto.css?y7a
110.52.250.126  20130530173820  source/plugin/wsh_wx/img/wsh_zk.css
110.52.250.126  20130530173820  data/cache/style_1_forum_index.css?y7a
110.52.250.126  20130530173820  source/plugin/wsh_wx/img/wx_jqr.gif
27.19.74.143    20130530173820  data/attachment/common/c8/common_2_verify_icon.png
27.19.74.143    20130530173820  data/cache/common_smilies_var.js?y7a

2.2 編寫MapReduce程序清理日志

package com.neuedu;

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.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.output.FileOutputFormat;

public class LogCleanJob {

        public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
            Configuration conf = new Configuration();

            if (args.length != 2) {
                System.err.println("Usage:Merge and duplicate removal <in> <out>");
                System.exit(2);
            }

            Job job = Job.getInstance(conf, "LogCleanJob");
            job.setJarByClass(LogCleanJob.class);
            job.setMapperClass(MyMapper.class);
            job.setReducerClass(MyReducer.class);
            job.setMapOutputKeyClass(LongWritable.class);
            job.setMapOutputValueClass(Text.class);
            job.setOutputKeyClass(Text.class);
            job.setOutputValueClass(NullWritable.class);
            FileInputFormat.addInputPath(job, new Path(args[0]));
            FileOutputFormat.setOutputPath(job, new Path(args[1]));
            System.exit(job.waitForCompletion(true) ? 0 : 1);
        }


    static class MyMapper extends
            Mapper<LongWritable, Text, LongWritable, Text> {
        LogParser logParser = new LogParser();
        Text outputValue = new Text();

        protected void map(
                LongWritable key,
                Text value,
                Context context)
                throws java.io.IOException, InterruptedException {
            final String[] parsed = logParser.parse(value.toString());

            // step1.過濾掉靜態(tài)資源訪問請求
            if (parsed[2].startsWith("GET /static/")
                    || parsed[2].startsWith("GET /uc_server")) {
                return;
            }
            // step2.過濾掉開頭的指定字符串
            if (parsed[2].startsWith("GET /")) {
                parsed[2] = parsed[2].substring("GET /".length());
            } else if (parsed[2].startsWith("POST /")) {
                parsed[2] = parsed[2].substring("POST /".length());
            }
            // step3.過濾掉結(jié)尾的特定字符串
            if (parsed[2].endsWith(" HTTP/1.1")) {
                parsed[2] = parsed[2].substring(0, parsed[2].length()
                        - " HTTP/1.1".length());
            }
            // step4.只寫入前三個記錄類型項
            outputValue.set(parsed[0] + "\t" + parsed[1] + "\t" + parsed[2]);
            context.write(key, outputValue);
        }
    }

    static class MyReducer extends
            Reducer<LongWritable, Text, Text, NullWritable> {
        protected void reduce(
                LongWritable k2,
                Iterable<Text> values,
                Context context)
                throws java.io.IOException, InterruptedException {
                context.write(values.iterator().next(), NullWritable.get());
        }
    }

    /*
     * 日志解析類
     */
    static class LogParser {
        public static final SimpleDateFormat FORMAT = new SimpleDateFormat(
                "d/MMM/yyyy:HH:mm:ss", Locale.ENGLISH);
        public static final SimpleDateFormat dateformat1 = new SimpleDateFormat(
                "yyyyMMddHHmmss");

        public static void main(String[] args) throws ParseException {
            final String S1 = "27.19.74.143 - - [30/May/2013:17:38:20 +0800] \"GET /static/image/common/faq.gif HTTP/1.1\" 200 1127";
            LogParser parser = new LogParser();
            final String[] array = parser.parse(S1);
            System.out.println("樣例數(shù)據(jù): " + S1);
            System.out.format(
                    "解析結(jié)果:  ip=%s, time=%s, url=%s, status=%s, traffic=%s",
                    array[0], array[1], array[2], array[3], array[4]);
        }

        /**
         * 解析英文時間字符串
         *
         * @param string
         * @return
         * @throws ParseException
         */
        private Date parseDateFormat(String string) {
            Date parse = null;
            try {
                parse = FORMAT.parse(string);
            } catch (ParseException e) {
                e.printStackTrace();
            }
            return parse;
        }

        /**
         * 解析日志的行記錄
         *
         * @param line
         * @return 數(shù)組含有5個元素峦剔,分別是ip、時間角钩、url吝沫、狀態(tài)、流量
         */
        public String[] parse(String line) {
            String ip = parseIP(line);
            String time = parseTime(line);
            String url = parseURL(line);
            String status = parseStatus(line);
            String traffic = parseTraffic(line);

            return new String[] { ip, time, url, status, traffic };
        }

        private String parseTraffic(String line) {
            final String trim = line.substring(line.lastIndexOf("\"") + 1)
                    .trim();
            String traffic = trim.split(" ")[1];
            return traffic;
        }

        private String parseStatus(String line) {
            final String trim = line.substring(line.lastIndexOf("\"") + 1)
                    .trim();
            String status = trim.split(" ")[0];
            return status;
        }

        private String parseURL(String line) {
            final int first = line.indexOf("\"");
            final int last = line.lastIndexOf("\"");
            String url = line.substring(first + 1, last);
            return url;
        }

        private String parseTime(String line) {
            final int first = line.indexOf("[");
            final int last = line.indexOf("+0800]");
            String time = line.substring(first + 1, last).trim();
            Date date = parseDateFormat(time);
            return dateformat1.format(date);
        }

        private String parseIP(String line) {
            String ip = line.split("- -")[0].trim();
            return ip;
        }
    }
}

一递礼、借助Hive進行統(tǒng)計

1.1 準備工作:建立分區(qū)表

HIVE

為了能夠借助Hive進行統(tǒng)計分析惨险,首先我們需要將清洗后的數(shù)據(jù)存入Hive中,那么我們需要先建立一張表脊髓。這里我們選擇分區(qū)表辫愉,以日期作為分區(qū)的指標,建表語句如下:(這里關(guān)鍵之處就在于確定映射的HDFS位置将硝,我這里是/project/techbbs/cleaned即清洗后的數(shù)據(jù)存放的位置)

hive> dfs -mkdir -p /project/techbbs/cleaned

hive>CREATE EXTERNAL TABLE techbbs(ip string, atime string, url string) PARTITIONED BY (logdate string) ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t' LOCATION '/project/techbbs/cleaned';

建立了分區(qū)表之后恭朗,就需要增加一個分區(qū),增加分區(qū)的語句如下:(這里主要針對20150425這一天的日志進行分區(qū))

hive>ALTER TABLE techbbs ADD PARTITION(logdate='2015_04_25') LOCATION '/project/techbbs/cleaned/2015_04_25';

hive> load data local inpath '/root/cleaned' into table techbbs3 partition(logdate='2015_04_25');

1.2 使用HQL統(tǒng)計關(guān)鍵指標

(1)關(guān)鍵指標之一:PV量

頁面瀏覽量即為PV(Page View)依疼,是指所有用戶瀏覽頁面的總和痰腮,一個獨立用戶每打開一個頁面就被記錄1 次。這里律罢,我們只需要統(tǒng)計日志中的記錄個數(shù)即可诽嘉,HQL代碼如下:

hive>CREATE TABLE techbbs_pv_2015_04_25 AS SELECT COUNT(1) AS PV FROM techbbs WHERE logdate='2015_04_25';

image

(2)關(guān)鍵指標之二:注冊用戶數(shù)

該論壇的用戶注冊頁面為member.php,而當用戶點擊注冊時請求的又是member.php?mod=register的url。因此虫腋,這里我們只需要統(tǒng)計出日志中訪問的URL是member.php?mod=register的即可,HQL代碼如下:

hive>CREATE TABLE techbbs_reguser_2015_04_25 AS SELECT COUNT(1) AS REGUSER FROM techbbs WHERE logdate='2015_04_25' AND INSTR(url,'member.php?mod=register')>0;

image

(3)關(guān)鍵指標之三:獨立IP數(shù)

一天之內(nèi)稀余,訪問網(wǎng)站的不同獨立 IP 個數(shù)加和悦冀。其中同一IP無論訪問了幾個頁面,獨立IP 數(shù)均為1睛琳。因此盒蟆,這里我們只需要統(tǒng)計日志中處理的獨立IP數(shù)即可,在SQL中我們可以通過DISTINCT關(guān)鍵字师骗,在HQL中也是通過這個關(guān)鍵字:

hive>CREATE TABLE techbbs_ip_2015_04_25 AS SELECT COUNT(DISTINCT ip) AS IP FROM techbbs WHERE logdate='2015_04_25';

image

(4)關(guān)鍵指標之四:跳出用戶數(shù)

只瀏覽了一個頁面便離開了網(wǎng)站的訪問次數(shù)历等,即只瀏覽了一個頁面便不再訪問的訪問次數(shù)。這里辟癌,我們可以通過用戶的IP進行分組寒屯,如果分組后的記錄數(shù)只有一條,那么即為跳出用戶黍少。將這些用戶的數(shù)量相加寡夹,就得出了跳出用戶數(shù),HQL代碼如下:

hive>CREATE TABLE techbbs_jumper_2015_04_25 AS SELECT COUNT(1) AS jumper FROM (SELECT COUNT(ip) AS times FROM techbbs WHERE logdate='2015_04_25' GROUP BY ip HAVING times=1) e;

image

PS:跳出率是指只瀏覽了一個頁面便離開了網(wǎng)站的訪問次數(shù)占總的訪問次數(shù)的百分比厂置,即只瀏覽了一個頁面的訪問次數(shù) / 全部的訪問次數(shù)匯總菩掏。這里,我們可以將這里得出的跳出用戶數(shù)/PV數(shù)即可得到跳出率昵济。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末智绸,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子访忿,更是在濱河造成了極大的恐慌瞧栗,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,729評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件醉顽,死亡現(xiàn)場離奇詭異沼溜,居然都是意外死亡,警方通過查閱死者的電腦和手機游添,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,226評論 3 399
  • 文/潘曉璐 我一進店門系草,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人唆涝,你說我怎么就攤上這事找都。” “怎么了廊酣?”我有些...
    開封第一講書人閱讀 169,461評論 0 362
  • 文/不壞的土叔 我叫張陵能耻,是天一觀的道長。 經(jīng)常有香客問我,道長晓猛,這世上最難降的妖魔是什么饿幅? 我笑而不...
    開封第一講書人閱讀 60,135評論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮戒职,結(jié)果婚禮上栗恩,老公的妹妹穿的比我還像新娘。我一直安慰自己洪燥,他們只是感情好磕秤,可當我...
    茶點故事閱讀 69,130評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著捧韵,像睡著了一般市咆。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上再来,一...
    開封第一講書人閱讀 52,736評論 1 312
  • 那天蒙兰,我揣著相機與錄音,去河邊找鬼其弊。 笑死癞己,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的梭伐。 我是一名探鬼主播痹雅,決...
    沈念sama閱讀 41,179評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼糊识!你這毒婦竟也來了绩社?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,124評論 0 277
  • 序言:老撾萬榮一對情侶失蹤赂苗,失蹤者是張志新(化名)和其女友劉穎愉耙,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體拌滋,經(jīng)...
    沈念sama閱讀 46,657評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡朴沿,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,723評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了败砂。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片赌渣。...
    茶點故事閱讀 40,872評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖昌犹,靈堂內(nèi)的尸體忽然破棺而出坚芜,到底是詐尸還是另有隱情,我是刑警寧澤斜姥,帶...
    沈念sama閱讀 36,533評論 5 351
  • 正文 年R本政府宣布鸿竖,位于F島的核電站沧竟,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏缚忧。R本人自食惡果不足惜悟泵,卻給世界環(huán)境...
    茶點故事閱讀 42,213評論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望搔谴。 院中可真熱鬧魁袜,春花似錦、人聲如沸敦第。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,700評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽芜果。三九已至,卻和暖如春融师,著一層夾襖步出監(jiān)牢的瞬間右钾,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,819評論 1 274
  • 我被黑心中介騙來泰國打工旱爆, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留舀射,地道東北人。 一個月前我還...
    沈念sama閱讀 49,304評論 3 379
  • 正文 我出身青樓怀伦,卻偏偏與公主長得像脆烟,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子房待,可洞房花燭夜當晚...
    茶點故事閱讀 45,876評論 2 361

推薦閱讀更多精彩內(nèi)容

  • 1.1 項目來源 本次實踐的目的就在于通過對該技術(shù)論壇網(wǎng)站的tomcat access log日志進行分析邢羔,計算該...
    piziyang12138閱讀 469評論 0 0
  • 1.1 項目來源 本次實踐的目的就在于通過對該技術(shù)論壇網(wǎng)站的tomcat access log日志進行分析,計算該...
    __豆約翰__閱讀 2,361評論 0 6
  • 1.1 項目來源 本次實踐的目的就在于通過對該技術(shù)論壇網(wǎng)站的tomcat access log日志進行分析桑孩,計算該...
    Arroganter閱讀 828評論 0 0
  • 中國互聯(lián)網(wǎng)用戶群已經(jīng)成為世界最大的互聯(lián)網(wǎng)群體拜鹤。與此同時,中國互聯(lián)網(wǎng)網(wǎng)站的發(fā)展也歷經(jīng)了幾個階段流椒,從單純的網(wǎng)絡(luò)媒體到現(xiàn)...
    零一間閱讀 4,271評論 1 41
  • 關(guān)于Mongodb的全面總結(jié) MongoDB的內(nèi)部構(gòu)造《MongoDB The Definitive Guide》...
    中v中閱讀 31,953評論 2 89