#IT明星不是夢#利用Python進行網站日誌分析

網站的訪問日誌是一個很是重要的文件,經過分析訪問日誌,可以挖掘出不少有價值的信息。本文介紹如何利用Python對一個真實網站的訪問日誌進行分析,文中將綜合運用Python文件操做、字符串處理、列表、集合、字典等相關知識點。本文所用的訪問日誌access_log來自我我的的雲服務器,你們能夠從文末的附件中下載。php

1.提取指定日期的日誌

下面是一條典型的網站訪問日誌,客戶端訪問網站中的每一個資源都會產生一條日誌。web

193.112.9.107 - - [25/Jan/2020:06:32:58 +0800] "GET /robots.txt HTTP/1.1" 404 208 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:61.0) Gecko/20100101 Firefox/61.0"編程

每條日誌都由空格分隔爲九部分,其中比較重要的是:瀏覽器

  • 第1部分,193.112.9.107 ,客戶端的IP地址。
  • 第4部分,[25/Jan/2020:06:32:58 +0800],用戶訪問請求發生的時間。
  • 第5部分,GET /robots.txt HTTP/1.1,客戶端發來的HTTP請求報文首部的第一行信息。這部分採用「請求方法 請求資源 請求協議」的格式表示,是日誌中最重要的部分。「GET /robots.txt HTTP/1.1」表示客戶端以GET方法請求訪問服務器的/robots.txt文件,所使用的HTTP協議版本爲HTTP/1.1。
  • 第6部分, 「404」,HTTP響應狀態碼。狀態碼用於表示用戶的請求是否成功,若是該值爲200,則表示用戶的訪問成功,不然就可能存在問題。通常來講,以2開頭的狀態碼都可以表示用戶的訪問成功,以3開頭的狀態碼錶示用戶的請求被頁面從新定向到了其它位置,以4開頭的狀態碼錶示客戶端遇到了錯誤,以5開頭的狀態碼錶示服務器遇到了錯誤。
  • 第7部分,「208」,響應報文的大小,單位字節,這個數值不包括響應報文的首部。把日誌記錄中的這些值加起來就能夠得知服務器在必定時間內發送了多少數據。
  • 第9部分, "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:61.0) Gecko/20100101 Firefox/61.0",表示客戶端發來的HTTP請求報文中首部「User-Agent」的值,即發出請求的應用程序,一般都是瀏覽器。

一個日誌文件中會包含不少天的日誌記錄,而咱們一般都是針對某一天進行日誌分析,因此首先須要從日誌文件中把咱們要分析的那一天的日誌提取出來。
好比要提取1月25日產生的日誌,能夠執行下面的代碼:安全

>>> with open('access_log','r') as f1, open('access_log-0125','w') as f2:
...     for line in f1:
...             if '25/Jan/2020' in line:
...                     f2.write(line)

在這段代碼中,以r讀取模式打開日誌文件access_log,做爲文件對象f1。以w寫入模式建立文件access_log-0125,做爲文件對象f2。
而後遍歷f1中的每一行,並判斷是否包含關鍵字「25/Jan/2020」,若是有的話,就將這行寫入到f2中。
這樣就提取出了1月25日的全部日誌記錄,並保存到了文件access_log-0125中。下面咱們就針對文件access_log-0125進行分析。服務器

2.統計PV和UV

PV是指PageView,網站的訪問請求數。用戶每次對網站中的一個頁面的請求或訪問均被記錄爲1個PV,例如某個用戶訪問了網站中的4個頁面,那麼PV就+4。並且用戶對同一頁面的屢次訪問,PV也是累計的。
UV是指UniqueView,網站的獨立訪客數,訪問網站的一個IP被視爲一個訪客,在同一天內相同的IP只被計算一次。
於是,咱們只要取出每條日誌中的IP並統計數量,那麼就能夠獲得PV,將IP去重,就能夠獲得UV。
執行下面的代碼,將每條日誌的IP提取出來,並存放到列表ips中。app

>>> ips = []
>>> with open('access_log-0125','r') as f:
...     for line in f:
...             ips.append(line.split()[0])

在這段代碼中,首先定義了一個空列表ips,而後打開文件access_log-0125並對其進行遍歷,每遍歷一行,就將該行以空格做爲分隔符分割成一個列表,並取出列表中的第一個元素(也就是IP地址),再追加到列表ips中。
下面咱們只要統計列表ips的長度就是PV,將列表元素去重以後,再統計長度就是UV。去重這裏採用了set()函數,將列表轉換爲集合,利用Python集合自己的特性,簡單高效的完成去重操做。運維

>>> pv = len(ips)
>>> uv = len(set(ips))
>>> print(pv,uv)
1011 48

3.統計網站出錯頁面比例

網站的出錯比例是很重要的一份數據,直接關係到網站的用戶體驗。要統計用戶訪問出錯的比例,能夠經過統計每一個請求的HTTP狀態碼獲得,狀態碼爲2xx或3xx的,視爲訪問正確,狀態碼爲4xx或5xx,則視爲訪問出錯。
首先能夠提取全部頁面的狀態碼,並保存到列表中。ssh

>>> codes = []
>>> with open('access_log-0125','r') as f:
...     for line in f:
...             codes.append(line.split()[8])

再統計出每種狀態碼的出現次數,保存到字典中:ide

>>> ret = {}
>>> for i in codes:
...     if i not in ret:
...             ret[i] = codes.count(i)
... 
>>> 
>>> ret
{'200': 192, '404': 796, '"-"': 4, '400': 13, '403': 3, '401': 2, '405': 1}

上面這段代碼用到了字典,這裏是對存放狀態碼的列表codes進行遍歷,從中取出狀態碼做爲字典的鍵,並統計這種狀態碼在列表codes中出現的次數,做爲字典的值。
若是要統計404頁面的比例,能夠執行下面的代碼:

>>> ret['404']/sum(ret.values())
0.7873392680514342

在這段代碼中,ret['404']表示從字典ret中取出鍵爲‘404’的元素的值,也就是404狀態碼的個數。ret.values()表示取出字典中全部元素的值,再用sum()函數求和,獲得全部狀態碼的總數量。二者的比值也就是錯誤頁面的比例了。
從結果中能夠看出,我這個網站的頁面出錯比例特別高,居然達到了78.7%,若是是一個正常網站,這確定是有問題的。但我這並非一個公開網站,也沒有什麼有價值的頁面,於是大部分訪問日誌其實都是由一些漏洞掃描軟件產生的,這也提醒咱們,隨時都有人在對咱們線上的網站進行着各類掃描測試。

4.統計網站熱門資源

下面咱們繼續統計出每一個頁面的用戶訪問量,並進行排序。
首先仍然是遍歷日誌文件,取出用戶訪問的全部頁面,並保存到列表中:

>>> webs = []
>>> with open('access_log-0125','r') as f:
...     for line in f:
...             webs.append(line.split()[6])

接着再統計出每一個頁面的訪問次數,並存放到字典中:

>>> counts = {}
>>> for i in webs:
...     if i not in counts:
...             counts[i] = webs.count(i)
...

按頁面的訪問量降序排序:

>>> sorted(counts.items(),key=lambda x:x[1],reverse=True)
[('/', 175), ('/robots.txt', 25), ('/phpinfo.php', 6), ('/Admin13790d6a/Login.php', 4), 
……

爲了更好地理解上面這個sorted()函數的用法,下面舉例說明。好比咱們定義一個名叫services的字典,若是直接用sorted()函數對這個字典排序,默認是按照鍵進行升序排序。爲了顯示字典中的全部內容,可使用items()方法,此時,字典中的每一個鍵值對會被組合成一個元組,而且默認是按照元組中的第一個元素,也就是字典的鍵進行排序的。

>>> services = {'http':80,'ftp':21,'https':443,'ssh':22}
>>> sorted(services)
['ftp', 'http', 'https', 'ssh']
>>> sorted(services.items())
[('ftp', 21), ('http', 80), ('https', 443), ('ssh', 22)]

若是但願按照字典中的值進行排序,也就是要按照元組中的第二個元素排序,能夠用key參數指定一個lambda表達式,以每一個元組中的第二個元素做爲關鍵字。

>>> sorted(services.items(),key=lambda x:x[1])
[('ftp', 21), ('ssh', 22), ('http', 80), ('https', 443)]

因此這也就解釋了以前那個sorted()函數的含義。至於lambda表達式,其實就是一個根據須要能夠隨時定義使用的小函數,「lambda x:x[1]」,冒號左側的x是函數要處理的參數,冒號右側的表達式是函數要執行的操做,最後再將這個表達式的結果返回。

本文屬於「Python安全與運維」系列課程的一部分,該系列課程目前已更新到第二部,感興趣的朋友能夠參考:
第一部 Python基本語法 https://edu.51cto.com/sd/53aa7
第二部 Python編程基礎 https://edu.51cto.com/sd/d100c

相關文章
相關標籤/搜索