筆者是一個癡迷于挖掘資料中的價值的學習人,希望在平日的工作學習中,挖掘資料的價值,找尋資料的秘密,筆者認為,資料的價值不僅僅隻展現在企業中,個人也可以體會到資料的魅力,用技術力量探索行為密碼,讓大資料助跑每一個人,歡迎直筒們關注我的公衆号,大家一起讨論資料中的那些有趣的事情。
我的公衆号為:livandata
網站點選流資料分析,業務知識,推薦書籍:
《網站分析實戰——如何以資料驅動決策,提升網站價值》王彥平,吳盛鋒編著
網站找些案例:
本文對應的代碼為:https://github.com/livan123/web_click_mr
- 網站點選流資料分析項目業務背景
1.1 什麼是點選流資料
1.1.1 WEB通路日志
即指使用者通路網站時的所有通路、浏覽、點選行為資料。比如點選了哪一個連結,在哪個網頁停留時間最多,采用了哪個搜尋項、總體浏覽時間等。而所有這些資訊都可被儲存在網站日志中。通過分析這些資料,可以獲知許多對網站營運至關重要的資訊。采集的資料越全面,分析就能越精準。
- 日志的生成管道:
1)是網站的web伺服器所記錄的web通路日志;
2)是通過在頁面嵌入自定義的js代碼來擷取使用者的所有通路行為(比如滑鼠懸停的位置,點選的頁面元件等),然後通過ajax請求到背景記錄日志;這種方式所能采集的資訊最全面;
3)通過在頁面上埋點1像素的圖檔,将相關頁面通路資訊請求到背景記錄日志;
- 日志資料内容詳述:
在實際操作中,有以下幾個方面的資料可以被采集:
- 訪客的系統屬性特征。比如所采用的作業系統、浏覽器、域名和通路速度等。
- 通路特征。包括停留時間、點選的URL等。
- 來源特征。包括網絡内容資訊類型、内容分類和來訪URL等。
- 産品特征。包括所通路的産品編号、産品類别、産品顔色、産品價格、産品利潤、産品數量和特價等級等。
以電商某東為例,其點選日志格式如下:
GET /log.gif?t=item.010001&m=UA-J2011-1&pin=-&uid=1679790178&sid=1679790178|12&v=je=1$sc=24-bit$sr=1600x900$ul=zh-cn$cs=GBK$dt=【雲南白藥套裝】 雲南白藥 牙膏 180g×3 (留蘭香型)【行情 報價 價格 評測】-京東$hn=item.jd.com$fl=16.0 r0$os=win$br=chrome$bv=39.0.2171.95$wb=1437269412$xb=1449548587$yb=1456186252$zb=12$cb=4$usc=direct$ucp=-$umd=none$uct=-$ct=1456186505411$lt=0$tad=-$sku=1326523$cid1=1316$cid2=1384$cid3=1405$brand=20583$pinid=-&ref=&rm=1456186505411 HTTP/1.1 |
1.1.2 點選流資料模型
點選流概念
點選流這個概念更注重使用者浏覽網站的整個流程,網站日志中記錄的使用者點選就像是圖上的“點”,而點選流更像是将這些“點”串起來形成的“線”。也可以把“點”認為是網站的Page,而“線”則是通路網站的Session。是以點選流資料是由網站日志中整理得到的,它可以比網站日志包含更多的資訊,進而使基于點選流資料統計得到的結果更加豐富和高效。
點選流模型生成
點選流資料在具體操作上是由散點狀的點選日志資料梳理所得,進而,點選資料在資料模組化時應該存在兩張模型表(Pageviews和visits):
- 用于生成點選流的通路日志表
時間戳 | IP位址 | Cookie | Session | 請求URL | Referal |
2012-01-01 12:31:12 | 101.0.0.1 | User01 | S001 | /a/... | somesite.com |
2012-01-01 12:31:16 | 201.0.0.2 | User02 | S002 | /a/... | - |
2012-01-01 12:33:06 | 101.0.0.2 | User03 | S002 | /b/... | baidu.com |
2012-01-01 15:16:39 | 234.0.0.3 | User01 | S003 | /c/... | google.com |
2012-01-01 15:17:11 | 101.0.0.1 | User01 | S004 | /d/... | /c/... |
2012-01-01 15:19:23 | 101.0.0.1 | User01 | S004 | /e/... | /d/.... |
- 頁面點選流模型Pageviews表
Session | userid | 時間 | 通路頁面URL | 停留時長 | 第幾步 |
S001 | User01 | 2012-01-01 12:31:12 | /a/.... | 30 | 1 |
S002 | User02 | 2012-01-01 12:31:16 | /a/.... | 10 | 1 |
S002 | User02 | 2012-01-01 12:33:06 | /b/.... | 110 | 2 |
S002 | User02 | 2012-01-01 12:35:06 | /e/.... | 30 | 3 |
- 點選流模型Visits表
Session | 起始時間 | 結束時間 | 進入頁面 | 離開頁面 | 通路頁面數 | IP | cookie | referal |
S001 | 2012-01-01 12:31:12 | 2012-01-01 12:31:12 | /a/... | /a/... | 1 | 101.0.0.1 | User01 | somesite.com |
S002 | 2012-01-01 12:31:16 | 2012-01-01 12:35:06 | /a/... | /e/... | 3 | 201.0.0.2 | User02 | - |
S003 | 2012-01-01 12:35:42 | 2012-01-01 12:35:42 | /c/... | /c/... | 1 | 234.0.0.3 | User03 | baidu.com |
S003 | 2012-01-01 15:16:39 | 2012-01-01 15:19:23 | /c/... | /e/... | 3 | 101.0.0.1 | User01 | google.com |
…… | …… | …… | …… | …… | …… | …… | …… | …… |
這就是點選流模型。當WEB日志轉化成點選流資料的時候,很多網站分析度量的計算變得簡單了,這就是點選流的“魔力”所在。基于點選流資料我們可以統計出許多常見的網站分析度量
作業:
1.原始資料:access.log.fensi
2.清洗資料,得到規整的結果,即上面第一個表;
時間戳 | IP位址 | Cookie | Session | 請求URL | Referal |
2012-01-01 12:31:12 | 101.0.0.1 | User01 | S001 | /a/... | somesite.com |
2012-01-01 12:31:16 | 201.0.0.2 | User02 | S002 | /a/... | - |
2012-01-01 12:33:06 | 101.0.0.2 | User03 | S002 | /b/... | baidu.com |
2012-01-01 15:16:39 | 234.0.0.3 | User01 | S003 | /c/... | google.com |
2012-01-01 15:17:11 | 101.0.0.1 | User01 | S004 | /d/... | /c/... |
2012-01-01 15:19:23 | 101.0.0.1 | User01 | S004 | /e/... | /d/.... |
3.從上述資料清洗出第二個表Pageviews、第三個表Visits。
1.2網站流量資料分析的意義
網站流量統計分析,可以幫助網站管理者、營運人員、推廣人員等實時擷取網站流量資訊,并從流量來源、網站内容、網站訪客特性等多方面提供網站分析的資料依據。進而幫助提高網站流量,提升網站使用者體驗,讓訪客更多的沉澱下來變成會員或客戶,通過更少的投入擷取最大化的收入。
如下表:
網站的眼睛 | 網站的神經 | 網站的大腦 |
通路者來自哪裡? 通路者在尋找什麼? 哪些頁面最受歡迎? 通路者從哪裡進入? | 網頁布局合理嗎? 網站導航清晰嗎? 哪些功能存在問題 網站内容有效嗎 轉化路徑靠譜嗎? | 如何分解目标? 如何配置設定廣告預算? 如何衡量産品表現? 哪些産品需要優化? 哪些名額需要關注? |
點選流分析的意義可分為兩大方面:
1、技術上:
可以合理修改網站結構及适度配置設定資源,建構背景伺服器群組,比如
輔助改進網絡的拓撲設計,提高性能
在有高度相關性的節點之間安排快速有效的通路路徑
幫助企業更好地設計網站首頁和安排網頁内容
2、業務上:
- 幫助企業改善市場營銷決策,如把廣告放在适當的Web頁面上。
- 優化頁面及業務流程設計,提高流量轉化率。
- 幫助企業更好地根據客戶的興趣來安排内容。
- 幫助企業對客戶群進行細分,針對不同客戶制定個性化的促銷政策等。
終極目标是:改善網站(電商、社交、電影、小說)的營運,擷取更高投資回報率(ROI)
1.3 如何進行網站流量分析
流量分析整體來說是一個内涵非常豐富的體系,其整體過程是一個金字塔結構:
1.3.1 流量分析模型舉例
通常有以下幾大類的分析需求:
1)網站流量品質分析
流量對于每個網站來說都是很重要,但流量并不是越多越好,應該更加看重流量的品質,換句話來說就是流量可以為我們帶來多少收入。
2)網站流量多元度細分
細分是指通過不同次元對名額進行分割,檢視同一個名額在不同次元下的表現,進而找出有問題的那部分名額,對這部分名額進行優化。
3)網站内容及導航分析
對于所有網站來說,頁面都可以被劃分為三個類别:
- 導航頁
- 功能頁
- 内容頁
首頁和清單頁都是典型的導航頁;
站内搜尋頁面、系統資料庫單頁面和購物車頁面都是典型的功能頁,
而産品詳情頁、新聞和文章頁都是典型的内容頁。
比如從内容導航分析中,以下兩類行為就是網站營運者不希望看到的行為:
第一個問題:通路者從導航頁進入,在還沒有看到内容頁面之前就從導航頁離開網站,需要分析導航頁造成通路者中途離開的原因。
第二個問題:通路者從導航頁進入内容頁後,又傳回到導航頁,說明需要分析内容頁的最初設計,并考慮中内容頁提供交叉的資訊推薦
4)網站轉化及漏鬥分析
所謂轉化,即網站業務流程中的一個封閉管道,引導使用者按照流程最終實作業務目标(比如商品成交);而漏鬥模型則是指進入管道的使用者在各環節遞進過程中逐漸流失的形象描述;
對于轉化管道,主要進行兩部分的分析:
通路者的流失和迷失
- 阻力和流失
造成流失的原因很多,如:
不恰當的商品或活動推薦
對支付環節中專業名詞的解釋、幫助資訊等内容不當
- 迷失
造成迷失的主要原因是轉化流量設計不合理,通路者在特定階段得不到需要的資訊,并且不能根據現有的資訊作出決策
總之,網站流量分析是一門内容非常豐富的學科,本課程中主要關注網站分析過程中的技術運用,更多關于網站流量分析的業務知識可學習推薦資料。
1.3.2 流量分析常見名額
課程中涉及的分析名額主要位于以下幾大方面:
1)基礎分析(PV,IP,UV)
- 趨勢分析:根據標明的時段,提供網站流量資料,通過流量趨勢變化形态,為您分析網站訪客的通路規律、網站發展狀況提供參考。
- 對比分析:根據標明的兩個對比時段,提供網站流量在時間上的縱向對比報表,幫您發現網站發展狀況、發展規律、流量變化率等。
- 目前線上:提供目前時刻站點上的訪客量,以及最近15分鐘流量、來源、受訪、訪客變化情況等,友善使用者及時了解目前網站流量狀況。
- 通路明細:提供最近7日的訪客通路記錄,可按每個PV或每次通路行為(訪客的每次會話)顯示,并可按照來源、搜尋詞等條件進行篩選。 通過通路明細,使用者可以詳細了解網站流量的累計過程,進而為使用者快速找出流量變動原因提供最原始、最準确的依據。
2)來源分析
- 來源分類:提供不同來源形式(直接輸入、搜尋引擎、其他外部連結、站内來源)、不同來源項引入流量的比例情況。通過精确的量化資料,幫助使用者分析什麼類型的來路産生的流量多、效果好,進而合理優化推廣方案。
- 搜尋引擎:提供各搜尋引擎以及搜尋引擎子産品引入流量的比例情況。從搜尋引擎引入流量的的角度,幫助使用者了解網站的SEO、SEM效果,進而為制定下一步SEO、SEM計劃提供依據。
- 搜尋詞:提供訪客通過搜尋引擎進入網站所使用的搜尋詞,以及各搜尋詞引入流量的特征和分布。幫助使用者了解各搜尋詞引入流量的品質,進而了解訪客的興趣關注點、網站與訪客興趣點的比對度,為優化SEO方案及SEM提詞方案提供詳細依據。
- 最近7日的訪客搜尋記錄,可按每個PV或每次通路行為(訪客的每次會話)顯示,并可按照訪客類型、地區等條件進行篩選。為您搜尋引擎優化提供最詳細的原始資料。
- 來路域名:提供具體來路域名引入流量的分布情況,并可按“社會化媒體”、“搜尋引擎”、“郵箱”等網站類型對來源域名進行分類。 幫助使用者了解哪類推廣管道産生的流量多、效果好,進而合理優化網站推廣方案。
- 來路頁面:提供具體來路頁面引入流量的分布情況。 尤其對于通過流量置換、包廣告位等方式從其他網站引入流量的使用者,該功能可以友善、清晰地展現廣告引入的流量及效果,為優化推廣方案提供依據。
- 來源升降榜:提供開通統計後任意兩日的TOP10000搜尋詞、來路域名引入流量的對比情況,并按照變化的劇烈程度提供排行榜。 使用者可通過此功能快速找到哪些來路對網站流量的影響比較大,進而及時排查相應來路問題。
3)受訪分析
- 受訪域名:提供訪客對網站中各個域名的通路情況。 一般情況下,網站不同域名提供的産品、内容各有差異,通過此功能使用者可以了解不同内容的受歡迎程度以及網站營運成效。
- 受訪頁面:提供訪客對網站中各個頁面的通路情況。 站内入口頁面為訪客進入網站時浏覽的第一個頁面,如果入口頁面的跳出率較高則需要關注并優化;站内出口頁面為訪客通路網站的最後一個頁面,對于離開率較高的頁面需要關注并優化。
- 受訪升降榜:提供開通統計後任意兩日的TOP10000受訪頁面的浏覽情況對比,并按照變化的劇烈程度提供排行榜。 可通過此功能驗證經過改版的頁面是否有流量提升或哪些頁面有巨大流量波動,進而及時排查相應問題。
- 熱點圖:記錄訪客在頁面上的滑鼠點選行為,通過顔色區分不同區域的點選熱度;支援将一組頁面設定為"關注範圍",并可按來路細分點選熱度。 通過訪客在頁面上的點選量統計,可以了解頁面設計是否合理、廣告位的安排能否擷取更多傭金等。
- 使用者視點:提供受訪頁面對頁面上連結的其他站内頁面的輸出流量,并通過輸出流量的高低繪制熱度圖,與熱點圖不同的是,所有記錄都是實際打開了下一頁面産生了浏覽次數(PV)的資料,而不僅僅是擁有滑鼠點選行為。
- 通路軌迹:提供觀察焦點頁面的上下遊頁面,了解訪客從哪些途徑進入頁面,又流向了哪裡。 通過上遊頁面清單比較出不同流量引入管道的效果;通過下遊頁面清單了解使用者的浏覽習慣,哪些頁面元素、内容更吸引訪客點選。
4)訪客分析
- 地區營運商:提供各地區訪客、各網絡營運商訪客的通路情況分布。 地方網站、下載下傳站等與地域性、網絡鍊路等結合較為緊密的網站,可以參考此功能資料,合理優化推廣營運方案。
- 終端詳情:提供網站訪客所使用的浏覽終端的配置情況。 參考此資料進行網頁設計、開發,可更好地提高網站相容性,以達到良好的使用者互動體驗。
- 新老訪客:當日訪客中,曆史上第一次通路該網站的訪客記為當日新訪客;曆史上已經通路過該網站的訪客記為老訪客。 新訪客與老訪客進入網站的途徑和浏覽行為往往存在差異。該功能可以輔助分析不同訪客的行為習慣,針對不同訪客優化網站,例如為制作新手導航提供資料支援等。
- 忠誠度:從訪客一天内回訪網站的次數(日通路頻度)與訪客上次通路網站的時間兩個角度,分析訪客對網站的通路粘性、忠誠度、吸引程度。 由于提升網站内容的更新頻率、增強使用者體驗與使用者價值可以有更高的忠誠度,是以該功能在網站内容更新及使用者體驗方面提供了重要參考。
- 活躍度:從訪客單次通路浏覽網站的時間與網頁數兩個角度,分析訪客在網站上的活躍程度。 由于提升網站内容的品質與數量可以獲得更高的活躍度,是以該功能是網站内容分析的關鍵名額之一。
5)轉化路徑分析
轉化定義
·訪客在您的網站完成了某項您期望的活動,記為一次轉化,如注冊或下載下傳。
目标示例
·獲得使用者目标:線上注冊、建立賬号等。
·咨詢目标:咨詢、留言、電話等。
·互動目标:視訊播放、加入購物車、分享等。
·收入目标:線上訂單、付款等。
轉化資料的應用
·在報告的自定義名額中勾選轉化名額,實時掌握網站的推廣及營運情況。
·結合“全部來源”、“轉化路徑”、“頁面上下遊”等報告分析通路漏鬥,提高轉化率。
·對“轉化目标”設定價值,預估轉化收益,衡量ROI。
路徑分析:根據設定的特定路線,監測某一流程的完成轉化情況,算出每步的轉換率和流失率資料,如注冊流程,購買流程等。
轉化率存在一個漏鬥模型:
轉化類型:
- 頁面
- 事件
2 整體技術流程及架構
2.1 資料處理流程
該項目是一個純粹的資料分析項目,其整體流程基本上就是依據資料的處理流程進行,依此有以下幾個大的步驟:
- 資料采集
首先,通過頁面嵌入JS代碼的方式擷取使用者通路行為,并發送到web服務的背景記錄日志
然後,将各伺服器上生成的點選流日志通過實時或批量的方式彙聚到HDFS檔案系統中
當然,一個綜合分析系統,資料源可能不僅包含點選流資料,還有資料庫中的業務資料(如使用者資訊、商品資訊、訂單資訊等)及對分析有益的外部資料。
- 資料預處理
通過mapreduce程式對采集到的點選流資料進行預處理,比如清洗,格式整理,濾除髒資料等
- 資料入庫
将預處理之後的資料導入到HIVE倉庫中相應的庫和表中
- 資料分析
項目的核心内容,即根據需求開發ETL分析語句,得出各種統計結果
- 資料展現
将分析所得資料進行可視化
2.2 項目結構
由于本項目是一個純粹資料分析項目,其整體結構亦跟分析流程比對,并沒有特别複雜的結構,如下圖:
其中,需要強調的是:
系統的資料分析不是一次性的,而是按照一定的時間頻率反複計算,因而整個處理鍊條中的各個環節需要按照一定的先後依賴關系緊密銜接,即涉及到大量任務單元的管理排程,是以,項目中需要添加一個任務排程子產品
2.3 資料展現
資料展現的目的是将分析所得的資料進行可視化,以便營運決策人員能更友善地擷取資料,更快更簡單地了解資料
3 子產品開發——資料采集
3.1 需求
資料采集的需求廣義上來說分為兩大部分。
1)是在頁面采集使用者的通路行為,具體開發工作:
- 開發頁面埋點js,采集使用者通路行為
- 背景接受頁面js請求記錄日志
此部分工作也可以歸屬為“資料源”,其開發工作通常由web開發團隊負責
2)是從web伺服器上彙聚日志到HDFS,是資料分析系統的資料采集,此部分工作由資料分析平台建設團隊負責,具體的技術實作有很多方式:
- Shell腳本
優點:輕量級,開發簡單
缺點:對日志采集過程中的容錯處理不便控制
- Java采集程式
優點:可對采集過程實作精細控制
缺點:開發工作量大
- Flume日志采集架構
成熟的開源日志采集系統,且本身就是hadoop生态體系中的一員,與hadoop體系中的各種架構元件具有天生的親和力,可擴充性強
3.2 技術選型
在點選流日志分析這種場景中,對資料采集部分的可靠性、容錯能力要求通常不會非常嚴苛,是以使用通用的flume日志采集架構完全可以滿足需求。
本項目即使用flume來實作日志采集。
3.3 Flume日志采集系統搭建
- 資料源資訊
本項目分析的資料用nginx伺服器所生成的流量日志,存放在各台nginx伺服器上,如:
/var/log/httpd/access_log.2015-11-10-13-00.log
/var/log/httpd/access_log.2015-11-10-14-00.log
/var/log/httpd/access_log.2015-11-10-15-00.log
/var/log/httpd/access_log.2015-11-10-16-00.log
- 資料内容樣例
資料的具體内容在采集階段其實不用太關心。
58.215.204.118 - - [18/Sep/2013:06:51:35 +0000] "GET /wp-includes/js/jquery/jquery.js?ver=1.10.2 HTTP/1.1" 304 0 "http://blog.fens.me/nodejs-socketio-chat/" "Mozilla/5.0 (Windows NT 5.1; rv:23.0) Gecko/20100101 Firefox/23.0" |
字段解析:
1、訪客ip位址: 58.215.204.118
2、訪客使用者資訊: - -
3、請求時間:[18/Sep/2013:06:51:35 +0000]
4、請求方式:GET
5、請求的url:/wp-includes/js/jquery/jquery.js?ver=1.10.2
6、請求所用協定:HTTP/1.1
7、響應碼:304
8、傳回的資料流量:0
9、訪客的來源url:http://blog.fens.me/nodejs-socketio-chat/
10、訪客所用浏覽器:Mozilla/5.0 (Windows NT 5.1; rv:23.0) Gecko/20100101 Firefox/23.0
- 日志檔案生成規律
基本規律為:
目前正在寫的檔案為access_log;
檔案體積達到64M,或時間間隔達到60分鐘,即滾動重命名切換成曆史日志檔案;
形如: access_log.2015-11-10-13-00.log
當然,每個公司的web伺服器日志政策不同,可在web程式的log4j.properties中定義,如下:
log4j.appender.logDailyFile = org.apache.log4j.DailyRollingFileAppender log4j.appender.logDailyFile.layout = org.apache.log4j.PatternLayout log4j.appender.logDailyFile.layout.ConversionPattern = [%-5p][%-22d{yyyy/MM/dd HH:mm:ssS}][%l]%n%m%n log4j.appender.logDailyFile.Threshold = DEBUG log4j.appender.logDailyFile.ImmediateFlush = TRUE log4j.appender.logDailyFile.Append = TRUE log4j.appender.logDailyFile.File = /var/logs/access_log log4j.appender.logDailyFile.DatePattern = '.'yyyy-MM-dd-HH-mm'.log' log4j.appender.logDailyFile.Encoding = UTF-8 |
- Flume采集實作
Flume采集系統的搭建相對簡單:
- 在個web伺服器上部署agent節點,修改配置檔案
- 啟動agent節點,将采集到的資料彙聚到指定的HDFS目錄中
如下圖:
- 版本選擇:apache-flume-1.6.0
- 采集規則設計:
- 采集源:nginx伺服器日志目錄
- 存放地:hdfs目錄/home/hadoop/weblogs/
- 采集規則配置詳情
agent1.sources = source1 agent1.sinks = sink1 agent1.channels = channel1 # Describe/configure spooldir source1 #agent1.sources.source1.type = spooldir #agent1.sources.source1.spoolDir = /var/logs/nginx/ #agent1.sources.source1.fileHeader = false # Describe/configure tail -F source1 #使用exec作為資料源source元件 agent1.sources.source1.type = exec #使用tail -F指令實時收集新産生的日志資料 agent1.sources.source1.command = tail -F /var/logs/nginx/access_log agent1.sources.source1.channels = channel1 #configure host for source #配置一個攔截器插件 agent1.sources.source1.interceptors = i1 agent1.sources.source1.interceptors.i1.type = host #使用攔截器插件擷取agent所在伺服器的主機名 agent1.sources.source1.interceptors.i1.hostHeader = hostname #配置sink元件為hdfs agent1.sinks.sink1.type = hdfs #a1.sinks.k1.channel = c1 #agent1.sinks.sink1.hdfs.path=hdfs://hdp-node-01:9000/weblog/flume-collection/%y-%m-%d/%H%M%S #指定檔案sink到hdfs上的路徑 agent1.sinks.sink1.hdfs.path= hdfs://hdp-node-01:9000/weblog/flume-collection/%y-%m-%d/%H-%M_%hostname #指定檔案名字首 agent1.sinks.sink1.hdfs.filePrefix = access_log agent1.sinks.sink1.hdfs.maxOpenFiles = 5000 #指定每批下沉資料的記錄條數 agent1.sinks.sink1.hdfs.batchSize= 100 agent1.sinks.sink1.hdfs.fileType = DataStream agent1.sinks.sink1.hdfs.writeFormat =Text #指定下沉檔案按1G大小滾動 agent1.sinks.sink1.hdfs.rollSize = 1024*1024*1024 #指定下沉檔案按1000000條數滾動 agent1.sinks.sink1.hdfs.rollCount = 1000000 #指定下沉檔案按30分鐘滾動 agent1.sinks.sink1.hdfs.rollInterval = 30 #agent1.sinks.sink1.hdfs.round = true #agent1.sinks.sink1.hdfs.roundValue = 10 #agent1.sinks.sink1.hdfs.roundUnit = minute agent1.sinks.sink1.hdfs.useLocalTimeStamp = true # Use a channel which buffers events in memory #使用memory類型channel agent1.channels.channel1.type = memory agent1.channels.channel1.keep-alive = 120 agent1.channels.channel1.capacity = 500000 agent1.channels.channel1.transactionCapacity = 600 # Bind the source and sink to the channel agent1.sources.source1.channels = channel1 agent1.sinks.sink1.channel = channel1 |
啟動采集:
在部署了flume的nginx伺服器上,啟動flume的agent,指令如下:
bin/flume-ng agent --conf ./conf -f ./conf/weblog.properties.2 -n agent
注意:啟動指令中的 -n 參數要給配置檔案中配置的agent名稱
4 子產品開發——資料預處理
4.1 主要目的:
過濾“不合規”資料
格式轉換和規整
根據後續的統計需求,過濾分離出各種不同主題的基礎資料。
要分析的日志内容為:
4.2 實作方式:
開發一個mr程式WeblogPreProcess(内容太長,見工程代碼)
貼源表:對接外部資料的層,表結構定義與外部資料源一緻,主要是友善上層表的資料結構一緻,友善後期統計與資料管理。
日志的解析為:
- 運作mr對資料進行預處理
hadoop jar weblog.jar cn.itcast.bigdata.hive.mr.WeblogPreProcess /weblog/input /weblog/preout |
4.3 點選流模型資料梳理
由于大量的名額統計從點選流模型中更容易得出,是以在預處理階段,可以使用mr程式來生成點選流模型的資料
4.3.1 點選流模型pageviews表
點選流模型的設計mr為:
梳理visit模型:
運作Pageviews對資料進行處理:
hadoop jar weblogpreprocess.jar \ cn.itcast.bigdata.hive.mr.ClickStreamThree \ /user/hive/warehouse/dw_click.db/test_ods_weblog_origin/datestr=2013-09-20/ /test-click/pageviews/ |
表結構:
(表定義及資料導入見6.2節)
4.3.2 點選流模型visit資訊表
注:“一次通路”=“N次連續請求”
直接從原始資料中用hql文法得出每個人的“次”通路資訊比較困難,可先用mapreduce程式分析原始資料得出“次”資訊資料,然後再用hql進行更多元度統計
用MR程式從pageviews資料中,梳理出每一次visit的起止時間、頁面資訊
代碼見工程 hadoop jar weblogpreprocess.jar cn.itcast.bigdata.hive.mr.ClickStreamVisit /weblog/sessionout /weblog/visitout |
然後,在hive倉庫中建點選流visit模型表
drop table if exist click_stream_visit; create table click_stream_visit( session string, remote_addr string, inTime string, outTime string, inPage string, outPage string, referal string, pageVisits int) partitioned by (datestr string); |
然後,将MR運算得到的visit資料導入visit模型表
load data inpath '/weblog/visitout' into table click_stream_visit partition(datestr='2013-09-18'); |
5 子產品開發——資料倉庫設計
注:采用星型模型:中間一個事實表,四周為次元表;
(雪花模型:中間是一個事實表,四周是主要的鍵表,再往外是次元表,這樣的三段樣式為雪花模型)
5.1 事實表
原始資料表:t_origin_weblog | ||
valid | string | 是否有效 |
remote_addr | string | 訪客ip |
remote_user | string | 訪客使用者資訊 |
time_local | string | 請求時間 |
request | string | 請求url |
status | string | 響應碼 |
body_bytes_sent | string | 響應位元組數 |
http_referer | string | 來源url |
http_user_agent | string | 訪客終端資訊 |
t_etl_referurl: | ||
ETL中間表:t_etl_referurl | ||
valid | string | 是否有效 |
remote_addr | string | 訪客ip |
remote_user | string | 訪客使用者資訊 |
time_local | string | 請求時間 |
request | string | 請求url |
request_host | string | 請求的域名 |
status | string | 響應碼 |
body_bytes_sent | string | 響應位元組數 |
http_referer | string | 來源url |
http_user_agent | string | 訪客終端資訊 |
valid | string | 是否有效 |
remote_addr | string | 訪客ip |
remote_user | string | 訪客使用者資訊 |
time_local | string | 請求時間 |
request | string | 請求url |
status | string | 響應碼 |
body_bytes_sent | string | 響應位元組數 |
http_referer | string | 外鍊url |
http_user_agent | string | 訪客終端資訊 |
host | string | 外鍊url的域名 |
path | string | 外鍊url的路徑 |
query | string | 外鍊url的參數 |
query_id | string | 外鍊url的參數值 |
t_ods_access_detail: | ||
通路日志明細寬表:t_ods_access_detail | ||
valid | string | 是否有效 |
remote_addr | string | 訪客ip |
remote_user | string | 訪客使用者資訊 |
time_local | string | 請求時間 |
request | string | 請求url整串 |
request_level1 | string | 請求的一級欄目 |
request_level2 | string | 請求的二級欄目 |
request_level3 | string | 請求的三級欄目 |
status | string | 響應碼 |
body_bytes_sent | string | 響應位元組數 |
http_referer | string | 來源url |
http_user_agent | string | 訪客終端資訊 |
valid | string | 是否有效 |
remote_addr | string | 訪客ip |
remote_user | string | 訪客使用者資訊 |
time_local | string | 請求時間 |
request | string | 請求url |
status | string | 響應碼 |
body_bytes_sent | string | 響應位元組數 |
http_referer | string | 外鍊url |
http_user_agent | string | 訪客終端資訊整串 |
http_user_agent_browser | string | 訪客終端浏覽器 |
http_user_agent_sys | string | 訪客終端作業系統 |
http_user_agent_dev | string | 訪客終端裝置 |
host | string | 外鍊url的域名 |
path | string | 外鍊url的路徑 |
query | string | 外鍊url的參數 |
query_id | string | 外鍊url的參數值 |
daystr | string | 日期整串 |
tmstr | string | 時間整串 |
month | string | 月份 |
day | string | 日 |
hour | string | 時 |
minute | string | 分 |
## | ## | ## |
mm | string | 分區字段--月 |
dd | string | 分區字段--日 |
5.2 次元表
時間次元 v_year_month_date |
year |
month |
day |
hour |
minute |
訪客地域次元t_dim_area |
北京 |
上海 |
廣州 |
深圳 |
河北 |
河南 |
終端類型次元t_dim_termination |
uc |
firefox |
chrome |
safari |
ios |
android |
網站欄目次元 t_dim_section |
跳蚤市場 |
房租資訊 |
休閑娛樂 |
建材裝修 |
本地服務 |
人才市場 |
6 子產品開發——ETL
該項目的資料分析過程在hadoop叢集上實作,主要應用hive資料倉庫工具,是以,采集并經過預處理後的資料,需要加載到hive資料倉庫中,以進行後續的挖掘分析。
6.1建立原始資料表
在hive倉庫中建貼源資料表
drop table if exists ods_weblog_origin; create table ods_weblog_origin( valid string, remote_addr string, remote_user string, time_local string, request string, status string, body_bytes_sent string, http_referer string, http_user_agent string) partitioned by (datestr string) row format delimited fields terminated by '\001'; |
點選流模型pageviews表:
drop table if exists ods_click_pageviews; create table ods_click_pageviews( Session string, remote_addr string, time_local string, request string, visit_step string, page_staylong string, http_referer string, http_user_agent string, body_bytes_sent string, status string) partitioned by (datestr string) row format delimited fields terminated by '\001'; |
時間維表建立:
drop table dim_time if exists ods_click_pageviews; create table dim_time( year string, month string, day string, hour string) row format delimited fields terminated by ', |
6.2導入資料
導入清洗結果資料到貼源資料表ods_weblog_origin:每天有資料導入到腳本,是以需要寫一個腳本運作:load_ods_table.sh load data inpath '/weblog/preprocessed/16-02-24-16/' overwrite into table ods_weblog_origin partition(datestr='2013-09-18'); 0: jdbc:hive2://localhost:10000> show partitions ods_weblog_origin; +-------------------+--+ | partition | +-------------------+--+ | timestr=20151203 | +-------------------+--+ 0: jdbc:hive2://localhost:10000> select count(*) from ods_origin_weblog; +--------+--+ | _c0 | +--------+--+ | 11347 | +--------+--+ 導入點選流模型pageviews資料到ods_click_pageviews表: 0: jdbc:hive2://hdp-node-01:10000> load data inpath '/weblog/clickstream/pageviews' overwrite into table ods_click_pageviews partition(datestr='2013-09-18'); 0: jdbc:hive2://hdp-node-01:10000> select count(1) from ods_click_pageviews; +------+--+ | _c0 | +------+--+ | 66 | +------+--+ 導入點選流模型visit資料到ods_click_visit表 導入時間維表: load data inpath '/dim_time.txt' into table dim_time; |
6.3 生成ODS層明細寬表
6.3.1 需求概述
整個資料分析的過程是按照資料倉庫的層次分層進行的,總體來說,是從ODS原始資料中整理出一些中間表(比如,為後續分析友善,将原始資料中的時間、url等非結構化資料作結構化抽取,将各種字段資訊進行細化,形成明細表),然後再在中間表的基礎之上統計出各種名額資料。
6.3.2 ETL實作
建表——明細表 (源:ods_weblog_origin) (目标:ods_weblog_detail)
drop table ods_weblog_detail; create table ods_weblog_detail( valid string, --有效辨別 remote_addr string, --來源IP remote_user string, --使用者辨別 time_local string, --通路完整時間 daystr string, --通路日期 timestr string, --通路時間 month string, --通路月 day string, --通路日 hour string, --通路時 request string, --請求的url status string, --響應碼 body_bytes_sent string, --傳輸位元組數 http_referer string, --來源url ref_host string, --來源的host ref_path string, --來源的路徑 ref_query string, --來源參數query ref_query_id string, --來源參數query的值 http_user_agent string --客戶終端辨別 ) partitioned by (datestr string); |
http_referer:
http://www.baidu.com/aapath?sousuoci=’angel’
parse_url_tuple(url,’HOST’,’PATH’,’QUERY’,’QUERY:id’)
--抽取refer_url到中間表 "t_ods_tmp_referurl"
--将來訪url分離出host path query query id
在對來訪資料進行抽取時,需要寫一個函數,可以使用:Parse_url_tuple()
drop table if exists t_ods_tmp_referurl; create table t_ ods _tmp_referurl as SELECT a.*,b.* FROM ods_origin_weblog a LATERAL VIEW parse_url_tuple(regexp_replace(http_referer, "\"", ""), 'HOST', 'PATH','QUERY', 'QUERY:id') b as host, path, query, query_id; |
--抽取轉換time_local字段到中間表明細表 ”t_ ods _detail”
drop table if exists t_ods_tmp_detail; create table t_ods_tmp_detail as select b.*,substring(time_local,0,10) as daystr, substring(time_local,11) as tmstr, substring(time_local,5,2) as month, substring(time_local,8,2) as day, substring(time_local,11,2) as hour From t_ ods _tmp_referurl b; |
以上語句可以改寫成:
insert into table zs.ods_weblog_detail partition(datestr='$day_01') select c.valid,c.remote_addr,c.remote_user,c.time_local, substring(c.time_local,0,10) as daystr, substring(c.time_local,12) as tmstr, substring(c.time_local,6,2) as month, substring(c.time_local,9,2) as day, substring(c.time_local,11,3) as hour, c.request,c.status,c.body_bytes_sent,c.http_referer,c.ref_host,c.ref_path,c.ref_query,c.ref_query_id,c.http_user_agent from (SELECT a.valid,a.remote_addr,a.remote_user,a.time_local, a.request,a.status,a.body_bytes_sent,a.http_referer,a.http_user_agent,b.ref_host,b.ref_path,b.ref_query,b.ref_query_id FROM zs.ods_weblog_origin a LATERAL VIEW parse_url_tuple(regexp_replace(http_referer, "\"", ""), 'HOST', 'PATH','QUERY', 'QUERY:id') b as ref_host, ref_path, ref_query, ref_query_id) c " 0: jdbc:hive2://localhost:10000> show partitions ods_weblog_detail; +---------------------+--+ | partition | +---------------------+--+ | dd=18%2FSep%2F2013 | +---------------------+--+ 1 row selected (0.134 seconds) |
7 子產品開發——統計分析
注:每一種統計名額都可以跟各次元表進行叉乘,進而得出各個次元的統計結果
篇幅限制,叉乘的代碼及注釋資訊詳見項目工程代碼檔案
為了在前端展示時速度更快,每一個名額都事先算出各次元結果存入mysql
提前準備好維表資料,在hive倉庫中建立相應維表,如:
時間維表:
create table v_time(year string,month string,day string,hour string) row format delimited fields terminated by ','; load data local inpath '/home/hadoop/v_time.txt' into table v_time; |
在實際生産中,究竟需要哪些統計名額通常由相關資料需求部門人員提出,而且會不斷有新的統計需求産生,以下為網站流量分析中的一些典型名額示例。
- PV統計
1.1 多元度統計PV總量
1. 時間次元
--計算指定的某個小時pvs select count(*),month,day,hour from dw_click.ods_weblog_detail group by month,day,hour; --計算該處理批次(一天)中的各小時pvs drop table dw_pvs_hour; create table dw_pvs_hour(month string,day string,hour string,pvs bigint) partitioned by(datestr string); insert into table dw_pvs_hour partition(datestr='2016-03-18') select a.month as month,a.day as day,a.hour as hour,count(1) as pvs from ods_weblog_detail a where a.datestr='2016-03-18' group by a.month,a.day,a.hour; 或者用時間維表關聯 |
次元:日
drop table dw_pvs_day; create table dw_pvs_day(pvs bigint,month string,day string); insert into table dw_pvs_day select count(1) as pvs,a.month as month,a.day as day from dim_time a join ods_weblog_detail b on b.dd='18/Sep/2013' and a.month=b.month and a.day=b.day group by a.month,a.day; --或者,從之前算好的小時結果中統計 Insert into table dw_pvs_day Select sum(pvs) as pvs,month,day from dw_pvs_hour group by month,day having day='18'; 結果如下: |
次元:月
drop table t_display_pv_month; create table t_display_pv_month (pvs bigint,month string); insert into table t_display_pv_month select count(*) as pvs,a.month from t_dim_time a join t_ods_detail_prt b on a.month=b.month group by a.month; |
2. 按終端次元統計pv總量
注:探索資料中的終端類型
select distinct(http_user_agent) from ods_weblog_detail where http_user_agent like '%Mozilla%' limit 200; |
終端次元:uc
drop table t_display_pv_terminal_uc; create table t_display_pv_ terminal_uc (pvs bigint,mm string,dd string,hh string); |
終端次元:chrome
drop table t_display_pv_terminal_chrome; create table t_display_pv_ terminal_ chrome (pvs bigint,mm string,dd string,hh string); |
終端次元:safari
drop table t_display_pv_terminal_safari; create table t_display_pv_ terminal_ safari (pvs bigint,mm string,dd string,hh string); |
3. 按欄目次元統計pv總量
欄目次元:job
欄目次元:news
欄目次元:bargin
欄目次元:lane
1.2 人均浏覽頁數
需求描述:比如,今日所有來訪者,平均請求的頁面數
--總頁面請求數/去重總人數
drop table dw_avgpv_user_d; create table dw_avgpv_user_d( day string, avgpv string); insert into table dw_avgpv_user_d select '2013-09-18',sum(b.pvs)/count(b.remote_addr) from (select remote_addr,count(1) as pvs from ods_weblog_detail where datestr='2013-09-18' group by remote_addr) b; |
1.3 按referer次元統計pv總量
需求:按照來源及時間次元統計PVS,并按照PV大小倒序排序
-- 按照小時粒度統計,查詢結果存入:( "dw_pvs_referer_h" )
drop table dw_pvs_referer_h; create table dw_pvs_referer_h(referer_url string,referer_host string,month string,day string,hour string,pv_referer_cnt bigint) partitioned by(datestr string); insert into table dw_pvs_referer_h partition(datestr='2016-03-18') select http_referer,ref_host,month,day,hour,count(1) as pv_referer_cnt from ods_weblog_detail group by http_referer,ref_host,month,day,hour having ref_host is not null order by hour asc,day asc,month asc,pv_referer_cnt desc; |
按天粒度統計各來訪域名的通路次數并排序
drop table dw_ref_host_visit_cnts_h; create table dw_ref_host_visit_cnts_h(ref_host string,month string,day string,hour string,ref_host_cnts bigint) partitioned by(datestr string); insert into table dw_ref_host_visit_cnts_h partition(datestr='2016-03-18') select ref_host,month,day,hour,count(1) as ref_host_cnts from ods_weblog_detail group by ref_host,month,day,hour having ref_host is not null order by hour asc,day asc,month asc,ref_host_cnts desc; |
注:還可以按來源地域次元、訪客終端次元等計算
1.4 統計pv總量最大的來源TOPN
需求描述:按照時間次元,比如,統計一天内産生最多pvs的來源topN
需要用到row_number函數
以下語句對每個小時内的來訪host次數倒序排序标号,
select ref_host,ref_host_cnts,concat(month,hour,day),
row_number() over (partition by concat(month,hour,day) order by ref_host_cnts desc) as od
from dw_ref_host_visit_cnts_h
效果如下:
根據上述row_number的功能,可編寫Hql取各小時的ref_host通路次數topn
drop table dw_pvs_refhost_topn_h; create table dw_pvs_refhost_topn_h( hour string, toporder string, ref_host string, ref_host_cnts string ) partitioned by(datestr string); insert into table zs.dw_pvs_refhost_topn_h partition(datestr='2016-03-18') select t.hour,t.od,t.ref_host,t.ref_host_cnts from (select ref_host,ref_host_cnts,concat(month,day,hour) as hour, row_number() over (partition by concat(month,day,hour) order by ref_host_cnts desc) as od from zs.dw_ref_host_visit_cnts_h) t where od<=3; |
結果如下:
注:還可以按來源地域次元、訪客終端次元等計算
- 受訪分析
統計每日最熱門的頁面top10
drop table dw_pvs_d; create table dw_pvs_d(day string,url string,pvs string); insert into table dw_pvs_d select '2013-09-18',a.request,a.request_counts from (select request as request,count(request) as request_counts from ods_weblog_detail where datestr='2013-09-18' group by request having request is not null) a order by a.request_counts desc limit 10; 結果如下: |
注:還可繼續得出各次元交叉結果
- 訪客分析
3.1 獨立訪客
需求描述:按照時間次元比如小時來統計獨立訪客及其産生的pvCnts
對于獨立訪客的識别,如果在原始日志中有使用者辨別,則根據使用者辨別即很好實作;
此處,由于原始日志中并沒有使用者辨別,以訪客IP來模拟,技術上是一樣的,隻是精确度相對較低
時間次元:時
drop table dw_user_dstc_ip_h; create table dw_user_dstc_ip_h( remote_addr string, pvs bigint, hour string); insert into table dw_user_dstc_ip_h select remote_addr,count(1) as pvs,concat(month,day,hour) as hour from ods_weblog_detail Where datestr='2013-09-18' group by concat(month,day,hour),remote_addr; |
在此結果表之上,可以進一步統計出,每小時獨立訪客總數,每小時請求次數topn訪客等
如每小時獨立訪客總數:
select count(1) as dstc_ip_cnts,hour from dw_user_dstc_ip_h group by hour; |
練習: 統計每小時請求次數topn的獨立訪客 |
時間次元:月
select remote_addr,count(1) as counts,month from ods_weblog_detail group by month,remote_addr; |
時間次元:日
select remote_addr,count(1) as counts,concat(month,day) as day from ods_weblog_detail Where dd='18/Sep/2013' group by concat(month,day),remote_addr; |
注:還可以按來源地域次元、訪客終端次元等計算
3.2 每日新訪客
需求描述:将每天的新訪客統計出來
實作思路:建立一個去重訪客累積表,然後将每日訪客對比累積表
時間次元:日
--曆日去重訪客累積表 drop table dw_user_dsct_history; create table dw_user_dsct_history( day string, ip string ) partitioned by(datestr string); --每日新使用者追加到累計表 drop table dw_user_dsct_history; create table dw_user_dsct_history( day string, ip string ) partitioned by(datestr string); --每日新使用者追加到累計表 insert into table dw_user_dsct_history partition(datestr='2013-09-19') select tmp.day as day,tmp.today_addr as new_ip from ( select today.day as day,today.remote_addr as today_addr,old.ip as old_addr from (select distinct remote_addr as remote_addr,"2013-09-19" as day from ods_weblog_detail where datestr="2013-09-19") today left outer join dw_user_dsct_history old on today.remote_addr=old.ip ) tmp where tmp.old_addr is null; |
驗證:
select count(distinct remote_addr) from ods_weblog_detail; -- 1005 select count(1) from dw_user_dsct_history where prtflag_day='18/Sep/2013'; --845 select count(1) from dw_user_dsct_history where prtflag_day='19/Sep/2013'; --160 |
時間次元:月
類似日粒度算法 |
注:還可以按來源地域次元、訪客終端次元等計算
- Visit分析(點選流模型)
4.2 回頭/單次訪客統計
需求描述:查詢今日所有回頭訪客及其通路次數
實作思路:上表中出現次數>1的訪客,即回頭訪客;反之,則為單次訪客
drop table dw_user_returning; create table dw_user_returning( day string, remote_addr string, acc_cnt string) partitioned by (datestr string); insert overwrite table dw_user_returning partition(datestr='2013-09-18') select tmp.day,tmp.remote_addr,tmp.acc_cnt from (select '2013-09-18' as day,remote_addr,count(session) as acc_cnt from click_stream_visit group by remote_addr) tmp where tmp.acc_cnt>1; |
4.3 人均通路頻次
需求:統計出每天所有使用者通路網站的平均次數(visit)
總visit數/去重總使用者數
select sum(pagevisits)/count(distinct remote_addr) from click_stream_visit partition(datestr='2013-09-18'); |
5. Visit分析另一種實作方式
5.1 mr程式識别出訪客的每次通路
a.) 首先開發MAPREDUCE程式:UserStayTime
注:代碼略長,見項目工程代碼 |
b.) 送出MAPREDUCE程式進行運算
[[email protected] ~]$ hadoop jar weblog.jar cn.itcast.bigdata.hive.mr.UserStayTime /weblog/input /weblog/stayout4 --導入hive表("t_display_access_info")中 drop table ods_access_info; create table ods_access_info(remote_addr string,firt_req_time string,last_req_time string,stay_long string) partitioned by(prtflag_day string) row format delimited fields terminated by '\t'; load data inpath '/weblog/stayout4' into table ods_access_info partition(prtflag_day='18/Sep/2013'); 建立表時stay_long使用的string類型,但是在後續過程中發現還是用bigint更好,進行表修改 alter table ods_access_info change column stay_long stay_long bigint; |
5.2 将mr結果導入訪客通路資訊表 "t_display_access_info"
由于有一些通路記錄是單條記錄,mr程式處理處的結果給的時長是0,是以考慮給單次請求的停留時間一個預設市場30秒
drop table dw_access_info; create table dw_access_info(remote_addr string,firt_req_time string,last_req_time string,stay_long string) partitioned by(prtflag_day string); insert into table dw_access_info partition(prtflag_day='19/Sep/2013') select remote_addr,firt_req_time,last_req_time, case stay_long when 0 then 30000 else stay_long end as stay_long from ods_access_info where prtflag_day='18/Sep/2013'; |
在通路資訊表的基礎之上,可以實作更多名額統計,如:
統計所有使用者停留時間平均值,觀察使用者在站點停留時長的變化走勢
select prtflag_day as dt,avg(stay_long) as avg_staylong
from dw_access_info group by prtflag_day;
5.3 回頭/單次訪客統計
注:從上一步驟得到的通路資訊統計表“dw_access_info”中查詢
--回頭訪客通路資訊表 "dw_access_info_htip"
drop table dw_access_info_htip; create table dw_access_info_htip(remote_addr string, firt_req_time string, last_req_time string, stay_long string,acc_counts string) partitioned by(prtflag_day string); insert into table dw_access_info_htip partition(prtflag_day='18/Sep/2013') select b.remote_addr,b.firt_req_time,b.last_req_time,b.stay_long,a.acc_counts from (select remote_addr,count(remote_addr) as acc_counts from dw_access_info where prtflag_day='18/Sep/2013' group by remote_addr having acc_counts>1) a join dw_access_info b on a.remote_addr = b.remote_addr; |
--單次訪客通路資訊表 "dw_access_info_dcip"
drop table dw_access_info_dcip; create table dw_access_info_dcip(remote_addr string, firt_req_time string, last_req_time string, stay_long string,acc_counts string) partitioned by(prtflag_day string); insert into table dw_access_dcip partition(prtflag_day='18/Sep/2013') select b.remote_addr,b.firt_req_time,b.last_req_time,b.stay_long,a.acc_counts from (select remote_addr,count(remote_addr) as acc_counts from dw_access_info where prtflag_day='18/Sep/2013' group by remote_addr having acc_counts<2) a join dw_access_info b on a.remote_addr = b.remote_addr; |
在回頭/單詞訪客資訊表的基礎之上,可以實作更多統計名額,如:
--當日回頭客占比
drop table dw_htpercent_d; create table dw_htpercent_d(day string,ht_percent float); Insert into table dw_htpercent_d select '18/Sep/2013',(tmp_ht.ht/tmp_all.amount)*100 from (select count( distinct a.remote_addr) as ht from dw_access_info_htip a where prtflag_day='18/Sep/2013') tmp_ht Join (select count(distinct b.remote_addr) as amount from dw_access_info b where prtflag_day='18/Sep/2013') tmp_all; |
5.4 人均通路頻度
--總通路次數/去重總人數,從訪客次數彙總表中查詢
select avg(user_times.counts) as user_access_freq from (select remote_addr,counts from t_display_htip union all select remote_addr,counts from t_display_access_dcip) user_times; --或直接從 通路資訊表 t_display_access_info 中查詢 select avg(a.acc_cts) from (select remote_addr,count(*) as acc_cts from dw_access_info group by remote_addr) a; |
- 關鍵路徑轉化率分析——漏鬥模型
轉化:在一條指定的業務流程中,各個步驟的完成人數及相對上一個步驟的百分比
6.1 需求分析
6.2 模型設計
定義好業務流程中的頁面辨別,下例中的步驟為:
Step1、 /item%
Step2、 /category
Step3、 /order
Step4、 /index
6.3 開發實作
分步驟開發:
- 查詢每一個步驟的總通路人數
create table route_numbs as select 'step1' as step,count(distinct remote_addr) as numbs from ods_click_pageviews where request like '/item%' union select 'step2' as step,count(distinct remote_addr) as numbs from ods_click_pageviews where request like '/category%' union select 'step3' as step,count(distinct remote_addr) as numbs from ods_click_pageviews where request like '/order%' union select 'step4' as step,count(distinct remote_addr) as numbs from ods_click_pageviews where request like '/index%'; |
- 查詢每一步驟相對于路徑起點人數的比例
思路:利用join
select rn.step as rnstep,rn.numbs as rnnumbs,rr.step as rrstep,rr.numbs as rrnumbs from route_num rn inner join route_num rr |
select tmp.rnstep,tmp.rnnumbs/tmp.rrnumbs as ratio from ( select rn.step as rnstep,rn.numbs as rnnumbs,rr.step as rrstep,rr.numbs as rrnumbs from route_num rn inner join route_num rr) tmp where tmp.rrstep='step1'; |
- 查詢每一步驟相對于上一步驟的漏出率
select tmp.rrstep as rrstep,tmp.rrnumbs/tmp.rnnumbs as ration from ( select rn.step as rnstep,rn.numbs as rnnumbs,rr.step as rrstep,rr.numbs as rrnumbs from route_num rn inner join route_num rr) tmp where cast(substr(tmp.rnstep,5,1) as int)=cast(substr(tmp.rrstep,5,1) as int)-1 |
- 彙總以上兩種名額
select abs.step,abs.numbs,abs.ratio as abs_ratio,rel.ratio as rel_ratio from ( select tmp.rnstep as step,tmp.rnnumbs as numbs,tmp.rnnumbs/tmp.rrnumbs as ratio from ( select rn.step as rnstep,rn.numbs as rnnumbs,rr.step as rrstep,rr.numbs as rrnumbs from route_num rn inner join route_num rr) tmp where tmp.rrstep='step1' ) abs left outer join ( select tmp.rrstep as step,tmp.rrnumbs/tmp.rnnumbs as ratio from ( select rn.step as rnstep,rn.numbs as rnnumbs,rr.step as rrstep,rr.numbs as rrnumbs from route_num rn inner join route_num rr) tmp where cast(substr(tmp.rnstep,5,1) as int)=cast(substr(tmp.rrstep,5,1) as int)-1 ) rel on abs.step=rel.step |
8 子產品開發——結果導出
報表統計結果,由sqoop從hive表中導出,示例如下,詳見工程代碼
sqoop export \ --connect jdbc:mysql://hdp-node-01:3306/webdb --username root --password root \ --table click_stream_visit \ --export-dir /user/hive/warehouse/dw_click.db/click_stream_visit/datestr=2013-09-18 \ --input-fields-terminated-by '\001' |
9 子產品開發——工作流排程
注:将整個項目的資料處理過程,從資料采集到資料分析,再到結果資料的導出,一系列的任務分割成若幹個oozie的工作流,并用coordinator進行協調
工作流定義示例
Ooize配置片段示例,詳見項目工程
1、日志預處理mr程式工作流定義
<workflow-app name="weblogpreprocess" xmlns="uri:oozie:workflow:0.4"> <start to="firstjob"/> <action name="firstjob"> <map-reduce> <job-tracker>${jobTracker}</job-tracker> <name-node>${nameNode}</name-node> <prepare> <delete path="${nameNode}/${outpath}"/> </prepare> <configuration> <property> <name>mapreduce.job.map.class</name> <value>cn.itcast.bigdata.hive.mr.WeblogPreProcess$WeblogPreProcessMapper</value> </property> <property> <name>mapreduce.job.output.key.class</name> <value>org.apache.hadoop.io.Text</value> </property> <property> <name>mapreduce.job.output.value.class</name> <value>org.apache.hadoop.io.NullWritable</value> </property> <property> <name>mapreduce.input.fileinputformat.inputdir</name> <value>${inpath}</value> </property> <property> <name>mapreduce.output.fileoutputformat.outputdir</name> <value>${outpath}</value> </property> <property> <name>mapred.mapper.new-api</name> <value>true</value> </property> <property> <name>mapred.reducer.new-api</name> <value>true</value> </property> </configuration> </map-reduce> <ok to="end"/> <error to="kill"/> |
2、資料加載etl工作流定義:
<workflow-app xmlns="uri:oozie:workflow:0.5" name="hive2-wf"> <start to="hive2-node"/> <action name="hive2-node"> <hive2 xmlns="uri:oozie:hive2-action:0.1"> <job-tracker>${jobTracker}</job-tracker> <name-node>${nameNode}</name-node> <configuration> <property> <name>mapred.job.queue.name</name> <value>${queueName}</value> </property> </configuration> <jdbc-url>jdbc:hive2://hdp-node-01:10000</jdbc-url> <script>script.q</script> <param>input=/weblog/outpre2</param> </hive2> <ok to="end"/> <error to="fail"/> </action> <kill name="fail"> <message>Hive2 (Beeline) action failed, error message[${wf:errorMessage(wf:lastErrorNode())}]</message> </kill> <end name="end"/> </workflow-app> |
3、資料加載工作流所用hive腳本:
create database if not exists dw_weblog; use dw_weblog; drop table if exists t_orgin_weblog; create table t_orgin_weblog(valid string,remote_addr string, remote_user string, time_local string, request string, status string, body_bytes_sent string, http_referer string, http_user_agent string) row format delimited fields terminated by '\001'; load data inpath '/weblog/preout' overwrite into table t_orgin_weblog; drop table if exists t_ods_detail_tmp_referurl; create table t_ods_detail_tmp_referurl as SELECT a.*,b.* FROM t_orgin_weblog a LATERAL VIEW parse_url_tuple(regexp_replace(http_referer, "\"", ""), 'HOST', 'PATH','QUERY', 'QUERY:id') b as host, path, query, query_id; drop table if exists t_ods_detail; create table t_ods_detail as select b.*,substring(time_local,0,11) as daystr, substring(time_local,13) as tmstr, substring(time_local,4,3) as month, substring(time_local,0,2) as day, substring(time_local,13,2) as hour from t_ods_detail_tmp_referurl b; drop table t_ods_detail_prt; create table t_ods_detail_prt( valid string, remote_addr string, remote_user string, time_local string, request string, status string, body_bytes_sent string, http_referer string, http_user_agent string, host string, path string, query string, query_id string, daystr string, tmstr string, month string, day string, hour string) partitioned by (mm string,dd string); insert into table t_ods_detail_prt partition(mm='Sep',dd='18') select * from t_ods_detail where daystr='18/Sep/2013'; insert into table t_ods_detail_prt partition(mm='Sep',dd='19') select * from t_ods_detail where daystr='19/Sep/2013'; |
更多工作流及hql腳本定義詳見項目工程
工作流單元測試
1、工作流定義配置上傳
[[email protected] wf-oozie]$ hadoop fs -put hive2-etl /user/hadoop/oozie/myapps/ [[email protected] wf-oozie]$ hadoop fs -put hive2-dw /user/hadoop/oozie/myapps/ [[email protected] wf-oozie]$ ll total 12 drwxrwxr-x. 2 hadoop hadoop 4096 Nov 23 16:32 hive2-dw drwxrwxr-x. 2 hadoop hadoop 4096 Nov 23 16:32 hive2-etl drwxrwxr-x. 3 hadoop hadoop 4096 Nov 23 11:24 weblog [[email protected] wf-oozie]$ export OOZIE_URL=http://localhost:11000/oozie |
2、工作流單元送出啟動
oozie job -D inpath=/weblog/input -D outpath=/weblog/outpre -config weblog/job.properties -run
啟動etl的hive工作流
oozie job -config hive2-etl/job.properties -run
啟動pvs統計的hive工作流
oozie job -config hive2-dw/job.properties -run
3、工作流coordinator配置(片段)
多個工作流job用coordinator組織協調:
[[email protected] hive2-etl]$ ll total 28 -rw-rw-r--. 1 hadoop hadoop 265 Nov 13 16:39 config-default.xml -rw-rw-r--. 1 hadoop hadoop 512 Nov 26 16:43 coordinator.xml -rw-rw-r--. 1 hadoop hadoop 382 Nov 26 16:49 job.properties drwxrwxr-x. 2 hadoop hadoop 4096 Nov 27 11:26 lib -rw-rw-r--. 1 hadoop hadoop 1910 Nov 23 17:49 script.q -rw-rw-r--. 1 hadoop hadoop 687 Nov 23 16:32 workflow.xml |
- config-default.xml
<configuration> <property> <name>jobTracker</name> <value>hdp-node-01:8032</value> </property> <property> <name>nameNode</name> <value>hdfs://hdp-node-01:9000</value> </property> <property> <name>queueName</name> <value>default</value> </property> </configuration> |
- job.properties
user.name=hadoop oozie.use.system.libpath=true oozie.libpath=hdfs://hdp-node-01:9000/user/hadoop/share/lib oozie.wf.application.path=hdfs://hdp-node-01:9000/user/hadoop/oozie/myapps/hive2-etl/ |
- workflow.xml
<workflow-app xmlns="uri:oozie:workflow:0.5" name="hive2-wf"> <start to="hive2-node"/> <action name="hive2-node"> <hive2 xmlns="uri:oozie:hive2-action:0.1"> <job-tracker>${jobTracker}</job-tracker> <name-node>${nameNode}</name-node> <configuration> <property> <name>mapred.job.queue.name</name> <value>${queueName}</value> </property> </configuration> <jdbc-url>jdbc:hive2://hdp-node-01:10000</jdbc-url> <script>script.q</script> <param>input=/weblog/outpre2</param> </hive2> <ok to="end"/> <error to="fail"/> </action> <kill name="fail"> <message>Hive2 (Beeline) action failed, error message[${wf:errorMessage(wf:lastErrorNode())}]</message> </kill> <end name="end"/> </workflow-app> |
- coordinator.xml
<coordinator-app name="cron-coord" frequency="${coord:minutes(5)}" start="${start}" end="${end}" timezone="Asia/Shanghai" xmlns="uri:oozie:coordinator:0.2"> <action> <workflow> <app-path>${workflowAppUri}</app-path> <configuration> <property> <name>jobTracker</name> <value>${jobTracker}</value> </property> <property> <name>nameNode</name> <value>${nameNode}</value> </property> <property> <name>queueName</name> <value>${queueName}</value> </property> </configuration> </workflow> </action> </coordinator-app> |
10 子產品開發——資料展示
在企業的資料分析系統中,前端展現工具有很多,代碼使用常見的springmvc的項目,此處僅做介紹,有興趣的可以自己寫一個項目,僅作展示使用。
- 獨立部署專門系統的方式:以Business Objects(BO,Crystal Report),Heperion(Brio),Cognos等國外産品為代表的,它們的伺服器是單獨部署的,與應用程式之間通過某種協定溝通資訊
- 有WEB程式展現方式:通過獨立的或者嵌入式的java web系統來讀取報表統計結果,以網頁的形式對結果進行展現,如,100%純Java的潤乾報表
本日志分析項目采用自己開發web程式展現的方式:
- Web展現程式采用的技術架構:
Jquery + Echarts + springmvc + spring + mybatis + mysql
- 展現的流程:
- 使用ssh從mysql中讀取要展現的資料
- 使用json格式将讀取到的資料傳回給頁面
- 在頁面上用echarts對json解析并形成圖示
Web程式工程結構
采用maven管理工程,引入SSH架構依賴及jquery+echarts的js庫
Web程式的實作代碼
采用典型的MVC架構實作
頁面 | HTML + JQUERY + ECHARTS |
Controller | SpringMVC |
Service | Service |
DAO | Mybatis |
資料庫 | Mysql |
代碼示例:ChartServiceImpl
@Service("chartService") public class ChartServiceImpl implements IChartService { @Autowired IEchartsDao iEchartsDao; public EchartsData getChartsData() { List<Integer> xAxiesList = iEchartsDao.getXAxiesList(""); List<Integer> pointsDataList = iEchartsDao.getPointsDataList(""); EchartsData data = new EchartsData(); ToolBox toolBox = EchartsOptionUtil.getToolBox(); Serie serie = EchartsOptionUtil.getSerie(pointsDataList); ArrayList<Serie> series = new ArrayList<Serie>(); series.add(serie); List<XAxi> xAxis = EchartsOptionUtil.getXAxis(xAxiesList); List<YAxi> yAxis = EchartsOptionUtil.getYAxis(); HashMap<String, String> title = new HashMap<String, String>(); title.put("text", "pvs"); title.put("subtext", "超級pvs"); HashMap<String, String> tooltip = new HashMap<String, String>(); tooltip.put("trigger", "axis"); HashMap<String, String[]> legend = new HashMap<String, String[]>(); legend.put("data", new String[]{"pv統計"}); data.setTitle(title); data.setTooltip(tooltip); data.setLegend(legend); data.setToolbox(toolBox); data.setCalculable(true); data.setxAxis(xAxis); data.setyAxis(yAxis); data.setSeries(series); return data;} public List<HashMap<String, Integer>> getGaiKuangList(String date) throws ParseException{ HashMap<String, Integer> gaiKuangToday = iEchartsDao.getGaiKuang(date); SimpleDateFormat sf = new SimpleDateFormat("MMdd"); Date parse = sf.parse(date); Calendar calendar = Calendar.getInstance(); calendar.setTime(parse); calendar.add(Calendar.DAY_OF_MONTH, -1); Date before = calendar.getTime(); String beforeString = sf.format(before); System.out.println(beforeString); HashMap<String, Integer> gaiKuangBefore = iEchartsDao.getGaiKuang(beforeString); ArrayList<HashMap<String, Integer>> gaiKuangList = new ArrayList<HashMap<String, Integer>>(); gaiKuangList.add(gaiKuangToday); gaiKuangList.add(gaiKuangBefore); return gaiKuangList; } public static void main(String[] args) { ChartServiceImpl chartServiceImpl = new ChartServiceImpl(); EchartsData chartsData = chartServiceImpl.getChartsData(); Gson gson = new Gson(); String json = gson.toJson(chartsData); System.out.println(json); } } |