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()
该类中主要有两个方法:
- parse: 将传入的字符串解析为有用的 7个 部分
- to_dict: 将解析好的7个部分的数据转化成键值对。
1.6. IP对应地区文件讲解
这边提供了两种文件
- area_ip.sql: ip地址的sql文件,可以直接导入关系型数据库。
- 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. 我们的目标
我们需要分析出以下几个结论:
- 每天的PV
- 一天中哪个时段访问量比较高
- 每个URL被请求了多少次(TOP 100)
- 外链地址点击次数(TOP 100)
- 浏览工具的种类(TOP 100)
- CDN IP 数量 (TOP 100)
- 真实用户IP访问次数(TOP 100)
- CDN IP 和 地址(TOP 100)
- 真实IP 和 地址(TOP 100)
昵称: HH
QQ: 275258836
ttlsa群交流沟通(QQ群②: 6690706 QQ群③: 168085569 QQ群④: 415230207(新) 微信公众号: ttlsacom)
感觉本文内容不错,读后有收获?

1F
41932a82b1f4354bd81e1ddb80f6f2a7很不错