Как я могу улучшить мой метод парсинга lz4 сжатого json?
Я анализирую очень большие (от 5 ГБ до 2 ТБ) сжатые файлы JSON и сохраняю некоторые данные в файлы CSV с помощью приведенного ниже алгоритма. Это работает, но является противоположностью эффективности из-за наличия трех вложенных циклов.
Я также не уверен в стоимости нескольких строк кода из-за того, что незнаком с библиотеками json и yaml, предоставляемыми python:
k = yaml.load(json.dumps(v))
Если вы не заметили, я уже позвонил yaml.load()
Функция над этой линией с:
header = yaml.load(json.dumps(header))
Кажется, мне пришлось дважды вызывать функцию, потому что внутренние листья (значения) ключей из header
были интерпретированы как строки.
Когда я просто распечатываю значение v в этой строке: for k, v in header.iteritems():
вывод обычно выглядит как одна из следующих строк:
[{'value': ['4-55251088-0 0NNN RT(1535855435726 0) q(0 -1 -1 -1) r(0 -1)'], 'key': 'x_iinfo'}]
[{'value': ['timeout=60'], 'key': 'keep_alive'}, {'value': ['Sun, 02 Sep 2018 02:30:35 GMT'], 'key': 'date'}]
[{'value': ['W/"12765-1490784752000"'], 'key': 'etag'}, {'value': ['Sun, 02 Sep 2018 02:27:16 GMT'], 'key': 'date'}]
[{'value': ['Sun, 02 Sep 2018 02:30:32 GMT'], 'key': 'date'}]
в общем, если в нашем файле есть категория с именем "неизвестно", то есть дерево json, включающее в себя все без определенной категории.
Есть ли лучший способ получить все эти значения без замедления алгоритма, добавив еще два цикла?
Полный метод источника:
def convertJsonHeadersToCSV(jsonFilePath, CSVFilePath,portNum, protocol):
try:
bodyPattern = re.compile('<(html|!DOCTYPE).*$', re.IGNORECASE | re.MULTILINE)
csvFile = open(CSVFilePath, 'w')
print("Converting " + protocol + " file to csv, please wait...")
spinner.start()
csvWriter = unicodecsv.writer(csvFile)
csvWriter.writerow(['ip', 'date', 'protocol', 'port', 'data'])
chunk_size = 128 * 1024 * 1024
with lz4.frame.open(jsonFilePath, 'r') as f:
for line in f:
try:
text = ""
jsonData = json.loads(line)
ts = jsonData['timestamp'][:10]
ip = jsonData['ip']
data = jsonData['data']['http']
if 'response' in data:
if 'headers' in data['response']:
header = jsonData['data']['http']['response']['headers']
header = yaml.load(json.dumps(header))
for k, v in header.iteritems():
if 'unknown' in k:
#print(v)
k = yaml.load(json.dumps(v))
for i in k:
#print(str(i['key']) + ": "+str(i['value']) + "\r\n")
text = text + str(str(i['key']) + ": "+str(i['value']) + "\r\n")
else:
text = text + str(str(k) + ": "+str(v) + "\r\n")
#csvWriter.writerow([ip, ts, protocol, portNum, text])
except:#sometimes will run into a unicode error, still working on handling this exception.
pass
csvFile.close()
spinner.stop()
print("Completed conversion of " + protocol + " file.")
except Exception as ex:
spinner.stop()
traceback.print_exc()
print("An error occurred while converting the file, moving on to the next task...")
1 ответ
Что бы ускорить это чрезвычайно наверняка будет прекратить использование text
в виде строки, потому что эти строки:
text = text + str(str(i['key']) + ": "+str(i['value']) + "\r\n")
else:
text = text + str(str(k) + ": "+str(v) + "\r\n")
выполняем конкатенацию строк. Поскольку строки являются неизменяемыми, новая копия должна быть сделана каждый раз (даже с text +=
вместо text = text +
, так что это не поможет), и чем больше строка для копирования, тем медленнее (квадратичная сложность).
Было бы лучше:
- определять
text
как пустой список - добавить в список
- использование
"".join
в конце
так
for line in f:
try:
text = [] # define an empty list at start
jsonData = json.loads(line)
затем (используя str?format
Также было бы улучшение здесь, но это незначительно)
text.append(str(str(i['key']) + ": "+str(i['value']) + "\r\n"))
else:
text.append(str(str(k) + ": "+str(v) + "\r\n"))
и в конце концов "мутировать" text
в строку, как это:
text = "".join(text)
или просто
csvWriter.writerow([ip, ts, protocol, portNum, "".join(text)])