最近买了一本《Python宝典》在看,此书所讲Python知识的广度明显,但是深度略显不足,所以比较适合入门及提高级的读者来看。其中对于Python大数据处理一章的内容比较有兴趣,看明白了以后,我根据书上提供的案例对源代码进行了修改,也实现了模拟MapReduce的过程。
目标:从Apache的用户访问日志access.log中统计出页面资源的访问量。我们假设这个文件体积十分巨大。
access中的信息结构:66.249.68.43 - - [04/Aug/2011:01:06:48 +0800] "GET /某页面地址 HTTP/1.1" 200 4100
含义:66.249.68.43 来源ip, [04/Aug/2011:01:06:48 +0800] 日期时间,"GET /某页面地址 HTTP/1.1" 来源方式,200 返回状态,4100 返回字节数
步骤:1、将大文件切割成多个小文件;2、计算每个小文件中的页面访问次数(map过程),每一个小文件对应一个计算结果文件;3、将每个小文件中的计算结果文件进行合并累加,统计出最终的页面资源访问量,结果保存到reduceResult.txt中。
文件结构:
其中,access.log是原始的日志文件,smallFiles中保存分割后的一组小文件,mapFiles中存入每个小文件对应的处理结果文件,reduceResult.txt保存最终的处理结果。
以下是源码:
'''
Created on 2014-12-19
@author: guoxiyue
@file:0fileSplit.py
@function:文件分割
'''
import os,os.path,time;
sourceFile=open('files/access.log','r',encoding='utf8'); #打开原始大文件
targetDir='files/smallFiles'; # 设置小文件的保存目录
smallFileSize=30; #设置每个小文件中的记录条数
tempList=[]; #临时列表,用于暂存小文件中的记录内容
fileNum=1; # 小文件序号
readLine=sourceFile.readline(); #先读一行
while(readLine): #循环
lineNum=1
while(lineNum<=smallFileSize): #控制每个小文件的记录条数不超过设定值
tempList.append(readLine); #将当前行的读取结果保存到临时列表中
readLine=sourceFile.readline(); #再读下一行
lineNum+=1;# 行号自增
if not readLine:break;#如果读到空,则说明大文件读完,退出内层循环
tempFile=open('files/smallFiles/access_'+str(fileNum)+'.txt','w',encoding='utf8');#将读取到的30条记录保存到文件中
tempFile.writelines(tempList);
tempFile.close();
tempList=[];#清空临时列表
print('files/smallFiles/access_'+str(fileNum)+'.txt 创建于 '+str(time.asctime()));
fileNum+=1; #文件序号自增
sourceFile.close();
'''
Created on 2014-12-19
@author: guoxiyue
@file:1map.py
@function:map过程,分别计算每一个小文件中的页面资源访问量
'''
import os,os.path,re,time;
sourceFileList=os.listdir('files/smallFiles/'); #获取所有小文件文件名列表
targetDir="files/mapFiles/"; #设置处理结果保存目录
for eachFile in sourceFileList: #遍历小文件
currentFile=open('files/smallFiles/'+eachFile,'r',encoding='utf8'); #打开小文件
currentLine=currentFile.readline(); #先读一行
tempDict={}; #临时字典
while(currentLine):
p_re=re.compile("(GET|POST)\s(.*?)\sHTTP", re.IGNORECASE); #用正则表达式来提取访问资源
match=p_re.findall(currentLine); #从当前行中提取出访问资源
if(match):
url=match[0][1]; #提出资源页面
if url in tempDict: #获取当前资源的访问次数,并添加到字典中
tempDict[url]+=1;
else:
tempDict[url]=1;
currentLine=currentFile.readline(); #再读下一行
currentFile.close();
#以下是将当前小文件的统计结果排序并保存
tList=[];
for key,value in sorted(tempDict.items(),key=lambda data:data[0],reverse=True):
tList.append(key+' '+str(value));
tList.append('\n')
tempFile=open(targetDir+eachFile,'a',encoding='utf8');
tempFile.writelines(tList);
tempFile.close()
print(targetDir+eachFile+'.txt 创建于 '+str(time.asctime()));
'''
Created on 2014-12-19
@author: guoxiyue
@file:2reduce.py
@function:reduce过程,汇总最终的页面资源访问量
'''
import os,os.path,re,time;
sourceFileList=os.listdir('files/mapFiles/'); #获取小文件的map结果文件名列表
targetFile='files/reduceResult.txt'; # 设置最终结果保存文件
tempDict={}; #临时字典
p_re=re.compile('(.*?)(\d{1,}$)', re.IGNORECASE); #利用正则表达式抽取资源访问次数
for eachFile in sourceFileList:#遍历map文件
currentFile=open('files/mapFiles/'+eachFile,'r',encoding='utf8'); #打开当前文件
currentLine=currentFile.readline(); #读一行
while(currentLine):
subData=p_re.findall(currentLine) #提取出当前行中资源的访问次数
if(subData[0][0] in tempDict): #将结果累加
tempDict[subData[0][0]]+=int(subData[0][1]);
else:
tempDict[subData[0][0]]=int(subData[0][1]);
currentLine=currentFile.readline();#再读一行
currentFile.close();
#以下是将所有map文件的统计结果排序并保存
tList=[];
for key,value in sorted(tempDict.items(),key=lambda data:data[1],reverse=True):
tList.append(key+' '+str(value));
tList.append('\n')
tempFile=open(targetFile,'a',encoding='utf8');
tempFile.writelines(tList);
tempFile.close()
print(targetFile+' 创建于 '+str(time.asctime()));