我正在写一个解决方案,以便从文件中提取信息。 这些文件是通过Windows Event Utility命令中的其他脚本生成的(我不调用,只是接收文件进行解析):
wevtutil qe Application /q:"*[System[Provider[@Name='NameOfTheSourceApplication']]]" >> %FILE%
此命令将有关源应用程序的所有输出保存到转储文件中,最终每一行的每个 event 都有一个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-7GB以上)。因此,我使用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代表“全局列表”)。
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))
然后,我只加载一个要在lxml
中解析的XML文件:
def loadXml(fileobj):
tree = etree.parse(fileobj)
print("Processing : %s" % (fileobj))
return tree
这是我的瓶颈,因为在我只寻找Event Data
和我的Event Time
时,我没有找到任何其他便捷的方法来有效地解析文件。找到数据后,我将发现附加到两个单独的列表中(一个用于事件数据,一个用于事件时间),随后我将其转换为简单的CSV文件,以便继续通过{{1}进行解析}。
此代码实际上适用于2GB以下的文件,但在解析2GB以上的文件时会完全用尽内存,我的解决方案必须在只有2-3GB可用RAM(Windows 64位台式机)的系统中运行。
pandas
我尝试在解析后手动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
文件对象,但这似乎没有任何作用,并且python会继续建立内存直到PC崩溃。
del
CSV创建(zlg代表itertools-zip_longest):
def initParser(glst,DataL,DataTimeL):
for file in glst:
root = loadXml(file)
parser(root,DataL,DataTimeL)
gc.collect()
del file
我已经尝试过使用TinyDB,ZODB,这听起来有些过分,但它太慢了,或者我做错了。手动将事件转储到CSV速度非常慢。
由于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))
循环解析器功能实际上对于2GB以下的文件非常有效,因此我想找到一种安全高效地附加这些大列表的方法,而不会崩溃整个系统。
谢谢。
答案 0 :(得分:0)
这是一个概念证明,它使用迭代器读取一个大文件,该文件由带有独立XML的行组成,并将特定字段提取到CSV文件中。对其进行修改以适合您的需求。
import csv
import itertools
import typing
from io import StringIO
from xml.etree import ElementTree
from xml.etree.ElementTree import Element
def grouper(iterable, n, fill=None) -> typing.Iterator:
args = [iter(iterable)] * n
return itertools.zip_longest(*args, fillvalue=fill)
def parse_event_xml(event_xml: str) -> dict:
root: Element = ElementTree.fromstring(event_xml)
namespaces = {'ns': 'http://schemas.microsoft.com/win/2004/08/events/event'}
time_el = root.find('./ns:System/ns:TimeCreated', namespaces=namespaces)
data_el = root.find('./ns:EventData', namespaces=namespaces)
return {
'Event Time': time_el.attrib['SystemTime'],
'Event Data': data_el.text,
}
def process_batch(batch: typing.Iterator[str], batch_filename: str) -> None:
fields = ['Event Time', 'Event Data']
with open(batch_filename, 'w', newline='') as bf:
writer = csv.DictWriter(bf, fieldnames=fields)
writer.writeheader()
for item in batch:
if not item: # skip empty lines
continue
parsed = parse_event_xml(item)
writer.writerow(parsed)
if __name__ == '__main__':
xml_raw = '''<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></Keywords><TimeCreated SystemTime='2018-10-02T11:19:41.000000000Z'/><EventRecordID></EventRecordID><Channel>Application</Channel><Computer></Computer><Security/></System><EventData>DATA<Data></Data></EventData></Event>
<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></Keywords><TimeCreated SystemTime='2018-10-02T11:19:41.000000000Z'/><EventRecordID></EventRecordID><Channel>Application</Channel><Computer></Computer><Security/></System><EventData>DATA<Data></Data></EventData></Event>'''
batch_size = 10 # lines / events
# read the event stream
# normally you'd use `with open(filename, encoding='utf-8')`
# but here i'm reading from a string
with StringIO(xml_raw) as f:
for i, batch in enumerate(grouper(f, batch_size)):
batch_filename = f'batch_{i}.csv'
process_batch(batch, batch_filename)
调整parse_event_xml
函数以提取所需的数据,这里我只使用了EventTime
和EventData
此文件输出如下的csv文件:
Event Time,Event Data
2018-10-02T11:19:41.000000000Z,DATA
2018-10-02T11:19:41.000000000Z,DATA