Init – Python数据分析(1)

HH python1 29,02562字数 5566阅读18分33秒阅读模式

1.1. 前言

由于公司业务的发展,在一些产品的功能上面不再满足于哪些简简单单的功能性的主要功能了。也开始考虑一些为产品增添色彩的功能(数据统计)。

当然,产品中的一些基本的统计功能,使用关系型数据库(MySQL)基本都能解决的。但是当访问量上来了数据量稍微有了,使用MySQL来承担报表的工作就显得有点吃力了。文章源自运维生存时间-https://www.ttlsa.com/python/python-big-data-analysis-init/

这时候就不得不从业务和架构上去考虑来做这些报表功能。文章源自运维生存时间-https://www.ttlsa.com/python/python-big-data-analysis-init/

1.2. 业务的需求

对于做需求的来说,数据总是需要实时的,他们根本就不理解 '实时' 这两个字会给系统带来多大的负担。文章源自运维生存时间-https://www.ttlsa.com/python/python-big-data-analysis-init/

如:用户对浏览过的商品、页面的浏览量文章源自运维生存时间-https://www.ttlsa.com/python/python-big-data-analysis-init/

1、(实时)在访问量小的时候可以使用 MySQL 完全可以解决问题。文章源自运维生存时间-https://www.ttlsa.com/python/python-big-data-analysis-init/

2、(实时)在访问量稍微大了点对系统有点压力 可以使用 MySQL/Redis + 从库查询 来克服对主库带来的压力。文章源自运维生存时间-https://www.ttlsa.com/python/python-big-data-analysis-init/

3、(放弃实时,变成准实时)在访问量真的很大了使用上面的在做统计就有边的费力了这时候就需要使用其他的方法了。现在有许多的方法可以很好的完成这样的工作(Strom/Spark/Hadoop)。文章源自运维生存时间-https://www.ttlsa.com/python/python-big-data-analysis-init/

1、Strom: 实时流式计算文章源自运维生存时间-https://www.ttlsa.com/python/python-big-data-analysis-init/

2、Spark/Pandas: 内存计算文章源自运维生存时间-https://www.ttlsa.com/python/python-big-data-analysis-init/

3、Hadoop(M/R): 离线计算文章源自运维生存时间-https://www.ttlsa.com/python/python-big-data-analysis-init/

1.3. 正题

今天我们介绍使用 Hadoop(M/R) 框架 和 Pandas 来分析Nginx日志文章源自运维生存时间-https://www.ttlsa.com/python/python-big-data-analysis-init/

相关的示例Nginx日志文件在网盘上:文章源自运维生存时间-https://www.ttlsa.com/python/python-big-data-analysis-init/

链接: https://pan.baidu.com/s/1qYyAyvi 密码: rnbj文章源自运维生存时间-https://www.ttlsa.com/python/python-big-data-analysis-init/

源代码:https://github.com/daiguadaidai/nginx_log_parse文章源自运维生存时间-https://www.ttlsa.com/python/python-big-data-analysis-init/

1.4. Nginx 日志格式

140.205.127.56 - - [13/Jun/2016:14:37:04 +0800] "GET /wp-content/plugins/font-awesome-4-menus/fonts/fontawesome-webfont.woff2?v=4.6.1 HTTP/1.1" 304 0 "http://www.ttmark.com/wp-content/plugins/font-awesome-4-menus/css/font-awesome.min.css?ver=4.6.1" "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.154 Safari/537.36 LBBROWSER" "60.222.159.45"

这边我将日志分为 7个 部分: 文章源自运维生存时间-https://www.ttlsa.com/python/python-big-data-analysis-init/

cdn_ip(CDN请求IP): 140.205.127.56文章源自运维生存时间-https://www.ttlsa.com/python/python-big-data-analysis-init/

access_time(请求时间): 13/Jun/2016:14:37:04 +0800文章源自运维生存时间-https://www.ttlsa.com/python/python-big-data-analysis-init/

request_url(请求的URL): /wp-content/plugins/font-awesome-4-menus/fonts/fontawesome-webfont.woff2文章源自运维生存时间-https://www.ttlsa.com/python/python-big-data-analysis-init/

response_status(NG 响应状态码): 304文章源自运维生存时间-https://www.ttlsa.com/python/python-big-data-analysis-init/

reference_url(外链URL): www.ttmark.com文章源自运维生存时间-https://www.ttlsa.com/python/python-big-data-analysis-init/

browser(用户使用的浏览器): Chrome/42.0.2311.154 Safari/537.36 LBBROWSER

real_ip(用户真实IP): 60.222.159.45

1.5. 这边使用Python写了一个类专门用来解析Nginx日志的

代码如下:

#!/usr/bin/env python
#-*- coding:utf-8 -*-
 
import datetime
import urllib
 
class NgLineParser(object):
    """将 Nginx 日志解析成多个字段"""
 
    def __init__(self):
        self._cdn_ip = '' # CDN请求IP
        self._access_time = '' # 请求时间
        self._request_url = '' # 请求的URL
        self._reference_url = '' # 外链URL
        self._response_status = '' # NG 响应状态码
        self._browser = '' # 用户使用的浏览器
        self._real_ip = '' # 用户真实IP
 
    def parse(self, line):
        """通过传入的一行数据进行解析
        """
        line_item = line.strip().split('"')
 
        if len(line_item) > 9: # 由于日志有改变需要删除一些元素
            del line_item[1]
            del line_item[1]
 
        # 获取临时的 CDN IP 和 访问文件
        ip_time_tmp = line_item[0].strip().split()
 
        self.real_ip = line_item[7] # 用户真实IP
        self.cdn_ip = ip_time_tmp[0] # CDN请求IP
        self.access_time = ip_time_tmp[3].lstrip('[') # 请求时间
        self.request_url = line_item[1].strip().split()[1] # 请求的URL
        self.reference_url = line_item[3].strip() # 外链URL
        self.response_status = line_item[2].strip().split()[0] # NG 响应状态码
        self.browser = ' '.join(line_item[5].strip().split()[-2:]) # 用户使用的浏览器
 
    def to_dict(self):
        """将属性(@property)的转化为dict输出
        """
        propertys = {}
        propertys['real_ip'] = self.real_ip
        propertys['cdn_ip'] = self.cdn_ip
        propertys['access_time'] = self.access_time
        propertys['request_url'] = self.request_url
        propertys['reference_url'] = self.reference_url
        propertys['response_status'] = self.response_status
        propertys['browser'] = self.browser
 
        return propertys
 
    @property
    def real_ip(self):
        return self._real_ip
 
    @real_ip.setter
    def real_ip(self, real_ip):
        self._real_ip = real_ip.split(', ')[0]
 
    @property
    def browser(self):
        return self._browser
 
    @browser.setter
    def browser(self, browser):
        self._browser = browser
 
    @property
    def response_status(self):
        return self._response_status
 
    @response_status.setter
    def response_status(self, response_status):
        self._response_status = response_status
 
    @property
    def reference_url(self):
        return self._reference_url
 
    @reference_url.setter
    def reference_url(self, reference_url):
        """解析外链URL
        只需要解析后的域名, 如:
            传入: http://www.ttmark.com/diannao/2014/11/04/470.html
            解析成: www.ttmark.com
        """
        proto, rest = urllib.splittype(reference_url)
        res, rest = urllib.splithost(rest)
        if not res:
            self._reference_url = '-'
        else:
            self._reference_url = res
 
    @property
    def request_url(self):
        return self._request_url
 
    @request_url.setter
    def request_url(self, request_url):
        """解析请求的URL
        只需要解析后的URL路径不需要参数, 如:
            传入: /wp-admin/admin-ajax.php?postviews_id=1348
            解析成: /wp-admin/admin-ajax.php
        """
        proto, rest = urllib.splittype(request_url)
        url_path, url_param = urllib.splitquery(rest)
 
        if url_path.startswith('/tag/'):
            url_path = '/tag/'
 
        self._request_url = url_path
 
    @property
    def access_time(self):
        return str(self._access_time)
 
    @access_time.setter
    def access_time(self, access_time):
        # Nginx log 解析日志格式
        input_datetime_format = '%d/%b/%Y:%H:%M:%S'
 
        self._access_time = datetime.datetime.strptime(
                                     access_time,
                                     input_datetime_format)
 
    @property
    def cdn_ip(self):
        return self._cdn_ip
 
    @cdn_ip.setter
    def cdn_ip(self, cdn_ip):
        self._cdn_ip = cdn_ip
 
 
def main():
    ng_line_parser = NgLineParser()
 
    with open('www.ttmark.com.access.log', 'r') as f:
        for index, line in enumerate(f):
            ng_line_parser.parse(line)
 
            print '----------------------------'
            print index
            print ng_line_parser.real_ip
            print ng_line_parser.cdn_ip
            print ng_line_parser.access_time
            print ng_line_parser.request_url
            print ng_line_parser.reference_url
            print ng_line_parser.response_status
            print ng_line_parser.browser
 
            # 只解析10行
            if index == 10: break
 
 
if __name__ == '__main__':
    main()

该类中主要有两个方法:

  1. parse: 将传入的字符串解析为有用的 7个 部分
  2. to_dict: 将解析好的7个部分的数据转化成键值对。

1.6. IP对应地区文件讲解

这边提供了两种文件

  1. area_ip.sql: ip地址的sql文件,可以直接导入关系型数据库。
  2. area_ip.csv: 以 tab 分割的IP地址数据

在我们的示例中使用的是 area_ip.csv

area_ip.csv 的数据:

每一个数据中存放的都是一个区域的IP的范围

head area_ip.csv
2634    0       16777215        0.0.0.0 0.255.255.255   IANA    保留地址
2635    16777216        16777471        1.0.0.0 1.0.0.255       澳大利亚        亚太互联网络信息中心
2636    16777472        16778239        1.0.1.0 1.0.3.255       福建省  电信
2637    16778240        16779263        1.0.4.0 1.0.7.255       澳大利亚        墨尔本Big
2638    16779264        16781311        1.0.8.0 1.0.15.255      广东省  电信
2639    16781312        16785407        1.0.16.0        1.0.31.255      日本    东京I2Ts
2640    16785408        16793599        1.0.32.0        1.0.63.255      广东省  电信
2641    16793600        16809983        1.0.64.0        1.0.127.255     日本    広島県中区大手町Energia通信公司
2642    16809984        16842751        1.0.128.0       1.0.255.255     泰国    CZ88.NET
2643    16842752        16843007        1.1.0.0 1.1.0.255       福建省  电信

以第一行为例

2634: 主键

0: 一个区域IP起始数字(一个IP地址可以转化成一个INT数字)

16777215: 一个区域IP结束数字

0.0.0.0: 一个区域的IP起始地址

0.255.255.255: 一个区域的IP结束地址

IANA: 该IP范围的所在区域

保留地址: 该IP的运营商是谁

1.7. 我们的目标

我们需要分析出以下几个结论:

  1. 每天的PV
  2. 一天中哪个时段访问量比较高
  3. 每个URL被请求了多少次(TOP 100)
  4. 外链地址点击次数(TOP 100)
  5. 浏览工具的种类(TOP 100)
  6. CDN IP 数量 (TOP 100)
  7. 真实用户IP访问次数(TOP 100)
  8. CDN IP 和 地址(TOP 100)
  9. 真实IP 和 地址(TOP 100)

 

昵称: HH

QQ: 275258836

ttlsa群交流沟通(QQ群②: 6690706 QQ群③: 168085569 QQ群④: 415230207(新) 微信公众号: ttlsacom)

感觉本文内容不错,读后有收获?

逛逛衣服店,鼓励作者写出更好文章。

weinxin
我的微信
微信公众号
扫一扫关注运维生存时间公众号,获取最新技术文章~
HH
  • 本文由 发表于 26/10/2016 00:23:49
  • 转载请务必保留本文链接:https://www.ttlsa.com/python/python-big-data-analysis-init/
评论  1  访客  1
    • 心雨
      心雨 1

      41932a82b1f4354bd81e1ddb80f6f2a7很不错

    评论已关闭!