2013-02-03 47 views
2

我使用Python的iterparse解析了nessus扫描(.nessus文件)的XML结果。解析失败的意外记录,威尔相似的人已被正确解析。iterparse无法解析字段,而其他类似的字段不正确

XML文件的一般结构是很多类似下面的记录:

<ReportHost> 
    <ReportItem> 
    <foo>9.3</foo> 
    <bar>hello</bar> 
    </ReportItem> 
    <ReportItem> 
    <foo>10.0</foo> 
    <bar>world</bar> 
</ReportHost> 
<ReportHost> 
    ... 
</ReportHost> 

换句话说大量主机(ReportHost)有很多项目的报告(ReportItem),和后者具有几个特征(foobar)。我将考虑为每个产品生成一行,并具有其特征。

解析在文件的中间处(在这种情况下是cvss_base_scorefoo)一个简单的线路发生故障

<cvss_base_score>9.3</cvss_base_score> 

同时〜200条类似线已没有问题解析。

相关的一段代码如下 - 它集上下文标记(inReportHostinReportEvent这告诉我,在XML文件中我在的狭窄,并且或者分配或打印一个值,根据上下文)

import xml.etree.cElementTree as ET 
inReportHost = False 
inReportItem = False 

for event, elem in ET.iterparse("test2.nessus", events=("start", "end")): 
    if event == 'start' and elem.tag == "ReportHost": 
     inReportHost = True 
    if event == 'end' and elem.tag == "ReportHost": 
     inReportHost = False 
     elem.clear() 
    if inReportHost: 
     if event == 'start' and elem.tag == 'ReportItem': 
      inReportItem = True 
      cvss = '' 
     if event == 'start' and inReportItem: 
      if event == 'start' and elem.tag == 'cvss_base_score': 
       cvss = elem.text 
     if event == 'end' and elem.tag == 'ReportItem': 
      print cvss 
      inReportItem = False 

cvss有时具有无值(cvss = elem.text分配后),即使相同的条目已经在文件中properely较早解析。

如果我添加了assignement下面的东西沿着

if cvss is None: cvss = "0" 

线则许多进一步的cvss解析转让他们正确的价值观(和其他一些是没有的)。

当采取<ReportHost>...</reportHost>导致错误的解析并运行它通过程序 - 它工作正常(即cvss被分配9.3按预期)。

我迷失在我犯我代码错误的地方,因为有一大堆类似的记录,一些apre处理正确,有些不 - (某些记录是相同的,而且处理方式不同)。我也找不到任何关于失败记录的特别之处 - 早期和晚期都是一样的。

回答

3

iterparse() docs

注:iterparse()只保证它已经看到了“>”字符时,它发出一个“开始”事件的起始代码的 ,所以属性是 定义,但是文本和尾部属性的内容在此处未定义为 。这同样适用于元素儿童; 他们可能或可能不存在。如果您需要一个完全填充的元素,则可以使用 来查找“结束”事件。

删除inReport*变量和进程ReportHost只在完成解析时的“结束”事件。使用ElementTree API从当前ReportHost元素获取必要的信息,例如cvss_base_score

要保留内存,这样做:

import xml.etree.cElementTree as etree 

def getelements(filename_or_file, tag): 
    context = iter(etree.iterparse(filename_or_file, events=('start', 'end'))) 
    _, root = next(context) # get root element 
    for event, elem in context: 
     if event == 'end' and elem.tag == tag: 
      yield elem 
      root.clear() # preserve memory 

for host in getelements("test2.nessus", "ReportHost"): 
    for cvss_el in host.iter("cvss_base_score"): 
     print(cvss_el.text) 
+0

谢谢你 - 这是真的很有帮助。我更新了XML文件的示例以更好地反映现实(每台主机有几个项目)。我将尝试围绕您的想法构建我的代码,可能有两个循环(一个用于主机,然后用于主机中的项目),但我首先必须清楚地了解迭代的工作原理。 – WoJ

+0

@WoJ:'.iter()'方法是递归的,即无论有多少'ReportItem';所有'cvss_base_score'都可以在他们所属的任何“ReportItem”中找到(或者即使“cvss_base_score”元素在任何“ReportItem”之外)。 – jfs

+0

@ j-f-sebastian:我明白了,但我也需要知道他们属于哪个ReportItem。我试图输出表单行(以XML为例)'host1,foo =“9.3”,bar =“hello”'和'host2,foo =“10.0”,bar =“world”'。现在我已经清楚(从你的例子)如何提取字段,但我需要保持它们在上下文中(将它们链接到它们所属的项目) – WoJ