记一次800多万XML文本文件预处理经历
一.背景
由于某些需求,现需对系统在最近几个月生成的xml文件进行预处理,提取<text>标签内的数据进行分析。这些需要预处理的数据大概有280GB左右880多万,存放在gysl目录下,gysl的下一层按天命名,分为若干个目录,接下来一层目录下又有多个目录,我们所需的xml目录就在这一层。我们现在需要将此目录下面的xml文件使用Python脚本进行处理,并将处理结果按天(与源文件一致)保存到~/temp目录下。 二.操作过程2.1 Python脚本准备。 #!/usr/bin/python3 # -*- coding:utf-8 -*- import glob,os,sys,re from concurrent.futures import ProcessPoolExecutor import argparse import random def find_xs(str,list): i = 0 for i in range(0,len(str)): if str[i] in list: return i return -1 def segement_aux(para,OUT,sep_list,max_length,merge_prob): pos = 0 res_sentence = "" sentence_size = 0 while True: #segment one line. pos = find_xs(para,sep_list) if pos == -1: break cur_sentence = para[:pos+1].strip() cur_sentence_filtering = cur_sentence res_sentence = res_sentence + cur_sentence_filtering sentence_size = sentence_size + 1 if sentence_size == 2: OUT.write(res_sentence + ‘n‘) else: rand = random.random() if rand > merge_prob: OUT.write(res_sentence + ‘n‘) res_sentence = "" sentence_size = 0 else: sentence_size = sentence_size + 1 para = para[pos+1:] def loadXMLfile(inputfile): sep_list= [‘。‘,‘?‘,‘!‘] targetfolder = "Target" max_length = 100000 merge_prob = 0.6 basefilename =os.path.basename(inputfile) outputfile = targetfolder + ‘/‘+ os.path.splitext(basefilename)[0] + ".tsv" textSessionPattern = re.compile(r‘<text>(.*?)</text>s*‘,re.I|re.MULTILINE) OUT = open(outputfile,‘w‘,encoding="utf8") for line in open(inputfile,encoding="utf8"): line = line.strip().rstrip("rn") alltextsessions = re.findall(textSessionPattern,line) for textsession in alltextsessions: textsession = re.sub(r‘t‘,r‘ ‘,textsession) textsession = re.sub(r‘&;[^&;|&;]+&;‘,r‘‘,textsession) textsession = re.sub(r‘{[^}|{]+}‘,textsession) textsession = re.sub(r‘s+‘,textsession) OUT.write(textsession+‘n‘) OUT.close() return outputfile def processXMLfilesWithThreads(input): with ProcessPoolExecutor() as executor: xmlfiles = glob.glob(input + "/*.xml") for xmlfile,outputfile in zip( xmlfiles,executor.map( loadXMLfile,xmlfiles) ): print("Processed" + outputfile) if __name__ == ‘__main__‘: ap = argparse.ArgumentParser() ap.add_argument("-i","--input",required=True,help="the directory of input files") args = ap.parse_args() processXMLfilesWithThreads(input = args.input) 2.2 Shell脚本准备。 for folder in `find /home/ivandu/gysl -path "*/xml" -print`;do mkdir Target;python3 XMLPreProcess.py --input $folder;mv Target ~/temp/$(echo $folder|awk -F "/" ‘{print $5}‘);done 看着有点费劲,改一下: #!/bin/bash for folder in `find /home/ivandu/gysl -path "*/xml" -print`; do mkdir Target; python3 XMLPreProcess.py --input $folder; mv Target ~/temp/$(echo $folder|awk -F "/" ‘{print $5}‘); done 2.3 执行脚本。 此步骤就不展示了,涉及到某些商业机密。在Python脚本所在的目录下,执行Shell脚本就行。 三.遇到问题3.1 “Argument list too long”。 在执行某目录下的文件移动、复制、删除操作时,发现提示“Argument list too long”,命令执行不成功。这个目录下的文件数量超过30万,操作命令如下: cp * ~/gysl_test 提示: -sh: /bin/cp: Argument list too long 解决方案: for file in `ls`;do cp $file ~/gysl_test/;done rm,mv也类似,太忙了也就没继续探讨其他解决方案。 3.2 双for循环执行效率太低。 for folder in `find /home/ivandu/gysl/ -mindepth 2 -maxdepth 2 -print|grep "xml"`;do cd $folder;for file in $(ls);do cp $file ~/gysl_test/;done;done 如果使用这命令来拷贝这些文件的话,那么这一天可能就过去了!这个肯定不妥,必须改进。find也用得不够简洁,后来都进行优化了。 3.3 Python单线程执行效率太低。 Python脚本使用了多线程来进行处理。不过多解释,大家见谅^_^ 四.总结4.1 总体来说今天处理这些数据还是挺给力的,差不多5000秒就完成了。我写了一条命令动态观察了一下。 4.2 正则表达式随时都能用上,要不是处理一下特殊任务和Python多线程,直接一个grep命令写到shell命令或许早就完事了。 4.3 多线程。bash shell中的多线程还不会使用,以后还得加强学习一下。 4.4 就写这么多了,不足之处还望诸位不吝赐教。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |