Я пишу решение для извлечения информации из файла.
Эти файлы создаются командой Windows Event Utility с помощью другого сценария (я не вызываю, просто получаю файл для анализа):
wevtutil qe Application /q:"*[System[Provider[@Name='NameOfTheSourceApplication']]]" >> %FILE%
Эта команда сохраняет все выходные данные, относящиеся к исходному приложению, запрошенные в файл дампа, в конце концов, для каждой события в каждой строке есть XML. Все, что меня волнует, это EventData
и TimeCreated SystemTime
.
Пример вывода:
<?xml version="1.0" encoding="UTF-8"?>
<Event xmlns="http://schemas.microsoft.com/win/2004/08/events/event">
<System>
<Provider Name="" />
<EventID Qualifiers="">0</EventID>
<Level>4</Level>
<Task>0</Task>
<Keywords />
<TimeCreated SystemTime="2018-10-02T11:19:41.000000000Z" />
<EventRecordID />
<Channel>Application</Channel>
<Computer />
<Security />
</System>
<EventData>
DATA
<Data />
</EventData>
</Event>
Когда дамп файла закончен, файл может быть довольно большим (больше 6-7 ГБ). Поэтому я использую утилиту Linux iconv
, чтобы изменить кодировку исходного файла с UTF-16/UCS2-LE
(кодировка по умолчанию wevutil) на UTF-8
, новая кодировка уменьшает почти половину размера файла. Затем я использую рецепт grouper
в сочетании с простой функцией разделения файлов, чтобы разделить большой дамп-файл на файлы меньшего размера:
def grouper(n, iterable, fillvalue=None):
"""Collect data into fixed-length chunks or blocks"""
# grouper(3, 'ABCDEFG', 'x') --> ABC DEF Gxx
args = [iter(iterable)] * n
return zlg(fillvalue=fillvalue, *args)
def splitter(fileobj,outName,ranoutName,rencode,wencode):
with open(fileobj,"r",encoding='UTF-8',errors='replace') as f:
for i, g in enumerate(grouper(n, f, fillvalue=''), 1):
with open('{0}_{1}.xml'.format(i,outName), 'w',encoding=wencode) as fout:
fout.writelines(g)
print("Splitting file : %s" % (fileobj))
Поскольку эти файлы на самом деле не являются файлами XML, но каждая строка отформатирована как xml с пространством имен, я добавляю корневой тег к каждому разделенному файлу один за другим для последующего анализа lxml
(glst обозначает «globbed») список ").
def rooter(glst):
for logFiles in glst:
oFile = open(logFiles,'r',encoding='utf-8')
rFile = oFile.read()
wFile = open(logFiles,'w',encoding='utf-8')
wFile.write('<root>')
wFile.write(rFile)
wFile.write('</root>')
oFile.close()
wFile.close()
print("Rooting XML : %s" % (logFiles))
Затем я загружаю только один файл XML для анализа в lxml
:
def loadXml(fileobj):
tree = etree.parse(fileobj)
print("Processing : %s" % (fileobj))
return tree
Вот мое узкое место, поскольку я не нашел другого удобного метода для эффективного анализа файла, пока я ищу только Event Data
и мой Event Time
. После нахождения данных я добавляю свои результаты в два отдельных списка (один для данных о событии, один для времени события), которые я позже преобразовываю в простой файл CSV, чтобы продолжить мой анализ с помощью pandas
.
Этот код на самом деле работает с файлами размером менее 2 ГБ, но при запуске синтаксического анализа файлов размером более 2 ГБ не хватает памяти, мое решение должно работать в системе, имеющей всего 2-3 ГБ свободной оперативной памяти (64-разрядный рабочий стол Windows).
def parser(tree,DataL,DataTimeL):
for evts in tree.iter('{%s}EventData' % nameSpace):
EvtData = evts.find('{%s}Data' % nameSpace).text
DataL.append(EvtData)
for evtSysTime in tree.iter('{%s}System' % nameSpace):
eSysTime = evtSysTime.find('{%s}TimeCreated' % nameSpace).attrib
DataTimeL.append(eSysTime)
break
Я попытался вручную gc.collect
и del
объект файла после синтаксического анализа, но это, кажется, не имеет никакого эффекта, и python продолжает наращивать память, пока не происходит сбой ПК.
def initParser(glst,DataL,DataTimeL):
for file in glst:
root = loadXml(file)
parser(root,DataL,DataTimeL)
gc.collect()
del file
Создание CSV (zlg означает itertools - zip_longest):
with open('LogOUT.csv', 'w', encoding="UTF-8", newline='') as cF:
wr = csv.writer(cF)
wr.writerow(("Event", "Event Time"))
wr.writerows(zlg(EvtL,EvtTimeL))
Я пытался использовать TinyDB, ZODB, который звучит как перебор, также он слишком медленный или, возможно, я делаю это неправильно. Выгрузка событий в CSV вручную выполняется очень медленно.
Поскольку функция парсера цикла for
на самом деле очень эффективна для файлов размером менее 2 ГБ, я хотел бы найти способ безопасного и эффективного добавления этих больших списков без сбоев всей системы.
Заранее спасибо.