1-Python - 网站访问日志分析作业
需求
Python3.6.8
站长之家的正则在线测试
基本需求
- 统计日志文件的总pv、uv
- 列出全天每小时的pv、uv
- 列出top10 uv的IP地址,以及每个IP的pv点击数
- 列出top10访问量最多的页面及每个页面的访问量
- 列出访问来源设备列表及每个设备的访问量
说明
- pv:page visit,页面访问量,一次请求就是一次pv
- uv:uservisit,独立用户,一个IP就算一个独立的用户
注意:没有IP的日志在这里认为是异常日志,不在统计范围之内。
日志文件附件
百度云盘链接:https://pan.baidu.com/s/1IFrl1eDjCg8FP8eS86TfOA 提取码:0ada
需求分析
首先要了解日志的意思:
# 正常日志
27.10.109.31 - - [15/Apr/2019:00:45:23 +0800] "GET /api/v1/enroll/degrees/ HTTP/1.1" 200 270 "https://www.luffycity.com/study/degree" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.0.3 Safari/605.1.15"
# 访问IP:27.10.109.31
# 访问时间:15/Apr/2019:00:45:23 +0800
# 访问的URL:/api/v1/enroll/degrees/
# 访问设备:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.0.3 Safari/605.1.15
# 异常日志
ee/2/ HTTP/1.1" 200 1055 "https://www.luffycity.com/study/degree" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.75 Safari/537.36"
- - - [15/Apr/2019:00:32:54 +0800] "\x16\x03\x01\x00\x9A\x01\x00\x00\x96\x03\x03g\xAE%\xCF\xCA\xC8v\x191\x90\xAA\xAD9\xBC\xFE\x9AJ]\xFC\xB8\xB4\x83\xF6\xF7\xB3+\xA3<AG\xF3\xAE\x00\x00\x1A\xC0/\xC0+\xC0\x11\xC0\x07\xC0\x13\xC0\x09\xC0\x14\xC0" 400 166 "-" "-"
- - - [15/Apr/2019:01:05:45 +0800] "HEAD / HTTP/1.1" 499 0 "-" "-"
首先要把异常日志排除掉。
然后再针对性实现每个需求。
- 统计日志文件的总pv、uv
-- 循环每条正常的日志,使用正则查找当前行的 ip
- pv = 每一个可以重复的 ip 的和
- uv = 每一个不可重复的 ip 的和
- 列出全天每小时的pv、uv
-- 循环每条正常的日志,使用正则查找当前行的 ip
-- 以小时为分割,然后统计每个小时的pv和uv
- pv = 每一个可以重复的 ip 的和
- uv = 每一个不可重复的 ip 的和
-- 难点:
- 从日志中读取的日期时间,如何处理为可用的时间格式(格式化后的字符串/时间戳)
- 如何组织数据结构
- 列出top10 uv的IP地址,以及每个IP的pv点击数
-- 循环每条正常的日志,使用正则查找当前行的 ip
- 循环读取所有ip,取出每一个ip
- 统计每个不重复的ip,以及统计该ip重复出现的次数
- 最后进行降序排序,然后取最高的top10
- 列出top10访问量最多的页面及每个页面的访问量
-- 循环每条正常的日志,使用正则查找当前行的 page
- 循环读取所有page,取出每一个page
- 统计每个不重复的page,以及统计该page重复出现的次数
- 最后进行降序排序,然后取最高的top10 的page
- 列出访问来源设备列表及每个设备的访问量
-- 循环每条正常的日志,使用正则查找当前行的设备
- 统计每个不重复的设备,以及统计该设备重复出现的次数
- 虽然需求是列出所有的设备,但我的代码中只展示了前20条数据
-- 注意,设备有多种:
- Mozilla
- Mozilla/5.0 Mozilla/4.0 Mozilla/3.0
- Python-urllib/2.7
- Go-http-client/1.1
- - # 是的,只有一个 - ,我认为是未知的设备,我代码中没有处理
- curl/7.19.7
- Sogou web
- Xiaomi_MCT1_TD-LTE/V1
示例代码:
import datetime
import re
from prettytable import PrettyTable
LOG_INFO = './网站访问日志.log'
# ----- 正则规则 -----
MATCH_STARTSWITH = MATCH_IP = re.compile(
"(25[0-5]|2[0-4]\d|[0-1]\d{2}|[1-9]?\d)\.(25[0-5]|2[0-4]\d|[0-1]\d{2}|[1-9]?\d)\.(25[0-5]|2[0-4]\d|[0-1]\d{2}|[1-9]?\d)\.(25[0-5]|2[0-4]\d|[0-1]\d{2}|[1-9]?\d)")
MATCH_DATETIME = re.compile(
"(?P<ip>((25[0-5]|2[0-4]\d|[0-1]\d{2}|[1-9]?\d)\.(25[0-5]|2[0-4]\d|[0-1]\d{2}|[1-9]?\d)\.(25[0-5]|2[0-4]\d|[0-1]\d{2}|[1-9]?\d)\.(25[0-5]|2[0-4]\d|[0-1]\d{2}|[1-9]?\d))).*?\[(?P<dt>.*?)\s\+0800\]")
MATCH_EQUIPMENT = re.compile("\"\s\"(?P<equipment>.*?)\"")
MATCH_PAGE = re.compile("\"[A-Z]{3,}\s(?P<page>.*?)\sHTTP/1.1")
FILE_LIST = []
def init():
""" 过滤符合条件的日志,存储在列表中 """
with open(LOG_INFO, 'r', encoding='UTF-8') as f:
for line in f:
res = MATCH_STARTSWITH.match(line)
if res:
FILE_LIST.append(line)
def total_pv_uv():
""" 统计本日志文件的总pv、uv """
tmp_dict = {"pv": [], "uv": set(), }
print('waiting.....')
for i in FILE_LIST:
res = MATCH_IP.search(i)
if res:
res = res.group()
tmp_dict['pv'].append(res)
tmp_dict['uv'].add(res)
table = PrettyTable(['总PV', '总UV'])
table.add_row([len(tmp_dict['pv']), len(tmp_dict['uv'])])
print(table)
def total_24hour_pv_uv():
""" 列出全天每小时的pv、uv数 """
tmp_dict = {}
print("waiting.....")
for i in FILE_LIST:
res = MATCH_DATETIME.search(i)
if res:
ip, dt = res.group("ip"), res.group("dt")
date_time_str = datetime.datetime.strptime(dt, '%d/%b/%Y:%H:%M:%S')
date_time_day = date_time_str.strftime("%Y-%m-%d")
date_time_hour = date_time_str.strftime("%Y-%m-%d %H")
if not tmp_dict.get(date_time_day, False):
tmp_dict[date_time_day] = {}
# 创建当天的24小时
for hour in range(0, 24):
# 必须用zfill在个位数之前填充0,不然生成的 key 是这样的 2019-04-14 1,和 date_time_hour生成的key 2019-04-14 01 不一致,会出问题
tmp_key = "{} ".format(date_time_day) + "{}".format(hour).zfill(2)
if tmp_key not in tmp_dict[date_time_day]:
tmp_dict[date_time_day][tmp_key] = {"pv": [], "uv": set()}
tmp_dict[date_time_day][date_time_hour]['uv'].add(ip)
tmp_dict[date_time_day][date_time_hour]['pv'].append(ip)
# print(tmp_dict)
for k in tmp_dict:
print('[{}]日的每小时的pv和uv数统计如下: '.format(k))
table = PrettyTable(["时间", '每个小时的pv数', '每个小时的uv数'])
for j, v in tmp_dict[k].items():
table.add_row([j, len(v['pv']), len(v['uv'])])
print(table)
def total_top10_uv():
""" 列出top 10 uv的IP地址,以及每个ip的pv点击数 """
tmp_dict = {}
print("waiting.....")
for i in FILE_LIST:
res = MATCH_IP.search(i)
if res:
res = res.group()
if res in tmp_dict:
tmp_dict[res] += 1
else:
tmp_dict[res] = 1
table = PrettyTable(['top10 uv的IP', 'top10 uv的ip的pv'])
table.align['top10 uv的IP'] = 'l'
tmp_list = sorted(tmp_dict.items(), key=lambda x: x[1], reverse=True)[
0:10] # tmp_dict.items() --> ("113.89.97.191", 13)
for n1, n2 in tmp_list:
table.add_row([n1, n2])
print(table)
def total_top10_page():
""" 列出top 10 访问量最多的页面及每个页面的访问量 """
print("waiting.....")
tmp_dict = {}
for i in FILE_LIST:
res = MATCH_PAGE.search(i)
if res:
page = res.group("page")
if page in tmp_dict:
tmp_dict[page].append(page)
else:
tmp_dict[page] = [page]
table = PrettyTable(['访问量是top10的页面URL', '访问量'])
table.align['访问量是top10的页面URL'] = 'l'
tmp_list = sorted(tmp_dict.items(), key=lambda x: len(x[1]), reverse=True)[0:10]
for n1, n2 in tmp_list:
table.add_row([n1, len(n2)])
print(table)
def total_equipment_list():
""" 列出访问来源的设备列表及每个设备的访问量 """
print("waiting.....")
tmp_dict = {}
for i in FILE_LIST:
res = MATCH_EQUIPMENT.search(i)
if res:
equipment = res.group("equipment")
if equipment in tmp_dict:
tmp_dict[equipment].append(equipment)
else:
tmp_dict[equipment] = [equipment]
table = PrettyTable(['设备来源', '访问量'])
table.align['设备来源'] = 'l'
# 这里仅展示前20条,你可以取消分片,展示所有
# tmp_list = sorted(tmp_dict.items(), key=lambda x: len(x[1]), reverse=True)
tmp_list = sorted(tmp_dict.items(), key=lambda x: len(x[1]), reverse=True)[0:20]
for n1, n2 in tmp_list:
table.add_row([n1, len(n2)])
print(table)
def q():
""" 退出 """
exit('再来呦')
def handler():
tmp_dict = {
"1": ["统计本日志文件的总pv、uv", total_pv_uv],
"2": ["列出全天每小时的pv、uv数", total_24hour_pv_uv],
"3": ["列出top 10 uv的IP地址,以及每个ip的pv点击数", total_top10_uv],
"4": ["列出top 10 访问量最多的页面及每个页面的访问量", total_top10_page],
"5": ["列出访问来源的设备列表及每个设备的访问量", total_equipment_list],
"6": ["退出", q]
}
while True:
print('欢迎使用网站访问数据分析系统'.center(40, '*'))
for k, v in tmp_dict.items():
print(k, v[0])
cmd = input("输入序号选择对应的操作: ").strip()
if cmd in tmp_dict:
tmp_dict[cmd][-1]()
else:
print('输入不合法')
if __name__ == "__main__":
init()
handler()
演示结果,注意,由于原日志文件的内容差异、代码逻辑不同、正则规则不同,大家的结果可能存在误差。
*************欢迎使用网站访问数据分析系统*************
1 统计本日志文件的总pv、uv
2 列出全天每小时的pv、uv数
3 列出top 10 uv的IP地址,以及每个ip的pv点击数
4 列出top 10 访问量最多的页面及每个页面的访问量
5 列出访问来源的设备列表及每个设备的访问量
6 退出
输入序号选择对应的操作: 1
waiting.....
+-------+------+
| 总PV | 总UV |
+-------+------+
| 31288 | 1683 |
+-------+------+
*************欢迎使用网站访问数据分析系统*************
1 统计本日志文件的总pv、uv
2 列出全天每小时的pv、uv数
3 列出top 10 uv的IP地址,以及每个ip的pv点击数
4 列出top 10 访问量最多的页面及每个页面的访问量
5 列出访问来源的设备列表及每个设备的访问量
6 退出
输入序号选择对应的操作: 2
waiting.....
[2019-04-15]日的每小时的pv和uv数统计如下:
+---------------+----------------+----------------+
| 时间 | 每个小时的pv数 | 每个小时的uv数 |
+---------------+----------------+----------------+
| 2019-04-15 00 | 397 | 49 |
| 2019-04-15 01 | 102 | 23 |
| 2019-04-15 02 | 38 | 10 |
| 2019-04-15 03 | 48 | 16 |
| 2019-04-15 04 | 37 | 15 |
| 2019-04-15 05 | 17 | 10 |
| 2019-04-15 06 | 180 | 14 |
| 2019-04-15 07 | 305 | 39 |
| 2019-04-15 08 | 978 | 109 |
| 2019-04-15 09 | 2329 | 170 |
| 2019-04-15 10 | 2317 | 202 |
| 2019-04-15 11 | 2111 | 163 |
| 2019-04-15 12 | 1148 | 122 |
| 2019-04-15 13 | 1585 | 185 |
| 2019-04-15 14 | 2376 | 259 |
| 2019-04-15 15 | 2555 | 215 |
| 2019-04-15 16 | 2047 | 210 |
| 2019-04-15 17 | 2394 | 212 |
| 2019-04-15 18 | 1493 | 138 |
| 2019-04-15 19 | 1593 | 165 |
| 2019-04-15 20 | 2016 | 191 |
| 2019-04-15 21 | 2141 | 205 |
| 2019-04-15 22 | 1888 | 201 |
| 2019-04-15 23 | 1193 | 141 |
+---------------+----------------+----------------+
*************欢迎使用网站访问数据分析系统*************
1 统计本日志文件的总pv、uv
2 列出全天每小时的pv、uv数
3 列出top 10 uv的IP地址,以及每个ip的pv点击数
4 列出top 10 访问量最多的页面及每个页面的访问量
5 列出访问来源的设备列表及每个设备的访问量
6 退出
输入序号选择对应的操作: 3
waiting.....
+-----------------+------------------+
| top10 uv的IP | top10 uv的ip的pv |
+-----------------+------------------+
| 221.218.214.8 | 4018 |
| 122.71.67.110 | 855 |
| 118.113.14.162 | 357 |
| 47.95.112.89 | 299 |
| 113.246.241.131 | 244 |
| 117.25.109.180 | 219 |
| 106.44.6.54 | 209 |
| 116.18.244.11 | 203 |
| 58.45.45.183 | 198 |
| 60.247.104.68 | 195 |
+-----------------+------------------+
*************欢迎使用网站访问数据分析系统*************
1 统计本日志文件的总pv、uv
2 列出全天每小时的pv、uv数
3 列出top 10 uv的IP地址,以及每个ip的pv点击数
4 列出top 10 访问量最多的页面及每个页面的访问量
5 列出访问来源的设备列表及每个设备的访问量
6 退出
输入序号选择对应的操作: 4
waiting.....
+-------------------------------------------+--------+
| 访问量是top10的页面URL | 访问量 |
+-------------------------------------------+--------+
| /api/v1/enroll/degrees/ | 2047 |
| /api/v1/banners/ | 1088 |
| /api/v1/course_sub/category/list/ | 1050 |
| /api/v1/courses/?sub_category=0&ordering= | 1028 |
| /api/v1/enroll/info/?degree_id=1 | 651 |
| /api/v1/learndata/?degree_id=1 | 622 |
| /api/v1/account/login/ | 579 |
| /api/v1/enroll/degree/1/ | 511 |
| /api/v1/captcha_check/ | 436 |
| /mentor/ | 279 |
+-------------------------------------------+--------+
*************欢迎使用网站访问数据分析系统*************
1 统计本日志文件的总pv、uv
2 列出全天每小时的pv、uv数
3 列出top 10 uv的IP地址,以及每个ip的pv点击数
4 列出top 10 访问量最多的页面及每个页面的访问量
5 列出访问来源的设备列表及每个设备的访问量
6 退出
输入序号选择对应的操作: 5
waiting.....
+-----------------------------------------------------------------------------------------------------------------------------------------+--------+
| 设备来源 | 访问量 |
+-----------------------------------------------------------------------------------------------------------------------------------------+--------+
| Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36 | 2934 |
| Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:66.0) Gecko/20100101 Firefox/66.0 | 1268 |
| Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36 | 1085 |
| Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36 | 1048 |
| Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36 | 1008 |
| Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.75 Safari/537.36 | 974 |
| Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36 | 930 |
| Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.119 Safari/537.36 | 894 |
| Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Safari/537.36 Edge/17.17134 | 735 |
| Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36 | 677 |
| Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:66.0) Gecko/20100101 Firefox/66.0 | 434 |
| Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36 | 425 |
| Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36 | 414 |
| Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36 | 406 |
| Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Safari/537.36 Edge/18.17763 | 381 |
| Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36 | 336 |
| Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36 | 331 |
| Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36 | 328 |
| Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36 | 327 |
| Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.1 Mobile/15E148 Safari/604.1 | 326 |
+-----------------------------------------------------------------------------------------------------------------------------------------+--------+
*************欢迎使用网站访问数据分析系统*************
1 统计本日志文件的总pv、uv
2 列出全天每小时的pv、uv数
3 列出top 10 uv的IP地址,以及每个ip的pv点击数
4 列出top 10 访问量最多的页面及每个页面的访问量
5 列出访问来源的设备列表及每个设备的访问量
6 退出
输入序号选择对应的操作: 6
再来呦
that's all