Hadoop網站日志數據清洗——正則表達式實現


    周旭龍前輩的Hadoop學習筆記—網站日志分析項目案例簡明、經典,業已成為高校大數據相關專業的實驗項目。上周博主也完成了這個實驗,不同於周前輩使用特殊符號切割字符串得到數據的做法,博主使用了正則表達式來匹配數據。在此將我的思路及代碼張貼出來,以供后來者學習借鑒。

 

一、數據情況分析

    1.1、數據格式概覽

        本次實驗數據來自於國內某論壇,數據以行為單位,每行記錄由5部分組成,訪問者IP、訪問時間、訪問資源、訪問狀態、訪問流量。

    1.2、所需的數據

        按照實驗教程,我們只需要IP、時間、uri即可,不過本着既能完成實驗,又能鍛煉鍛煉的想法,我把發個文狀態以及訪問流量也提取了出來。

    1.3、上傳數據至HDFS

        本次試驗到手的數據大小為60MB,約60萬行。數據量較小,因此直接使用shell命令上傳至HDFS。

    注:欲知更詳細的項目背景,請點擊Hadoop學習筆記—網站日志分析項目案例(一)項目介紹__周旭龍_博客園

    

二、數據清洗准備

    2.1、日志解析類

        將解析日志信息的功能抽象成為一個日志解析類,分別解析各字段信息。

        2.1.1 各字段的正則表達式

            IP位於行的開頭,因此定位到行起始位置,向右讀取字符,直到遇到空格。又因為一個有效的IP最少為四個數字+三個符號,7位;最大為3*4+3,15位。所以行起始處7~15位為IP。表達式為:'^\S{7,15}'

            時間位於一個方括號內,直接提取方括號內的數據即可:'\[.*?\]'

            URI及其相關數據與時間數據位置類似,都在成對符號之內,因此可用相同的解法將其提取出來,再做下一步分析:'\".*?\"'

            狀態碼只有三位數,且兩邊都是空格,可以吧兩邊的空格也提出來,再去掉:' \d{3} '

            流量在行尾,也都是數字:'\d{1,6}$'

        2.1.2 函數

            除了一個parse函數以及五個分別處理字段的函數外,正則表達式匹配也抽象成了一個函數。需要注意的是匹配是否為空,以及匹配uri並將其分割為數組后下標取值是否越界。

  1. public class parseLine {  
  2.         
  3.     public String[] parse(String line)  
  4.     {  
  5.         String ip = parseIP(line);  
  6.         String time = parseTime(line);  
  7.         String url = parseURL(line);  
  8.         String status = parseStatus(line);  
  9.         String traffic = parseTraffic(line);  
  10.             
  11.         return new String[] {ip,time,url,status,traffic};  
  12.     }  
  13.         
  14.     private String parseReg(String reg,String str)  
  15.     {  
  16.         Pattern pat = Pattern.compile(reg);  
  17.         Matcher matcher = pat.matcher(str);  
  18.         boolean rs = matcher.find();  
  19.         if(rs)  
  20.             return matcher.group(0);  
  21.         else  
  22.             return "null";  
  23.     }  
  24.     
  25.     private String[] splitUrl(String str)  
  26.     {  
  27.         String []urlInfo = str.substring(1, str.length()-1).split(" ");  
  28.         return urlInfo;  
  29.     }  
  30.         
  31.     private String parseTraffic(String line) {  
  32.         String reg_ip = "\\d{1,6}$";  
  33.         return parseReg(reg_ip,line);  
  34.     }  
  35.     
  36.     private String parseStatus(String line) {  
  37.         String reg_ip = " \\d{3} ";  
  38.         return parseReg(reg_ip,line).trim();  
  39.     }  
  40.     
  41.     private String parseURL(String line) {  
  42.         String reg_ip = "\".*?\"";  
  43.         String str = parseReg(reg_ip,line);  
  44.         String[] urlInfo = splitUrl(str);  
  45.             
  46.         return urlInfo[1];  
  47.     }  
  48.     
  49.     private String parseTime(String line) {  
  50.         String reg_ip = "\\[.*?\\]";  
  51.         String str = parseReg(reg_ip,line);  
  52.         SimpleDateFormat in=new SimpleDateFormat("[dd/MMM/yyyy:HH:mm:ss ZZZZZ]",Locale.US);   
  53.         SimpleDateFormat out=new SimpleDateFormat("yyyyMMdd HHmmss");   
  54.         Date d = new Date();  
  55.         try  
  56.         {  
  57.             d=in.parse(str);   
  58.         }  
  59.         catch (ParseException e)  
  60.         {   
  61.             e.printStackTrace();  
  62.         }  
  63.         return out.format(d).trim();  
  64.     }  
  65.     
  66.     private String parseIP(String line) {  
  67.         String reg_ip = "^\\S{7,15}";  
  68.         return parseReg(reg_ip,line);  
  69.     }  
  70. }  

 

    2.2、Mapper類

        map階段的輸入為<偏移量,一行文本>,輸出為<偏移量,處理后的數據>。再這個類中,對數據的有效性判斷也在這兒,博主只過濾了靜態數據。

  1. public class logMap extends Mapper<LongWritable,Text,LongWritable,Text>{  
  2.     static parseInfo parseLine = new parseInfo();  
  3.     protected void map(LongWritable key1,Text value1,Context context) throws IOException,InterruptedException  
  4.     {  
  5.         String str1 = value1.toString();  
  6.         Text output = new Text();  
  7.             
  8.         final String[] info = parseLine.parse(str1);  
  9.             
  10.         if(info[2].startsWith("/static") || info[2].startsWith("/uc_server"))  
  11.             return ;  
  12.     
  13.         StringBuilder result = new StringBuilder();  
  14.         for(String x:info)  
  15.             result.append(x).append("\t");  
  16.             
  17.         output.set(result.toString());  
  18.         context.write(key1, output);  
  19.     }  
  20. }  

 

    2.3、Reducer類

        reduce階段的輸入與map的輸出有關,為<偏移量,處理后數據的集合>,輸出則為<處理后的數據,空>。

  1. public class logReducer extends Reducer<LongWritable,Text,Text,NullWritable>{  
  2.     protected void reduce(LongWritable k3,Iterable<Text> v3,Context context) throws IOException,InterruptedException{  
  3.         for(Text v3s : v3)  
  4.             context.write(v3s, NullWritable.get());  
  5.     }  
  6. }  

    2.4、主函數

  1. public class logMain {  
  2.     
  3.     public static void main(String[] args) throws Exception{  
  4.         Job job = Job.getInstance(new Configuration());  
  5.         job.setJarByClass(logMain.class);  
  6.             
  7.         job.setMapperClass(logMap.class);  
  8.         job.setMapOutputKeyClass(LongWritable.class);  
  9.         job.setMapOutputValueClass(Text.class);  
  10.             
  11.         job.setReducerClass(logReducer.class);  
  12.         job.setOutputKeyClass(Text.class);  
  13.         job.setOutputValueClass(NullWritable.class);  
  14.             
  15.         FileInputFormat.setInputPaths(job, new Path(args[0]));  
  16.         FileOutputFormat.setOutputPath(job, new Path(args[1]));  
  17.             
  18.         job.waitForCompletion(true);  
  19.     }  
  20.     
  21. }  

 

三、數據清洗

    將編寫好的MR到處導出為jar包后,在hadoop中用小規模的測試用例試運行,成功后再用實驗數據。

    結果如下:

 

 

注:完整項目

    Hadoop學習筆記—20.網站日志分析項目案例(一)項目介紹

    Hadoop學習筆記—20.網站日志分析項目案例(二)數據清洗

    Hadoop學習筆記—20.網站日志分析項目案例(三)統計分析


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM