nginx的文本日志数据分析实战介绍
对服务器的日志数据进行提取分析是比较经典的一个数据分析项目,在业务数据中,绝大部分的数据都是来自文本文件形式的日志。来在公司的业务分析、沟通交流中,大部分使用的数据是已经被系统处理好的结构化的数据。直接对日志进行数据处理分析还是比较考验一个数据科学从业者对数据全局的管控能力,需要研究不同日志的分割、字段的含义,处理异常数据的干扰等等。
本次实验以数据小站某个时间段的nginx访问日志为示例,抽取大概一万条的日志访问记录,通过python中的pandas库,对日志进行挖掘分析,进行一个数据应用实战项目。
通过本次nginx日志挖掘分析的实战演练,可以实现包括对数据清洗、挖掘与建模、分析与可视化等比较全面的数据分析全流程的尝试。
本站的nginx访问日志,可以作为数据分析爱好者的一个实验的数据分析项目。样本数据大概一万条,量级小,但是抽取了比较有代表性的访问内容:可以研究用户分布、页面访问、
数据下载地址: http://www.51dtsc.cn/files/access_qx.log
nginx日志的基本格式

介绍一下nginx日志的基本格式,日志格式是以空格作为分隔符,分为:ip、 -、 -、 时间 时区、 请求信息 、状态码、 返回文本大小 、来源页面、 浏览器信息 这几项,其中时间与时区中, 之间有空格,当用pandas读取csv格式时,会当做两个项目。跟在ip后面的两个项目都是 “-”,可以忽略。

通过pandas.read_csv()方式读取日志文件
file = "access_qx.log" names = ['ip', '用户名', '权限', '访问时间', '时区', 'request', 'status', 'body_bytes_sent', '来源页面', 'uA'] data = pd.read_csv(file,sep=' ',header=None,names = names)
nginx日志的数据内容研究
工欲善其事必先利其器,在掌握了nginx日志的基本格式之后,就需要对数据内容的本身进行深入研究。在一个项目中对数据挖掘的深度,取决于对原始数据的掌握程度。
在nginx日志中,requests的请求信息,包含了3部分内容:请求方式、地址、版本协议。请求方式是get/post请求地址是请求的链接,“/”代表访问根路径,就是http://www.51dtsc.cn/版本协议是HTTP/1.1requests的请求信息包裹在一组字符串之内,需要文本提取清洗
nginx日志中其他字段可见即可得,可以花点时间研究字段的含义,包含了哪些信息。研究透彻之后,就可以进行下一步,数据预处理。
充分了解的数据之后,就需要明确数据挖掘分析的主题,设计数据分析的方向。在业务运营方向,可以从几个方面进行分析:1.在用户挖掘中,会关注用户的数量、地区的分布、活跃的时间,用户地区的分布需要用点爬虫的内容,获取ip所在的地区2.在内容挖掘中,会关注页面访问的次数、来源、深度访问等。3.用户和页面数据关联之后,可以挖掘新老用户的留存,不同用户关注的页面4.更深度的挖掘,就可以加入些自然语言的处理,对内容进行标签提取,在根据用户的访问标签矩阵,对用户进行聚类,设计用户画像等等5.还可以结合从搜索引擎的关键词带来的流量,对站点内容进行调整,当然这部分数据在日志中没有提供
有了日志之后,可以分析的方向有很多,当然,我更关注的是是否有欺诈访问、是否被爬虫盯上,是否被攻击、异常访问等。针对这些异常的访问,需要设计些措施,减少服务器的负担。
实战演练-nginx站点日志分析
1.nginx日志数据的读取、清洗
先对nginx日志进行简单的数据处理,提取一些信息。
file = "access.log" names = ['ip', '用户名', '权限', '访问时间', '时区', 'request', 'status', 'body_bytes_sent', '来源页面', '浏览器'] data = pd.read_csv(file,sep=' ',header=None,names = names) data['访问时间'] = data['访问时间'].map(lambda x: datetime.strptime(x,'[%d/%b/%Y:%H:%M:%S')) data['日期'] = data['访问时间'].map(lambda x:'-'.join([str(x.year),str(x.month),str(x.day)]))
用pd.value_counts方法,对user-agent的访问次数进行了统计,发现UA次数最多的两个是爬虫。网址被DotBot和AhrefsBot两个爬虫盯上,爬虫的访问要比正常用户访问的次数多,服务器基本为爬虫而存在了。
print(pd.value_counts(data['UA'])) Mozilla/5.0 (compatible; DotBot/1.1; http://www.opensiteexplorer.org/dotbot, help@moz.com) 2272 Mozilla/5.0 (compatible; AhrefsBot/6.1; +http://ahrefs.com/robot/) 1644
2.对nginx日志中user-agent浏览器信息统计
发现网址大部分访问的记录都被这两个爬虫所占据了,先用robots把 DotBot和AhrefsBot 这两个爬虫给屏蔽。在看看日志中,有没有其他爬虫访问的迹象。
def get_spiter(x): if str(x).find('DotBot') != -1: return 'DotBot' if str(x).find('AhrefsBot') != -1: return 'AhrefsBot' if str(x).find('Googlebot') != -1: return 'Googlebot' if str(x).find('Baiduspider') != -1: return 'Baiduspider' return '无' data['请求来源'] = data['浏览器'].map(get_spiter) print(pd.value_counts(data['请求来源']))
定义了一个对浏览器内容进行字符查找的方法,查看浏览器中是否包含DotBot AhrefsBot Googlebot Baiduspider 这几个爬虫,找到就返回。在通过pandas中series对象自带的map方法,对浏览器数据进行提取。然后对提取的请求来源自带进行统计分析,查看这四个爬虫出现的次数。
无 5954
DotBot 2772
AhrefsBot 1646
Googlebot 1172
Baiduspider 307
Name: 请求来源, dtype: int64
发现一万多次的日志记录, DotBot AhrefsBot Googlebot Baiduspider 这几个爬虫 这几个爬虫占了一半多,还不包括其他没有统计到的爬虫。百度和谷歌的保留,其他的恶意爬虫发现就屏蔽了吧,封ip,加火墙,设rotbots。
浏览器UA即User-Agent, User Agent中文名为用户代理,简称 UA,它是一个特殊字符串头,使得服务器能够识别客户使用的操作系统及版本、CPU 类型、浏览器及版本、浏览器渲染引擎、浏览器语言、浏览器插件等。 一些网站常常通过判断 UA 来给不同的操作系统、不同的浏览器发送不同的页面,因此可能造成某些页面无法在某个浏览器中正常显示,但通过伪装 UA 可以绕过检测。
浏览器的 UA 字串
标准格式为: 浏览器标识 (操作系统标识; 加密等级标识; 浏览器语言) 渲染引擎标识 版本信息 详情请百度。
3.分析异常访问
按日查看不同请求来源的访问情况
print(data.groupby(['日期','请求来源']).size().unstack(fill_value=0))
日期 AhrefsBot Baiduspider DotBot Googlebot 无
2020-7-10 6 30 469 117 977
2020-7-11 447 77 1058 300 1003
2020-7-12 389 61 734 272 1570
2020-7-13 461 72 326 334 1889
2020-7-14 448 98 262 208 723
2020-7-15 69 92 50 211 736
2020-7-16 0 81 1 199 5925
2020-7-17 0 54 1 203 2440
2020-7-18 1 81 1 206 22860
2020-7-19 0 60 1 212 22087
2020-7-20 0 17 1 86 12249
前几天用服务器屏蔽了 AhrefsBot DotBot这个两个爬虫后, 这两个爬虫的访问已经减少。但是从18号,突然流量开始异常增加,需要查看流量异常的原因。
3.1查看异常ip来路
nodt = data[data['请求来源'] == '无']
print(pd.value_counts(nodt['ip']).head())
13.68.249.84 5245
134.122.81.154 3008
发现了两个异常高的ip,通过ip地址查询,都是来自境外的ip。
ip = '13.68.249.84' print('\n------------') ipdt = nodt[nodt['ip']==ip] print(pd.value_counts(ipdt['日期'])) print('\n------------') print(ipdt.groupby(['请求方式','status']).size().unstack(fill_value=0)) print('\n------------') print(pd.value_counts(ipdt['请求页面']))
将其中一个ip 13.68.249.84 的访问数据提取出来,查看访问的详情,发现:
2020-7-16 5245
Name: 日期, dtype: int64
status 200 301 404
请求方式
GET 2 2 2
POST 5239 0 0
/xmlrpc.php 5239
这个ip是从16号发起请求,主要是post,攻击的页面是 xmlrpc.php 。
查询大流量的访问ip,结果是一样。境外的ip,通过代理,不断的更换ip,对 xmlrpc.php 这个页面发起了post请求。 xmlrpc.php 这个页面,是wordpress的漏掉,可以绕过安全机制,暴力破解后台。
就通过在nginx的配置中,禁止对这个页面的访问。一切清净了。
4.nginx日志的访问分析源码
import pandas as pd
from datetime import datetime,date
def get_fs(x):
'''提取request中的请求方式'''
x = str(x)
if x.find('GET') != -1:
return 'GET'
if x.find('POST') != -1:
return 'POST'
if x.find('HEAD') != -1:
return 'HEAD'
if x.find('CONNECT') != -1:
return 'CONNECT'
return '-'
def get_lj(x):
'''获取访问的页面路径'''
st = (str(x).split(' '))
if len(st) ==3:
lj = st[1]
if lj.startswith('http'):
return lj[20:]
return lj
def get_spiter(x):
if str(x).find('DotBot') != -1:
return 'DotBot'
if str(x).find('AhrefsBot') != -1:
return 'AhrefsBot'
if str(x).find('Googlebot') != -1:
return 'Googlebot'
if str(x).find('Baiduspider') != -1:
return 'Baiduspider'
return '无'
if __name__ == '__main__':
file = r"C:\Users\chenyibin\Desktop\access.log"
names = ['ip', '用户名', '权限', '访问时间', '时区', 'request', 'status', 'body_bytes_sent', '来源页面', '浏览器']
data = pd.read_csv(file,sep=' ',header=None,names = names)
data['访问时间'] = data['访问时间'].map(lambda x: datetime.strptime(x,'[%d/%b/%Y:%H:%M:%S'))
data['日期'] = data['访问时间'].map(lambda x:'-'.join([str(x.year),str(x.month),str(x.day)]))
data['请求方式'] = data['request'].map(get_fs)
data['请求页面'] = data['request'].map(get_lj)
data['请求来源'] = data['浏览器'].map(get_spiter)
print(pd.value_counts(data['日期']))
print(data.groupby(['日期','请求来源']).size().unstack(fill_value=0))
print('\n------------')
nodt = data[data['请求来源'] == '无']
print(pd.value_counts(nodt['ip']).head())
ip = '13.68.249.84'
print('\n------------')
ipdt = nodt[nodt['ip']==ip]
print(pd.value_counts(ipdt['日期']))
print('\n------------')
print(ipdt.groupby(['请求方式','status']).size().unstack(fill_value=0))
print('\n------------')
print(pd.value_counts(ipdt['请求页面']))
后记,通过对nginx的日志记录的分析,发现来自境外ip的大量请求。而且,几天之前,后台被轻松攻破了,手动黑脸。幸运的是,未授权的来客手下留情,只是发布了几篇文章,没有对站点内容进行破坏。
趁这次机会,升级一下服务器安全环境,封了境外的ip,不在使用123456这种弱口令,调整文件权限,不在用777这种透明设置。这几天看,服务器负载轻了很多。查看404的访问记录,发现还有国内的一些小黑们,对一些不该访问的地址,在尝试做些他们兴趣爱好的事情吧。
无伤大雅,对于这种每天正常不过10人路过的小站来说,也许就是小黑们比较感兴趣,无非就是你发现发现漏掉,我修补修补补丁,也其乐融融,增加点调味剂。