您好,登录后才能下订单哦!
网站的访问日志是一个非常重要的文件,通过分析访问日志,能够挖掘出很多有价值的信息。本文介绍如何利用Python对一个真实网站的访问日志进行分析,文中将综合运用Python文件操作、字符串处理、列表、集合、字典等相关知识点。本文所用的访问日志access_log来自我个人的云服务器,大家可以从文末的附件中下载。
下面是一条典型的网站访问日志,客户端访问网站中的每个资源都会产生一条日志。
193.112.9.107 - - [25/Jan/2020:06:32:58 +0800] "GET /robots.txt HTTP/1.1" 404 208 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:61.0) Gecko/20100101 Firefox/61.0"
每条日志都由空格分隔为九部分,其中比较重要的是:
一个日志文件中会包含很多天的日志记录,而我们通常都是针对某一天进行日志分析,所以首先需要从日志文件中把我们要分析的那一天的日志提取出来。
比如要提取1月25日产生的日志,可以执行下面的代码:
>>> with open('access_log','r') as f1, open('access_log-0125','w') as f2:
... for line in f1:
... if '25/Jan/2020' in line:
... f2.write(line)
在这段代码中,以r读取模式打开日志文件access_log,作为文件对象f1。以w写入模式创建文件access_log-0125,作为文件对象f2。
然后遍历f1中的每一行,并判断是否包含关键字“25/Jan/2020”,如果有的话,就将这行写入到f2中。
这样就提取出了1月25日的所有日志记录,并保存到了文件access_log-0125中。下面我们就针对文件access_log-0125进行分析。
PV是指PageView,网站的访问请求数。用户每次对网站中的一个页面的请求或访问均被记录为1个PV,例如某个用户访问了网站中的4个页面,那么PV就+4。而且用户对同一页面的多次访问,PV也是累计的。
UV是指UniqueView,网站的独立访客数,访问网站的一个IP被视为一个访客,在同一天内相同的IP只被计算一次。
因而,我们只要取出每条日志中的IP并统计数量,那么就可以得到PV,将IP去重,就可以得到UV。
执行下面的代码,将每条日志的IP提取出来,并存放到列表ips中。
>>> ips = []
>>> with open('access_log-0125','r') as f:
... for line in f:
... ips.append(line.split()[0])
在这段代码中,首先定义了一个空列表ips,然后打开文件access_log-0125并对其进行遍历,每遍历一行,就将该行以空格作为分隔符分割成一个列表,并取出列表中的第一个元素(也就是IP地址),再追加到列表ips中。
下面我们只要统计列表ips的长度就是PV,将列表元素去重之后,再统计长度就是UV。去重这里采用了set()函数,将列表转换为集合,利用Python集合本身的特性,简单高效的完成去重操作。
>>> pv = len(ips)
>>> uv = len(set(ips))
>>> print(pv,uv)
1011 48
网站的出错比例是很重要的一份数据,直接关系到网站的用户体验。要统计用户访问出错的比例,可以通过统计每个请求的HTTP状态码得到,状态码为2xx或3xx的,视为访问正确,状态码为4xx或5xx,则视为访问出错。
首先可以提取所有页面的状态码,并保存到列表中。
>>> codes = []
>>> with open('access_log-0125','r') as f:
... for line in f:
... codes.append(line.split()[8])
再统计出每种状态码的出现次数,保存到字典中:
>>> ret = {}
>>> for i in codes:
... if i not in ret:
... ret[i] = codes.count(i)
...
>>>
>>> ret
{'200': 192, '404': 796, '"-"': 4, '400': 13, '403': 3, '401': 2, '405': 1}
上面这段代码用到了字典,这里是对存放状态码的列表codes进行遍历,从中取出状态码作为字典的键,并统计这种状态码在列表codes中出现的次数,作为字典的值。
如果要统计404页面的比例,可以执行下面的代码:
>>> ret['404']/sum(ret.values())
0.7873392680514342
在这段代码中,ret['404']表示从字典ret中取出键为‘404’的元素的值,也就是404状态码的个数。ret.values()表示取出字典中所有元素的值,再用sum()函数求和,得到所有状态码的总数量。两者的比值也就是错误页面的比例了。
从结果中可以看出,我这个网站的页面出错比例特别高,竟然达到了78.7%,如果是一个正常网站,这肯定是有问题的。但我这并不是一个公开网站,也没有什么有价值的页面,因而大部分访问日志其实都是由一些漏洞扫描软件产生的,这也提醒我们,随时都有人在对我们线上的网站进行着各种扫描测试。
下面我们继续统计出每个页面的用户访问量,并进行排序。
首先仍然是遍历日志文件,取出用户访问的所有页面,并保存到列表中:
>>> webs = []
>>> with open('access_log-0125','r') as f:
... for line in f:
... webs.append(line.split()[6])
接着再统计出每个页面的访问次数,并存放到字典中:
>>> counts = {}
>>> for i in webs:
... if i not in counts:
... counts[i] = webs.count(i)
...
按页面的访问量降序排序:
>>> sorted(counts.items(),key=lambda x:x[1],reverse=True)
[('/', 175), ('/robots.txt', 25), ('/phpinfo.php', 6), ('/Admin13790d6a/Login.php', 4),
……
为了更好地理解上面这个sorted()函数的用法,下面举例说明。比如我们定义一个名叫services的字典,如果直接用sorted()函数对这个字典排序,默认是按照键进行升序排序。为了显示字典中的所有内容,可以使用items()方法,此时,字典中的每个键值对会被组合成一个元组,并且默认是按照元组中的第一个元素,也就是字典的键进行排序的。
>>> services = {'http':80,'ftp':21,'https':443,'ssh':22}
>>> sorted(services)
['ftp', 'http', 'https', 'ssh']
>>> sorted(services.items())
[('ftp', 21), ('http', 80), ('https', 443), ('ssh', 22)]
如果希望按照字典中的值进行排序,也就是要按照元组中的第二个元素排序,可以用key参数指定一个lambda表达式,以每个元组中的第二个元素作为关键字。
>>> sorted(services.items(),key=lambda x:x[1])
[('ftp', 21), ('ssh', 22), ('http', 80), ('https', 443)]
所以这也就解释了之前那个sorted()函数的含义。至于lambda表达式,其实就是一个根据需要可以随时定义使用的小函数,“lambda x:x[1]”,冒号左侧的x是函数要处理的参数,冒号右侧的表达式是函数要执行的操作,最后再将这个表达式的结果返回。
本文属于“Python安全与运维”系列课程的一部分,该系列课程目前已更新到第二部,感兴趣的朋友可以参考:
第一部 Python基本语法 https://edu.51cto.com/sd/53aa7
第二部 Python编程基础 https://edu.51cto.com/sd/d100c
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。