access重复数据累计_极简教程: 网站日志数据分析实战(四)

a1f5f621787c684fc76edab0122a525a.png

目的

  • 阅读对象: 7天SQL基础+7天Python同学
  • 预期收获: 接触真实的日志数据

关于本文章涉及到的日志数据:

  • 数据来源: 访问superset学习网站生产的nginx真实的日志数据, IP属于隐私数据,所以映射为地域信息
  • 怎么使用: 大家直接进入学习网站,点击SQL Lab, 选择data_sample, 再选access_log即可看到
为方便练习SQL,提供一个公共账号, 用户名:hi-zhihu 密码:justdoit

为了更直白,我录一段操作小视频演示下:

960546b39ccfce39c0ba85f972de0472.png
教你如何进入数据练习环境https://www.zhihu.com/video/1142590482713632768

下面,简单对App的日志数据做简单介绍。

日志来源

数据分析面对的日志数据,主要来自两个端, 如下图:

4501dd8f5be9fff2aa094e20f4231926.png
日志来源

一个是服务端,主要是产品的每个功能背后对应的数据接口请求数据,这部分数据大部分生产,对用户来说是透明的,看不到,比如: App上报一些开关状态信息,用户无感知的情况下进行的;

另外一个是大前端,用户能直接看到的,包括客户端页面和网页端页面,我们常说的用户点击行为日志。

这样说,小白听了好像还是给没听一样, 这样的效果,我干嘛花时间码字?!所以,我现在把真实的日志捞出来,大家一起观摩一下,虽不能代表所有公司的日志场景,但是作为初次接触,了解学习,你会的收获会很足!这,才是我花时间写的目的!

日志数据样本

这里,简单介绍下网站日志的一种,nginx日志。 字段介绍:

  1. 客户端(用户)IP地址
  2. 访问时间。如: [07/Aug/2019:16:00:00 +0800]
  3. 请求方式(GET或者POST等)。如:上例中的 GET
  4. 请求状态(状态码,200表示成功,404表示页面不存在,301表示永久重定向等)
  5. 请求页面大小,默认为B(byte)
  6. 来源页面,即从哪个页面转到本页,专业名称叫做“referer”
  7. 用户浏览器版本、浏览器类型等

样本数据:(ip脱敏)

52026f1af61a70272fb43eacc9dff761.png
日志样例

日志脱敏

根据ip可以映射地域,这里我使用的是python第三方包ip2region进行ip到地域的映射。

  • ip映射地域的python代码
def ip_to_geo(ip, db_path):
    searcher = Ip2Region(db_path)
    res = searcher.memorySearch(ip)
    return str(res['city_id']) + '|' + res['region'].decode('utf-8')
  • 插入数据库代码片段
# 这里略过pytho连接mysql数据的过程,直接进入逻辑处理部分
def insert_log(cursor, connect, access_log, logger):
    column_list = ['ip', 'ext1', 'ext2', 'createTime', 'microSecond', 'api', 'statusCode', 'bodyBytes', 'httpReferer',
                   'userAgent']
    df = pd.read_csv(access_log, sep=' ', header=None, names=column_list).head()
    logger.info("%s has %s lines" % (access_log, df.count(axis=1).size))

    # 不同字段进行处理, 满足mysql表插入字段定义
    df['ipLast'] = df.ip.apply(lambda ip: ip_last_n(ip, n=3))
    df['geo'] = df.ip.apply(lambda ip: ip_to_geo(ip, db_path=db_path))
    df['newCreateTime'] = df.createTime.apply(time_format)
    df['newApi'] = df.api.apply(lambda x: cut_str(x, 100))
    df['newHttpReferer'] = df.httpReferer.apply(lambda x: cut_str(x, 200))
    df['newUserAgent'] = df.userAgent.apply(lambda x: cut_str(x, 500))
    list_with_ip = ['ip', 'ipLast', 'geo', 'newCreateTime', 'newApi', 'statusCode', 'bodyBytes', 'newHttpReferer', 'newUserAgent']

    list_insert_with_ip = df[list_with_ip].values.tolist()
    logger.info("%s first line is : %s" % (access_log, list_insert_with_ip[0]))

    insert_num = 0
    for line in list_insert_with_ip:
        insert_num += 1
        line_with_ip = [line[0]] + line[2:]
        line_without_ip = line[1:]
        try:
            cur.execute(sql_without_ip, tuple(line_without_ip))
        except Exception as e:
            logger.error("%s. n %s insert failed when to insert % line!! insert line: %s" % (e, access_log, insert_num, line))
            break
    conn.commit()

以上日志数据,经过简单处理后,插入到mysql数据库中,表信息如下:

CREATE TABLE `access_log_without_ip` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `eid` varchar(20) NOT NULL DEFAULT '' COMMENT '重复可能性大',
  `geo` varchar(60) NOT NULL DEFAULT '' COMMENT '地域信息:城市Id|国家|区域|省份|城市|ISP',
  `createTime` timestamp NOT NULL DEFAULT '2000-01-01 00:00:00' COMMENT '访问时间',
  `api` varchar(100) NOT NULL DEFAULT '' COMMENT '访问api',
  `statusCode` smallint(6) NOT NULL DEFAULT '0' COMMENT 'http请求返回状态码',
  `bodyBytes` int(11) NOT NULL DEFAULT '0' COMMENT '请求文件大小',
  `httpReferer` varchar(200) NOT NULL DEFAULT '' COMMENT '访问url',
  `userAgent` varchar(500) NOT NULL DEFAULT '' COMMENT 'ua',
  PRIMARY KEY (`id`),
  UNIQUE KEY `userAccess` (`eid`,`geo`,`createTime`,`api`)
) ENGINE=InnoDB AUTO_INCREMENT=324700 DEFAULT CHARSET=utf8mb4

取前几行样例如下:

mysql> select * from access_log_without_ip where date(createTime)='2019-08-07' limit 100,1 G;
*************************** 1. row ***************************
         id: 321435
        eid: 18
        geo: 995|中国|0|上海|上海市|联通
 createTime: 2019-08-07 08:43:11
        api: GET /superset/fave_dashboards/3/ HTTP/1.1
 statusCode: 200
  bodyBytes: 2
httpReferer: http://superset.workingindata.com/superset/welcome
  userAgent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36
1 row in set (0.09 sec)

日志分析

进入练习环境见文章开始的视频,下面简单的几个日志统计如下:

  • 每日访问pv分布
mysql> select date(createTime) as day, count(*) as pv from access_log_without_ip group by date(createTime) order by day ;
+------------+-------+
| day        | pv    |
+------------+-------+
| 2019-07-18 |   157 |
| 2019-07-19 |    59 |
| 2019-07-20 |    58 |
| 2019-07-21 |  1033 |
| 2019-07-22 |  2020 |
| 2019-07-23 |  1513 |
| 2019-07-24 |  1520 |
| 2019-07-25 |  1929 |
| 2019-07-26 |  6152 |
| 2019-07-27 |    54 |
| 2019-07-28 |  4448 |
| 2019-07-29 |  5576 |
| 2019-07-30 | 47677 |
| 2019-07-31 |  6212 |
| 2019-08-01 |  5285 |
| 2019-08-02 |  7351 |
| 2019-08-03 |  2468 |
| 2019-08-04 |  1784 |
| 2019-08-05 |  2211 |
| 2019-08-06 |  2629 |
| 2019-08-07 |  1636 |
+------------+-------+
21 rows in set (0.10 sec)
  • 24h用户访问分布

数据上看,每天的中午是访问的高峰期

mysql> select hour(createTime) as h, count(*) as pv from access_log_without_ip group by hour(createTime) order by h;
+------+-------+
| h    | pv    |
+------+-------+
|    0 |  1286 |
|    1 |   563 |
|    2 |   303 |
|    3 |   314 |
|    4 |   640 |
|    5 |   421 |
|    6 |   446 |
|    7 |   912 |
|    8 |  1790 |
|    9 |  4298 |
|   10 |  9555 |
|   11 | 11539 |
|   12 |  7925 |
|   13 | 10191 |
|   14 | 12397 |
|   15 |  9847 |
|   16 |  5508 |
|   17 |  4177 |
|   18 |  5187 |
|   19 |  3901 |
|   20 |  2237 |
|   21 |  3329 |
|   22 |  3312 |
|   23 |  1694 |
+------+-------+
24 rows in set (0.08 sec)
  • 历史访问总量的省份top 10

看看,top3城市缩写熟不熟悉,"北上广",所以,码农多不是没原因的。

mysql> SELECT 
    -> substring_index(SUBSTRING_INDEX(geo, '|', 4), '|', -1)
    -> as city,
    -> count(*) as pv 
    -> from access_log.access_log_without_ip 
    -> where date(createTime)> date_sub(date(createTime), interval 15 day)
    -> group by substring_index(SUBSTRING_INDEX(geo, '|', 4), '|', -1)
    -> order by pv desc limit 10;
+-----------+-------+
| city      | pv    |
+-----------+-------+
| 北京      | 23750 |
| 广东省    | 18112 |
| 上海      | 10934 |
| 重庆      | 10767 |
| 福建省    | 10698 |
| 辽宁省    |  4849 |
| 浙江省    |  4086 |
| 山东省    |  3283 |
| 江苏省    |  2605 |
| 湖北省    |  1714 |
+-----------+-------+
10 rows in set (0.22 sec)
  • 统计访问最多的url Top3

用户目的很明确,就是进入SQL实验室练习SQL,这也是我希望看到的。大家加油!

mysql> select httpReferer, count(*) pv from access_log.access_log_without_ip group by httpReferer order by pv desc limit 3;
+----------------------------------------------------+-------+
| httpReferer                                        | pv    |
+----------------------------------------------------+-------+
| http://superset.workingindata.com/superset/sqllab  | 62033 |
| http://superset.workingindata.com/login/           | 13940 |
| http://superset.workingindata.com/superset/welcome |  4794 |
+----------------------------------------------------+-------+
3 rows in set (0.59 sec)

ed80e34119895405e2132c13c5782376.png
精选文章

数据民工来取经儿:超级菜鸟如何入坑数据分析

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
SQLAlchemy 是一个 SQL 工具包和对象关系映射(ORM)库,用于 Python 编程语言。它提供了一个高级的 SQL 工具和对象关系映射工具,允许开发者以 Python 类和对象的形式操作数据库,而无需编写大量的 SQL 语句。SQLAlchemy 建立在 DBAPI 之上,支持多种数据库后端,如 SQLite, MySQL, PostgreSQL 等。 SQLAlchemy 的核心功能: 对象关系映射(ORM): SQLAlchemy 允许开发者使用 Python 类来表示数据库表,使用类的实例表示表中的行。 开发者可以定义类之间的关系(如一对多、多对多),SQLAlchemy 会自动处理这些关系在数据库中的映射。 通过 ORM,开发者可以像操作 Python 对象一样操作数据库,这大大简化了数据库操作的复杂性。 表达式语言: SQLAlchemy 提供了一个丰富的 SQL 表达式语言,允许开发者以 Python 表达式的方式编写复杂的 SQL 查询。 表达式语言提供了对 SQL 语句的灵活控制,同时保持了代码的可读性和可维护性。 数据库引擎和连接池: SQLAlchemy 支持多种数据库后端,并且为每种后端提供了对应的数据库引擎。 它还提供了连接池管理功能,以优化数据库连接的创建、使用和释放。 会话管理: SQLAlchemy 使用会话(Session)来管理对象的持久化状态。 会话提供了一个工作单元(unit of work)和身份映射(identity map)的概念,使得对象的状态管理和查询更加高效。 事件系统: SQLAlchemy 提供了一个事件系统,允许开发者在 ORM 的各个生命周期阶段插入自定义的钩子函数。 这使得开发者可以在对象加载、修改、删除等操作时执行额外的逻辑。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值