Python - 棋牌游戏11点 Python - 疯狂练习题 Python - 网站访问日志分析作业
需求
作业需求:
1、生成一副扑克牌(自己设计扑克牌的结构,小王和大王可以分别用14、15表示 )
2、3个玩家(玩家也可以自己定义)
user_list = ["zhangkai","likai","wangkai"]
3、发牌规则
默认先给用户发一张牌,其中 J、Q、K、小王、大王代表的值为0.5,其他就是则就是当前的牌面值。
用户根据自己的情况判断是否继续要牌。
要,则再给他发一张。(可以一直要牌,但是如果自己手中的牌总和超过11点,你的牌就爆掉了(牌面变成0))
不要,则开始给下个玩家发牌。(没有牌则则牌面默认是0)
如果用户手中的所有牌相加大于11,则表示爆了,此人的分数为0,并且自动开始给下个人发牌。
4、最终计算并获得每个玩家的分值,例如:
result = {
"zhangkai":8,
"likai":9,
"wangkai":0
}
必备技术点:随机抽排
import random
total_poke_list = [("红桃", 1), ("黑桃", 2), ......,("大王", 15), ("小王", 14)]
# 随机生成一个数,当做索引。
index = random.randint(0, len(total_poke_list) - 1)
# 获取牌
print("抽到的牌为:", total_poke_list[index])
# 踢除这张牌
total_poke_list.pop(index)
print("抽完之后,剩下的牌为:", total_poke_list)
# 采分点
请补充完善你的代码
result = {} # 存储最终各位玩家的得分
user_list = ["zhangkai","likai","wangkai"]
# 补充代码
print(result)
实现
注意,因为下面的代码包含cls清屏指令,所以,请在Windows的终端中执行,在pycharm中右键运行清屏代码不生效。
import os
poke_type = ('红桃', '黑桃', '梅花', '方块')
poke_set = set()
poke_set.update({('小王', 0.5), ('大王', 0.5)})
poke_set.update({(k + i, 0.5) for k in poke_type for i in ['J', 'Q', 'k', 'A']})
poke_set.update({(k + str(i), i) for k in poke_type for i in range(2, 11)})
# print(poke_set)
user_dict = {
"zhangkai": {'poke': [poke_set.pop()], 'score': 0, 'msg': ''},
"likai": {'poke': [poke_set.pop()], 'score': 0, 'msg': ''},
"wangkai": {'poke': [poke_set.pop()], 'score': 0, 'msg': ''},
}
# print(user_dict)
for i in user_dict:
while True:
user_dict[i]['score'] = sum([i[1] for i in user_dict[i]['poke']])
choice = input('尊敬的用户[{}],你现在手里有牌[{}],得分[{}],要牌y/不要n\n请根据需求输入: '.format(
i,
' '.join([i[0] for i in user_dict[i]['poke']]),
sum([i[1] for i in user_dict[i]['poke']])
)).strip()
if not choice:
continue
if choice.upper() == 'N':
os.system('cls') # windows 清屏指令,Linux请使用 clear
break
elif choice.upper() == "Y":
user_dict[i]['poke'].insert(0, poke_set.pop())
user_dict[i]['score'] = sum([i[1] for i in user_dict[i]['poke']])
if user_dict[i]['score'] > 11:
print('尊敬的用户[{}],你手里有牌[{}],得分是[{}],大于11点,爆掉了,根据规则,我们不带你玩了!!!'.format(
i,
' '.join([i[0] for i in user_dict[i]['poke']]),
user_dict[i]['score']
))
user_dict[i]['msg'] = '实际得分[{}],大于11点,爆掉了'.format(sum([i[1] for i in user_dict[i]['poke']]))
user_dict[i]['score'] = 0
break
else:
print(f'你输入的是[{choice}],本程序不支持!!!!请重新输入!!!')
print('选牌完毕,正在计算得分.....')
for i in user_dict:
print('尊敬的用户[{}],你的总得分是[{}]'.format(i, user_dict[i]['score']), user_dict[i]['msg'])
# 计算出赢家
winner_user = max(user_dict, key=lambda x: user_dict[x]['score'])
print("最后的赢家是: {},得分是: {}".format(winner_user, user_dict[winner_user]['score']))
欢迎斧正,that's all
Python基础
- 简述变量命名规范
- 变量名是由字母,数字,下划线组成
- 变量名不能以数字开头
- 禁止使用python中的关键词
- 变量名要区分大小写,
- 变量名不能使用中文和拼音
- 变量名要有意义
- 推荐写法:驼峰体(
UserName或者userName)或下划线(user_name)
- name = input(“>>>”) name变量是什么数据类型
name = input(">>")
print(type(name)) # str
- if条件语句的基本结构
if
if/if
if/else
if/elif
if/elif/elif
if/elif/.../else
if 嵌套
- 用print打印出下面内容:
⽂能提笔安天下,
武能上⻢定乾坤.
⼼存谋略何⼈胜,
古今英雄唯是君.
print('''
⽂能提笔安天下,
武能上⻢定乾坤.
⼼存谋略何⼈胜,
古今英雄唯是
''')
print("⽂能提笔安天下,\n武能上⻢定乾坤.\n⼼存谋略何⼈胜,\n古今英雄唯是君.")
- 利用if语句写出猜大小的游戏:设定一个理想数字比如:66,让用户输入数字,如果比66大,则显示猜测的结果大了;如果比66小,则显示猜测的结果小了;只有等于66,显示猜测结果正确:
a = input('请输入数字')
if int(a) > 66:
print('大了')
if int(a) < 66:
print('小了')
elif int(a) = 66:
print('对了')
- 提⽰⽤户输入他的年龄, 程序进⾏判断:如果⼩于10, 提⽰⼩屁孩, 如果⼤于10, ⼩于 20, 提⽰青春期叛逆的⼩屁孩. 如果⼤于20, ⼩于30. 提⽰开始定性, 开始混社会的⼩ 屁孩⼉, 如果⼤于30, ⼩于40. 提⽰看老⼤不⼩了, 赶紧结婚⼩屁孩⼉. 如果⼤于40, ⼩ 于50. 提⽰家⾥有个不听话的⼩屁孩⼉. 如果⼤于50, ⼩于60. 提⽰⾃⼰⻢上变成不听 话的老屁孩⼉.如果⼤于60, ⼩于70. 提⽰活着还不错的老屁孩⼉. 如果⼤于70, ⼩于 90. 提⽰⼈⽣就快结束了的⼀个老屁孩⼉. 如果⼤于90以上. 提⽰. 再⻅了这个世界.
s = input('请输入年龄')
s1 = int(s1)
if s1 < 10:
print('小屁孩')
elif 10 < s1 < 20:
print('青春叛逆的小屁孩')
elif 20 < s1 < 30:
print('开始定性,开始混社会的小屁孩')
elif 30 < s1 < 40:
print('老大不小了,赶紧结婚小屁孩')
elif 40 < s1 < 50:
print('青春叛逆的小屁孩')
elif 50 < s1 < 60:
print('开始定性,开始混社会的小屁孩')
elif 60 < s1 < 70:
print('老大不小了,赶
elif 70 < s1 < 80:
print('青春叛逆的小屁孩')
elif 80 < s1 < 90:
print('开始定性,开始混社会的小屁孩')
- 判断下面print的输出结果:
print(1 > 1 or 3 < 4 or 4 > 5 and 2 > 1 and 9 > 8 or 7 < 6) # True
print(not 2 > 1 and 3 < 4 or 4 > 5 and 2 > 1 and 9 > 8 or 7 < 6) # False
- 打印菱形小星星
"""
*
***
*****
*******
*********
***********
*********
*******
*****
***
*
"""
count = 1
range_num = 13
for i in range(1, range_num):
if range_num / 2 > i: # 当宽的一半小于 i 说明要从小到大,每次加两个*
sign = "*" * count
print(sign.center(range_num, ' '), '-----', 'count == ', count, 'i == ', i, '半数: ', range_num / 2)
count += 2
else:
sign = "*" * count
print(sign.center(range_num, ' '), '+++++++', 'count == ', count, 'i == ', i, '半数: ', range_num / 2)
count -= 2
- 三级菜单
需求:
可依次选择进入各子菜单
可从任意一层往回退到上一层
可从任意一层退出程序
所需新知识点:列表、字典
基础需求:80%
- 可依次选择进入各子菜单
- 可从任意一层往回退到上一层
- 可从任意一层退出程序
所需新知识点:列表、字典
升级需求:10%
- 使用一个while循环,且整体代码量不超过15行
编码规范需求:10%
思路:
字典查询 列表循环
while 循环 加 if 判断
for 循环打印结果
menu = {
'北京':{
'海淀':{
'五道口':{
'soho':{},
'网易':{},
'google':{}
},
'中关村':{
'爱奇艺':{},
'汽车之家':{},
'youku':{},
},
'上地':{
'百度':{},
},
},
'昌平':{
'沙河':{
'老男孩':{},
'北航':{},
},
'天通苑':{},
'回龙观':{},
},
'朝阳':{},
'东城':{},
},
'上海':{
'闵行':{
"人民广场":{
'炸鸡店':{}
}
},
'闸北':{
'火车战':{
'携程':{}
}
},
'浦东':{},
},
'山东':{},
}
exit_flag = False
while not exit_flag:
for key in menu:
print(key)
choice = input(">:").strip()
if len(choice) == 0 : continue
if choice == 'q':
exit_flag = True
continue
if choice in menu: #省存在,进入此省下一级
while not exit_flag:
next_layer = menu[choice]
for key2 in next_layer:
print(key2)
choice2 = input(">>:").strip()
if len(choice2) == 0: continue
if choice2 == 'b': break
if choice2 == 'q':
exit_flag = True
continue
if choice2 in next_layer: #再进入下一层
while not exit_flag:
next_layer2 = next_layer[choice2]
for key3 in next_layer2:
print(key3)
choice3 = input(">>>:").strip()
if len(choice3) == 0: continue
if choice3 == 'b': break
if choice3 == 'q':
exit_flag = True
continue
if choice3 in next_layer2:
while not exit_flag:
next_layer3 = next_layer2[choice3]
for key4 in next_layer3:
print(key4)
choice4 = input(">>>>:").strip()
if choice4 == 'b':break
if choice4 == 'q':
exit_flag = True
continue
进阶版
menu = {
'北京':{
'海淀':{
'五道口':{
'soho':{},
'网易':{},
'google':{}
},
'中关村':{
'爱奇艺':{},
'汽车之家':{},
'youku':{},
},
'上地':{
'百度':{},
},
},
'昌平':{
'沙河':{
'老男孩':{},
'北航':{},
},
'天通苑':{},
'回龙观':{},
},
'朝阳':{},
'东城':{},
},
'上海':{
'闵行':{
"人民广场":{
'炸鸡店':{}
}
},
'闸北':{
'火车战':{
'携程':{}
}
},
'浦东':{},
},
'山东':{},
}
last_layers = [ menu ] #上一层
current_layer = menu #当前层
while True:
for key in current_layer:
print(key)
choice = input(">>:").strip()
if len(choice)==0:continue
if choice in current_layer: #进入下一层
last_layers.append(current_layer) #当前层添加到列表
current_layer = current_layer[choice] #北京
if choice == "b":
if last_layers:
current_layer = last_layers[-1] #取到上一层,赋值给current_layer
last_layers.pop()
if choice == 'q':
break
函数
- 编写认证功能的装饰器,为多个函数加上认证的功能(用户名和密码),要求只需要登录成功一次,后续的函数都能自动登录成功:
"""
认证功能的装饰器
"""
certificate_dict = {}
def cert(func):
def wrapper(*args, **kwargs):
if certificate_dict.get('status', False):
print('[{}] 自动登录成功'.format(certificate_dict['user_name']))
return func(*args, **kwargs)
else:
user, pwd = input("user: ").strip(), input("pwd: ").strip()
if user.lower() == 'zhangkai' and pwd == '123': # 这里可以将用户名和密码写在文件中
print("[{}]登录成功".format(user))
certificate_dict['user_name'] = user
certificate_dict['status'] = True
return func(*args, **kwargs)
return wrapper
@cert
def login():
print('login function')
@cert
def index():
print('index page')
@cert
def back():
print('back page')
if __name__ == '__main__':
login()
index()
back()
- 计算
1!+2!+3!+10!的阶乘结果:
def foo1(n):
num, count = 1, 0
for i in range(1, n + 1):
num *= i
count += num
return count
def foo2(n):
count = 0
for i in range(1, n + 1):
tmp = 1
for k in range(1, i + 1):
tmp *= k
count += tmp
return count
if __name__ == '__main__':
print(foo1(10)) # 4037913
print(foo2(10)) # 4037913
- 使用函数完成三次登录,要求是用户名和密码都保存在一个
info.txt中,且info.txt文件中存储有多个用户名和和密码,每个用户名和密码占用一行。三次登录失败即退出程序:
"""
# info.txt
zhangkai|123
likai|234
wangkai|345
"""
PATH = r'./info.txt'
def read_file(path):
with open(path, 'r', encoding='utf-8') as f:
l = []
for i in f:
name, pwd = i.strip().split("|")
l.append({"user": name, "pwd": pwd})
return l
def login():
user_list = read_file(PATH)
count = 1
while count <= 3:
user = input('user: ').strip()
pwd = input('pwd: ').strip()
for i in user_list:
if user == i['user'] and pwd == i['pwd']:
print('login successful')
break
else:
print('login error')
count += 1
if __name__ == '__main__':
login()
- 递归统计指定目录下的文件个数:
import os
def foo(path):
if os.path.isdir(path):
total = 0
for line in os.listdir(path):
tmp_path = os.path.join(path, line)
if os.path.isdir(tmp_path):
total += foo(tmp_path)
else:
total += 1
return total
else:
exit('给定的路径不是目录')
if __name__ == '__main__':
print(foo(r"D:\tmp"))
- 使用相关模块生成6为的验证码,验证码内容包括随机数字、随机小写字符、随机大写字符:
import random
import string
# 法1
def code(n=6):
end = ''
for i in range(n):
num = str(random.randint(0,9))
alpha_up = chr(random.randint(65,90))
alpha_low = chr(random.randint(97,122))
aim = random.choice([num,alpha_up,alpha_low])
end += aim
return end
print(code(8)) # 传几位就生成几位验证码
# 法2
def foo(n):
return ''.join(random.sample(string.digits + string.ascii_lowercase + string.ascii_uppercase, n))
print(foo(6))
- 用map来处理列表,把列表中所有人都变成
xx_666,如张开_666,name = ["张开", "李开", "王开", "赵开"]:
name = ["张开", "李开", "王开", "赵开"]
print(list(map(lambda x: x + '_666', name)))
"""
['张开_666', '李开_666', '王开_666', '赵开_666']
"""
- 使用map来处理列表,将列表中每个人的名字都编程以
xx_666,如张开_666,tmp_list = [{'name': '张开'}, {'name': '李开'}, {'name': '王开'}, {'name': '赵开'}]:
tmp_list = [{'name': '张开'}, {'name': '李开'}, {'name': '王开'}, {'name': '赵开'}]
print(list(map(lambda x: x['name'] + '_666', tmp_list)))
"""
['张开_666', '李开_666', '王开_666', '赵开_666']
"""
- 将下面的列表内的元素以
age升序排序:
tmp_list = [
{'name': '张开', 'age': 18}, {'name': '李开', 'age': 8},
{'name': '王开', 'age': 32}, {'name': '赵开', 'age': 25}
]
tmp_list.sort(key=lambda x: x['age'], reverse=False)
print(tmp_list)
"""
[{'name': '李开', 'age': 8}, {'name': '张开', 'age': 18}, {'name': '赵开', 'age': 25}, {'name': '王开', 'age': 32}]
"""
- 文件操作,使用Python将图片新建一个副本,比如有a.jpg,使用Python得到副本b.jpg:
with open('a.jpg', 'rb') as rf:
with open('b.jpg', 'wb') as wf:
wf.write(rf.read())
- 实现一个统计函数执行时间的装饰器
import time
import random
def timmer(func):
def wrapper(*args, **kwargs):
start = time.time()
res = func(*args, **kwargs)
print('{} running: {}'.format(func.__name__, time.time() - start))
return res
return wrapper
@timmer
def foo():
time.sleep(random.random())
foo()
- 写函数,接收两个数字参数,将较小的数字返回:
def foo(x, y):
return y if x > y else x
print(foo(3, 6))
print(foo(6, 3))
模块与包
- 获取当前的字符串时间(
2020-12-24 11:06:14),获取三天后的字符串时间(2020-12-24 11:06:14):
import time
# 一天的时间戳时间
one_day_time = 24 * 60 * 60
# 当前时间戳时间 --> 结构化时间 --> 字符串时间
now_timestamp_time = time.time()
now_struct_time = time.localtime(now_timestamp_time)
now_format_time = time.strftime('%Y-%m-%d %H:%M:%S', now_struct_time)
print(now_format_time) # 2020-12-24 11:06:14
# 三天后的字符串时间:当前时间戳时间 + 3天时间戳时间 --> 结构化时间 --> 字符串时间
after_three_days_timestamp_time = now_timestamp_time + one_day_time * 3
after_three_days_struct_time = time.localtime(after_three_days_timestamp_time)
after_three_days_format_time = time.strftime('%Y-%m-%d %H:%M:%S', after_three_days_struct_time)
print(after_three_days_format_time) # 2020-12-27 11:06:14
import datetime
after_three_days_struct_time = datetime.datetime.now() + datetime.timedelta(days=3)
after_three_days_format_time = after_three_days_struct_time.strftime("%Y-%m-%d %H:%M:%S")
print(after_three_days_format_time)
- 请将时间'2018-11-11 11:11:11'转换成时间戳时间
import time
''''
思路:字符串时间 --> 结构化时间 --> 时间戳时间
'''
t = "2018-11-11 11:11:11"
strpt_time = time.strptime(t, '%Y-%m-%d %H:%M:%S')
print(time.mktime(strpt_time)) # 1541905871.0
- 如何获取当前脚本的绝对路径和父级路径:
import os
print(os.path.abspath(__file__))
print(os.path.dirname(os.path.abspath(__file__)))
- 回答:
# 什么是可迭代对象?什么是迭代器?可迭代对象和迭代器的区别是什么?什么是生成器,如何得到一个生成器
# 如果一个对象具有__iter__方法,则称为可迭代对象
# 可迭代对象执行__iter__方法返回的结果称为迭代器
# 可迭代对象只有__iter__方法,而迭代器则有__iter__、__next__两个方法。
# 函数体内包含有yield关键字,那么该函数被称为生成器函数,而该函数执行的结果(返回值generator_obj)为生成器
# https://www.cnblogs.com/Neeo/articles/13200309.html
# https://www.cnblogs.com/Neeo/articles/13200313.html
- 写函数,完成给一个列表去重的功能(不能使用set集合),
tmp_list = [1, 2, 2, 1, 3, 4, 5, 6]:
def foo(l):
tmp_list = []
for i in l:
if i not in tmp_list:
tmp_list.append(i)
return tmp_list
print(foo([1, 2, 2, 1, 3, 4, 5, 6])) # [1, 2, 3, 4, 5, 6]
异常处理
面向对象
Others
- 求最大可能,也称为求一个集合的所有的子集:
def PowerSetsBinary(items):
#generate all combination of N items
N = len(items)
#enumerate the 2**N possible combinations
for i in range(2**N):
combo = []
for j in range(N):
#test jth bit of integer i
if(i >> j ) % 2 == 1:
combo.append(items[j])
yield combo
for i in PowerSetsBinary('123'):
print(i)
'''
[]
['1']
['2']
['1', '2']
['3']
['1', '3']
['2', '3']
['1', '2', '3']
'''
- Python生成目录树代码,用Python实现类似Windows下的tree命令,获取目录树结构:
import os
import os.path
BRANCH = '├─'
LAST_BRANCH = '└─'
TAB = '│ '
EMPTY_TAB = ' '
def get_dir_list(path, placeholder=''):
folder_list = [folder for folder in os.listdir(path) if os.path.isdir(os.path.join(path, folder))]
file_list = [file for file in os.listdir(path) if os.path.isfile(os.path.join(path, file))]
result = ''
for folder in folder_list[:-1]:
result += placeholder + BRANCH + folder + '\n'
result += get_dir_list(os.path.join(path, folder), placeholder + TAB)
if folder_list:
result += placeholder + (BRANCH if file_list else LAST_BRANCH) + folder_list[-1] + '\n'
result += get_dir_list(os.path.join(path, folder_list[-1]), placeholder + (TAB if file_list else EMPTY_TAB))
for file in file_list[:-1]:
result += placeholder + BRANCH + file + '\n'
if file_list:
result += placeholder + LAST_BRANCH + file_list[-1] + '\n'
return result
if __name__ == '__main__':
print(os.path.dirname(os.getcwd()))
print(get_dir_list(os.path.dirname(os.getcwd())))
- 打印斐波那契数列,斐波那契数列指的是这样一个数列 0, 1, 1, 2, 3, 5, 8, 13,特别指出:第0项是0,第1项是第一个1。从第三项开始,每一项都等于前两项之和。
# 1. 利用for循环动态输入一个动态的数列,如输入5,>>> [0, 1, 1, 2, 3]
num = int(input('>>>:'))
fibonacci = [0, 1]
for i in range(num - 2):
fibonacci.append(fibonacci[-2] + fibonacci[-1])
print(fibonacci)
# 2. 使用函数的方式
def fibonacci(num):
result = [0, 1]
for i in range(num-2):
result.append(result[-2] + result[-1])
return result
print(fibonacci(11))
# 3. 递归版本
def r_fibonacci(num):
"""递归版本 """
if num < 2:
return num
return r_fibonacci(num - 1) + r_fibonacci(num - 2)
# print(r_fibonacci(10))
for i in range(11):
print(r_fibonacci(i))
# 4. lambda版
fibonacci = lambda n: 1 if n <= 2 else fibonacci(n - 1) + fibonacci(n - 2)
print(fibonacci(35))
# 生成器版
class Fib(object):
def __init__(self):
self.prev = 0
self.curr = 1
def __iter__(self):
return self
def __next__(self):
self.curr, self.prev = self.prev + self.curr, self.curr
return self.curr
fib = Fib()
for i in range(10):
print(next(fib))
# yield版
def fib():
prev, curr = 0, 1
while True:
yield curr
curr, prev = prev + curr, curr
f = fib()
for i in range(10):
print(next(f))
欢迎斧正,that's all
需求
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

浙公网安备 33010602011771号