分析日志结构
# 正常日志
162.221.197.226 - - [05/May/2022:22:37:51 +0800] "GET /robots.txt HTTP/1.1" 200 127 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1985.125 Safari/537.36" "-"0.182 0.182 127.0.0.1:9000 2000.000 - blog.sirliu.com
# 访问IP:162.221.197.226
# 访问时间:[05/May/2022:22:37:51 +0800]
# 访问的URL: /robots.txt HTTP/1.1
# 访问设备:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1985.125 Safari/537.36
# 异常日志
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"
- - - [1/Dec/2021:11:53:42 +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 "-" "-"
- - - [1/Dec/2021:12:36:00 +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 = 'files/access.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()
写入到excel
import datetime
import re
from openpyxl import Workbook
LOG_INFO = 'files/access.log'
cmd = input("请输入分析的是什么终端日志:").strip()
# ----- 正则规则 -----
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\]")
FILE_LIST = []
def xlsx_file(files):
works = Workbook()
words = works.active
words.title = f"{cmd}.xlsx"
words.append(['时间', '每小时PV', '每小时UV'])
for exl in files:
words.append(exl)
works.save('excel/pc.xlsx')
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_24hour_pv_uv():
""" 列出全天每小时的pv、uv数 """
tmp_dict = {}
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):
tmp_key = "{} ".format(date_time_day) + "{}".format(hour).zfill(2)
if tmp_key not in tmp_dict[date_time_day]:
# 拼接字典结构,这里的set()是集合,应为集合中不能写入相同的数据
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)
for k in tmp_dict:
tt = []
for j, v in tmp_dict[k].items():
tt.append([j, len(v['pv']), len(v['uv'])])
xlsx_file(tt)
if __name__ == "__main__":
init()
total_24hour_pv_uv()
最新评论
# 这只是一个创建远程登录并授权的语句、仅作为记录 GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY 'Fit2cloud!' WITH GRANT OPTION;
当MGR集群初始化结束后,需要开启MGR集群自启动(需要有一台节点是自动开启引导) loose-group_replication_start_on_boot = ON #设置节点是否在启动时自动启动 MGR 集群 loose-group_replication_bootstrap_group = ON #设置节点是否作为初始引导节点启动集群
密码:blog.sirliu.com
本内容密码:blog.sirliu.com 最新整理的文章在这里喔:https://blog.sirliu.com/2018/11/shell_lian_xi_ti.html