硬件条件:内存8G,可用内存7.76G,四核
解析目标:每个文件均为.xml.gz
尝试调库解析XML
从头到尾尝试过了使用pandas.read_xml和lxml进行解析。这两个库都很方便,并且可以直接对于.xml.gz进行解析(还挺高级的),但是在遇到大文件的时候,程序会自动停止。
import pandas as pd
import os
file_name1 = 'smallfile.xml.gz'
file_name2 = 'bigfile.xml.gz'
#short file
print(os.path.getsize(file_name2)/1024/1024,'MB')
test1 = pd.read_xml(file_name2)
print(file_name2,'done!')
#long file
print(os.path.getsize(file_name1)/1024/1024,'MB')
test2 = pd.read_xml(file_name1)
print(file_name1,'done!')
#返回结果
0.09057044982910156 MB
smallfile.xml.gz done!
87.88075542449951 MB
#xml解析
import pandas as pd
import os
file_name1 = 'smallfile.xml.gz'
file_name2 = 'bigfile.xml.gz'
#short file
print(os.path.getsize(file_name2)/1024/1024,'MB')
test1 = etree.parse(file_name2)
print(file_name2,'done!')
#long file
print(os.path.getsize(file_name1)/1024/1024,'MB')
test2 = etree.parse(file_name1)
print(file_name1,'done!')
#返回结果
0.09057044982910156 MB
smallfile.xml.gz done!
87.88075542449951 MB
直接调库应该是不行了,所以得重新看看数据
解压后处理
首先是先把XML能解析了再说,这个时候意识到是压缩文件了,所以就试试解压之后处理行不行
import os
import gzip
file_name2 = '/home/laimingshu/airflow/dataset/AIndexEODPrices_backup/706804130#3#AIndexEODPrices_D_20240619_181751000247.xml.gz'
#long file
print(os.path.getsize(file_name1)/1024/1024,'MB')
test2 = gzip.GzipFile(file_name1).read().decode('utf-8')
print(file_name1,'done!')
with open('test.txt','w') as f:
f.write(test2)
print(os.path.getsize('test.txt'))
print(file_name1,'done!')
#返回结果
87.88075542449951 MB
bigfile.xml.gz done!
777.7352848052979 MB
import os
import lxml
import pandas as pd
import numpy as np
from lxml import etree
from pprint import pp
import gzip
import re
class GetXmlData:
def __init__(self,file_root,columns):
self.result_dict = {}
self.file_root = file_root
self.file_list = os.listdir(file_root)
self.tmp_dict = dict()
self.analyze_result = []
for col in columns:
self.tmp_dict[col] = ''
for file in self.file_list:
# method1
# tmp_content = self._analyze(os.path.join(self.file_root,file))
# tmp_content = re.findall('<Product[^>]*>[\w\W]*<\/Product>',tmp_content)
# if len(tmp_content)>0:
# tmp_content = tmp_content[0].split('</Product>')
# self.analyze_result.append(tmp_content)
# method2
tmp_content = self._analyze(os.path.join(self.file_root,file))
tmp_content = tmp_content.split('\n')[57::]
start,end,length = 0,0,len(tmp_content)
for tmp in tmp_content:
if '<Product>' in tmp:start==end
elif '</Product>' in tmp:
self.analyze_result.append(tmp_content[start+1:end])
else: end += 1
print(file)
def _analyze(self,file):
return gzip.GzipFile(filename=file,mode='r').read().decode('utf-8')
if __name__ == '__main__':
file_path = "bigfile.xml.gz"
test_xml = GetXmlData('folder_root',answer_columns)
#return result
压缩率真高(此时只是中等大小的数据文件)
用htop 命令看一下内存占用情况,看来还是会内存爆掉,最多就是能解析出来xml了,所以找了外援问一下怎么解决
流式加载
问了外援之后,继续解决。
内存不够用的话,解决方式有两种,一种是分块加载,一种是流式加载
目标文件的结构大致分成三个部分:文件信息、内容信息和数据内容,前两个部分的固定行数的头文件,第三部分是每个块以及总长度都不固定的数据内容。
而分块加载,需要每次加载固定长度的内容,流式加载可以进行每次加载的量的控制。
所以对于这份数据,每个块长不固定的情况而言,流式加载更加合适。
import pandas as pd
import gzip
from pathlib import Path
class GetXmlData:
def __init__(self,file_root,columns=''):
self.result_dict = {}
self.file_root = file_root
self.file_list = Path(file_root).glob("*.gz")
self.tmp_dict = dict()
self.analyze_result = []
for col in columns:
self.tmp_dict[col] = ''
self.__debug_freq = 0
def do(self):
for file in self.file_list:
gzip_file = gzip.GzipFile(filename=file,mode='r')
line_number = -1
gzip_line_swap = []
gzip_swap_enable_flag = False
gzip_convert_hook_flag = False
while True:
gzip_line = gzip_file.readline().decode()
line_number += 1
if not gzip_line:
break
if line_number < 57:
continue
if gzip_line.strip() == "<Product>":
gzip_swap_enable_flag = True
continue
if gzip_line.strip() == "</Product>":
gzip_swap_enable_flag = False
gzip_convert_hook_flag = True
if gzip_swap_enable_flag:
gzip_line_swap.append(gzip_line.strip())
if gzip_convert_hook_flag:
self._convert_hook(gzip_line_swap)
gzip_convert_hook_flag = False
gzip_line_swap = []
def _convert_hook(self, gzip_line_swap: list):
if self.__debug_freq < 10:
print(gzip_line_swap)
self.__debug_freq += 1
else:
pass
if __name__ == '__main__':
folder_root = ''
file_path = "bigfile.xml.gz"
test_xml = GetXmlData(folder_root,answer_columns)
test_xml.do()
外援指导还是不一样,哎~最高的内存消耗都没有上过4G,基本都是稳定在3G左右
后续就是我自己处理数据了。
多线程
sing_process = multiprocessing.Process(target=test_xml.do(),group=None)
sing_process.start()
函数执行的时候加一个多线程会更快
同一个文件,单线程处理需要六分钟+,多线程会降低到两分半,所以还是很有效的。