【发布时间】:2019-07-01 12:58:00
【问题描述】:
我正在编写一个解决方案,以便从文件中提取信息。 这些文件是通过其他脚本在 Windows 事件实用程序命令中生成的(我没有调用,只是接收文件进行解析):
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-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 代表“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))
然后我在lxml中只加载一个要解析的XML文件:
def loadXml(fileobj):
tree = etree.parse(fileobj)
print("Processing : %s" % (fileobj))
return tree
这是我的瓶颈,因为我没有找到任何其他方便的方法来有效地解析文件,而我只寻找Event Data 和我的Event Time。找到数据后,我将我的发现附加到两个单独的列表(一个用于事件数据,一个用于事件时间),稍后我将其转换为简单的 CSV 文件,以便通过pandas 继续解析。
此代码实际上适用于 2GB 以下的文件,但在解析 2GB 以上的文件时会完全耗尽内存,我的解决方案必须在只有 2-3GB 可用 RAM 的系统中运行(Windows 64 位桌面)。
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一直在建立内存,直到PC崩溃。
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 循环解析器函数实际上对 2GB 以下的文件非常有效,因此我想找到一种方法来安全有效地附加这些大列表,而不会导致整个系统崩溃。
提前致谢。
【问题讨论】:
-
你熟悉 Python 中的生成器/迭代器表达式吗?无需在列表/变量中累积数据或完全读取文件,您可以逐行(或任何最小的工作单元)处理它,而无需完全使用 RAM。
-
嗨,我是一个新手 python 程序员,我知道生成器/迭代器表达式和产量,只是不知道如何重塑它以实现我的目标,这基本上是解析这个大文件为了节省大量使用文本编辑器的手动工作。