小编说:由于Python在开发效率和高可维护性方法具有很大的优势,因此使用Python进行大数据处理也是一种很好的选择。本文演示了用Python编写脚本对apache日志文件access.log进行处理的过程。模似Hadoop的MapReduce编程模型,对数据进行处理。
大数据通常用来形容一个公司创造的大量非结构化和半结构化数据,这些数据在下载到关系型数据库用于分析时会花费过多的时间和金钱。大数据分析常和云计算联系在一起,因为实时的大型数据集分析需要像MapReduce一样的框架来向数十、数百、甚至数千的电脑分配工作。
在开源领域,Hadoop的发展如日中天。Hadoop旨在通过一个高度可扩展的分布式批量处理系统,对大型数据集进行扫描,以产生结果。
在Hadoop中,可以用两种方式来实现Map/Reduce:
Java的方式,由于Hadoop本身是用Java来实现的,因此,这种方式最为常见。
Hadoop Streaming方式,通过SHELL/Python/ruby等各种支持标准输入\输出的语言实现。
由于Python在开发效率和高可维护性方法具有很大的优势,因此使用Python进行大数据处理也是一种很好的选择。使用Python处理大数据,既减少了学习开发语言的难度,又可以较高的开发效率来完成工作。
本文将演示用Python编写脚本对apache日志文件access.log进行处理的过程。模似Hadoop的MapReduce编程模型,按以下流程对数据进行处理。
首先对大的日志文件进行分割,根据处理计算机的配置,设置一个分割大小的标准,将大的日志文件分割为n份。
将分割出来的较小日志文件分别提交给Map函数进行处理,这时的Map函数可分布在多台计算机中。根据工作量,一个Map函数可以处理多个小日志文件。将处理结果保存为一个文本文件,作为Reduce函数的输入。
将各Map函数处理的结果提交给Reduce函数进行处理,最终得到处理结果。
具体流程如图1所示。
图1 处理流程
提示:按上述流程编写的Python脚本,在测试时可用一个较小的日志文件,最好将日志文件限制在100MB以内进行测试,以减少脚本处理的时间,提高开发测试效率。当测试通过之后,再用其处理大的日志。
日志文件的分割
日志文件很大时,是没办法将其直接打开的,这时就可以考虑将其分割为较小的文件。在分割文件时,需要考虑到处理数据的计算机的内存,如果分割的文件仍然较大,则在处理时很容易造成内存溢出。
在Python中,对于打开的文件,可以逐行读入数据。因此,分割文件的脚本很简单,具体的脚本如下。
在上述脚本中,首先设置了每一个分割文件要保存数据的数量,并设置一个空的列表作为缓存,用来保存分割文件的数据。接着打开大的日志文件,逐行读入数据,再将其添加到缓存列表中,当达到分割文件保存数据的数量时,将缓存列表中的数据写入文件。然后,清空缓存列表,继续从大的日志文件中读入数据,重复前面的操作,保存到第2个文件中。这样不断重复,最终就可将大的日志文件分割成小的文件。
在命令行状态中执行FileSplit.py脚本,将当前目录中的access.log文件分割成小文件,并保存到当前目录的下层access目录中。执行结果如图2所示,从图2中输出的结果可以看出,在将文件大小为27MB的日志文件(约有25万条数据)按每个文件10万条数据进行分割后,将得到3个文件,并且从执行时间来看,在1秒钟之内就完成了对3个文件的分割、保存操作。
图2 分割文件
再打开access目录,可看到分割后得到的小文件,10万条数据文件的大小约在11MB左右,处理这些文件要轻松得多。
图3 分割得到的小文件
编写Map函数处理小文件
得到分割的小文件之后,接下来就需要编写Map函数,对这些小文件进行处理。Map函数最后得到一个小的数据文件,可能经过处理,将11MB大小的文件中的数据进行加工汇总得到一个大小为几百KB的文件。再将这个结果文件交给Reduce进行处理,这样,就可减轻Reduce处理的压力了。
在编写Map函数之前,首先需要明确本次处理的目标是什么,即希望从数据中收集哪些信息。目标不同,Map函数处理的结果将不同。
例如,若需要统计出网站中最受欢迎的页面(即打开次数最多的页面),则在Map函数中就需从每条日志中找出页面(日志的第5部分,包含“方法+资源+协议”,其中的“资源”就是页面地址),将页面提取出来进行统计。
需要注意的是,在一个Map函数中,统计的结果不能作为依据。因此,在这一部分日志文件中,可能A页面访问量最大,但在另一部分日志(可能由另一台计算机的Map函数处理)中,可能B页面的访问量最大。因此,在Map函数中,只能是将各页面的访问量分类汇总起来,保存到一个文件中,交由Reduce函数进行最后的汇总。
下面的脚本就可完成分类汇总页面访问量的工作。
在上面的脚本中,Map函数打开分割后的小日志文件,然后定义了一个空的字典,用字典来保存不同页面的访问量(用页面链接地址作为字典的键,对应的值就是访问量)。
前面介绍过,日志文件中每一条数据可分为7个部分,用空格来隔开。注意,这里最好不用split函数对一条日志进行切分,因为日志某些字段内部可能也会出现空格。因此,最好的方式是使用正则表达式来提取页面地址。
得到页面地址后,接着就判断字典中是否已有此地址作为键,若有,则在该键的值上累加1,表示增加了一次访问。若没有该键,则新建一个键,并设置访问量为1。
当将(分割后的)小日志文件的每条数据都读入并处理之后,字典tempData中就保存了当前这一部分日志文件中所有页面的访问数据了。最后,对字典进行排序(也可不排序)后生成到一个列表中,再将列表保存到一个后缀为“_map.txt”的文件中,完成当前这一部分日志文件的处理,得到一个较小的结果文件。
执行上述脚本,几秒钟时间就可处理完成,在access目录中得到3个后缀为“_map.txt”的文件,如图14-5所示,从执行结果可以看出,经过Map函数的处理,对分割后的约11MB大小的文件进行处理后,得到的结果文件大小为300KB。
图4 Map函数执行结果
编写Reduce函数
Reduce函数进行最后的归集处理。将Map函数运算的结果作为Reduce函数的输入,经过处理,最后得到一个文件,这个文件就是针对大日志文件的处理结果,而不再是一个部分结果了。
Reduce函数的处理流程也很简单,就是读入后缀为“_map.txt”的文件,进行数据的归并处理,最后输出一个结果文件。具体的脚本如下。
上述脚本中,在循环的外面定义了一个空的字典,用来归并所有的页面访问量数据。接着使用os.walk函数循环指定目录中的文件,找到后缀为“_map.txt”的文件进行处理。具体处理过程是,逐个将Map函数的输出文件(后缀为“_map.txt”)读入,并将数据装入字典。然后对字典进行排序并转换为列表,最后将列表输出到文件,即可得到一个后缀为“_reduce.txt”的文件,在这个文件中保存了日志中所有页面的访问量数据。如果只需要获取访问量前10(或前50)的页面,还可以只输出排序后的前10条(或前50条)数据。
经过文件分割、Map和Reduce处理后,即可将原来大小为27MB的文件归集成只有几百KB的一个文件,并得到需要的数据。
Reduce处理得到数据之后,就可以使用Excel或其他常用数据处理软件对数据进行分析、输出图表等操作了。当然,也可以在Python中继续编写脚本来分析这些数据。
上面的操作是以页面访问量为统计目标进行的数据处理操作。如果有其他目标,则需要编写不同的Map和Reduce函数来进行处理。例如,若要统计网站每天不同时段的访问量,则在Map函数中可使用正则表达式提取日志中的访问时间段,并根据一定的规则进行数据统计。在Reduce函数中再根据Map函数的输出数据进行归并处理,即可得到所要的数据。
由于Python脚本的开发效率较高,因此,开发Map、Reduce函数的开发效率也非常高,当统计目标改变后,可以在几分钟就完成对函数的修改,这是其他很多程序设计语言无法做到的。
相关图书
《Python数据分析从入门到精通》
领取专属 10元无门槛券
私享最新 技术干货