在企業中,從日志中提取數據進行分析,可以幫助企業更加了解用戶行為,用戶最感興趣的產品或者內容,分析得到數據后,可以決定企業在今后的走向。
從這些日志數據中,比較重要的有:
1. 用戶訪問最多的url,即用戶在企業網站最感興趣的產品或者內容
2. 用戶群體的的主要線路是什么?移動?聯通?電信?
3. 用戶訪問的高峰期是什么時候?最高PV(訪問量)、UV(獨立訪客)、IP(獨立IP)。
4. 各時段狀態碼數。比如304,表示靜態資源在沒有發生改變時,服務器要求客戶使用了瀏覽器本地的緩存,可以降低服務器流量負載等。403、404如果異常得出現很多,則要根據訪問得url來判斷是否有惡意用戶在對網站目錄進行掃描和探測。400、500等狀態碼很多的情況就需要運維人員及時分析並排查原因。
5. 客戶瀏覽器的名稱、版本。統計出各種瀏覽器的分布情況,比如:如果手機瀏覽器、IE 6.0版本瀏覽器訪問記錄很多,則大概可以判斷出用戶群體大概的操作系統是winXP,win7以上版本,或是手機訪問。那就需要考慮是否要對特定版本瀏覽器進行頁面優化,或者如果客戶是手機瀏覽器,那是否要壓縮網站頁面大小,降低流量消耗,亦或是否要對手機端優化,提升用戶體驗,牢牢得抓住客戶。。
這里涉及部分SEO方面知識,僅作了解即可,如果企業真正需要了,再深入學習。
下面的例子對訪問狀態碼和瀏覽器名稱、版本進行了統計,以引出日志分析、數據挖掘的重要性:
import datetime
import re
from queue import Queue
import threading
from pathlib import Path
from user_agents import parse
from collections import defaultdict
# 正則,文件讀取,時間窗口,隊列,多線程,高階函數,分發器,嵌套函數
logline = '''183.60.212.153 - - [19/Feb/2013:10:23:29 +0800] "GET /o2o/media.html?menu=3 HTTP/1.1" 200 16691 "-" "Mozilla/5.0 (compatible; EasouSpider; +http://www.easou.com/search/spider.html)"'''
pattern = '''(?P<remote_addr>[\d\.]{7,}) - - (?:\[(?P<datetime>[^\[\]]+)\]) "(?P<request>[^"]+)" (?P<status>\d+) (?P<size>\d+) "[^"]+" "(?P<useragent>[^"]+)"'''
# 數據源處理
ops = {
'datetime': lambda timestr: datetime.datetime.strptime(timestr, "%d/%b/%Y:%H:%M:%S %z"),
'request': lambda request: dict(zip(('method', 'url', 'protocol'), request.split())),
'status': int,
'size': int,
'useragent': lambda useragent: parse(useragent)
}
regex = re.compile(pattern)
def extract(line):
matcher = regex.match(line)
if matcher:
return {k: ops.get(k, lambda x: x)(v) for k, v in matcher.groupdict().items()}
def openfile(path:str):
with open(path) as f:
for line in f:
fields = extract(line)
if fields:
yield fields # return generator objects,next(load(path))
else:
# TODO 不合格數據有哪些
continue # TODO 解析失敗就拋棄,或者打印日志
def load(*paths):
'''裝載日志文件或路徑'''
for item in paths:
p = Path(item)
if not p.exists():
continue
if p.is_dir():
for file in p.iterdir():
if file.is_file():
yield from openfile(str(file))
elif p.is_file():
yield from openfile(str(p))
def window(src:Queue, handler, width: int, interval: int):
'''
窗口函數
:param src: 數據源,生成器,用來拿數據
:param handler: 數據處理函數
:param width: 時間窗口寬度,秒
:param interval: 處理時間間隔,秒/ 時間偏移量,秒
:return:
'''
start = datetime.datetime.strptime('1970/01/01 01:01:01 +0800', '%Y/%m/%d %H:%M:%S %z')
current = datetime.datetime.strptime('1970/01/01 01:01:02 +0800', '%Y/%m/%d %H:%M:%S %z')
delta = datetime.timedelta(seconds=width-interval)
buffer = [] #窗口里的待計算數據
while True: #while True方式迭代queue
# 從數據源獲取數據
data = src.get() # block阻塞的
if data:
buffer.append(data)
current = data['datetime']
if (current - start).total_seconds() >= interval:
ret = handler(buffer) # 如何處理
print("{}".format(ret))
start = current
buffer = [i for i in buffer if i['datetime'] > current - delta]
def donothing_handler(iterable:list):
# print(iterable)
return iterable
# 狀態碼時間段百分比分析
def status_handler(iterable:list):
d = {}
for item in iterable:
key = item['status']
if key not in d:
d[key] = 0
d[key] += 1
total= sum(d.values())
return {'{}: {:.2f}%'.format(k,v/total*100) for k,v in d.items()}
# 瀏覽器分析函數
ua_dict = defaultdict(lambda : 0) # 作用域改為全局之后,字典遞增保存所有ua及其版本
def browser_handler(iterable):
for item in iterable:
ua = item['useragent']
key = (ua.browser.family, ua.browser.version_string)
ua_dict[key] += 1
return ua_dict
# 分發器,嵌套函數
def dispatcher(src):
queues = [] # 隊列列表
threads = [] # 線程管理
def reg(handler, width, interval):
q = Queue() # 分配隊列
queues.append(q) # 方便調用
t = threading.Thread(target=window,args=(q, handler, width, interval))
threads.append(t)
def run():
for t in threads:
t.start()
for x in src:
for q in queues:
q.put(x)
return reg,run
reg,run = dispatcher(load('test.log'))
# reg注冊 窗口
# reg(donothing_handler, 10, 5) #注冊測試
# reg(status_handler, 10, 5) # 注冊狀態碼處理函數
reg(browser_handler, 60, 60) # 注冊useragent處理函數,注意時間窗口寬度
run()
