1-Python - 网站访问日志分析作业

需求

Python3.6.8
站长之家的正则在线测试

基本需求

  1. 统计日志文件的总pv、uv
  2. 列出全天每小时的pv、uv
  3. 列出top10 uv的IP地址,以及每个IP的pv点击数
  4. 列出top10访问量最多的页面及每个页面的访问量
  5. 列出访问来源设备列表及每个设备的访问量

说明

  1. pv:page visit,页面访问量,一次请求就是一次pv
  2. 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 "-" "-"

首先要把异常日志排除掉。
然后再针对性实现每个需求。

  1. 统计日志文件的总pv、uv
-- 循环每条正常的日志,使用正则查找当前行的 ip
      - pv = 每一个可以重复的 ip 的和
      - uv = 每一个不可重复的 ip 的和
  1. 列出全天每小时的pv、uv
-- 循环每条正常的日志,使用正则查找当前行的 ip
-- 以小时为分割,然后统计每个小时的pv和uv
      - pv = 每一个可以重复的 ip 的和
      - uv = 每一个不可重复的 ip 的和
-- 难点:
      - 从日志中读取的日期时间,如何处理为可用的时间格式(格式化后的字符串/时间戳)
      - 如何组织数据结构
  1. 列出top10 uv的IP地址,以及每个IP的pv点击数
-- 循环每条正常的日志,使用正则查找当前行的 ip
      - 循环读取所有ip,取出每一个ip
            - 统计每个不重复的ip,以及统计该ip重复出现的次数
      - 最后进行降序排序,然后取最高的top10
  1. 列出top10访问量最多的页面及每个页面的访问量
-- 循环每条正常的日志,使用正则查找当前行的 page
      - 循环读取所有page,取出每一个page
            - 统计每个不重复的page,以及统计该page重复出现的次数
      - 最后进行降序排序,然后取最高的top10 的page
  1. 列出访问来源设备列表及每个设备的访问量
-- 循环每条正常的日志,使用正则查找当前行的设备
      - 统计每个不重复的设备,以及统计该设备重复出现的次数
      - 虽然需求是列出所有的设备,但我的代码中只展示了前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
posted @ 2019-02-27 23:01  听雨危楼  阅读(886)  评论(0编辑  收藏  举报