iterparse无法解析某个字段,而其他类似字段正常
我在用Python的iterparse
来解析一个nessus扫描的XML结果文件(.nessus文件)。但是在解析时,遇到了一些意外的记录,导致解析失败,而类似的记录却能正常解析。
这个XML文件的结构大致是这样的:里面有很多记录,比如下面这个:
<ReportHost>
<ReportItem>
<foo>9.3</foo>
<bar>hello</bar>
</ReportItem>
<ReportItem>
<foo>10.0</foo>
<bar>world</bar>
</ReportHost>
<ReportHost>
...
</ReportHost>
换句话说,就是有很多主机(ReportHost
),每个主机有很多要报告的项目(ReportItem
),而这些项目又有几个特征(foo
、bar
)。我想生成每个项目一行,并列出它的特征。
解析在文件中间的某一行失败了,这一行很简单(在这个例子中,foo
的值是cvss_base_score
)。
<cvss_base_score>9.3</cvss_base_score>
而在此之前,大约有200行类似的记录都能正常解析。
下面是相关的代码片段——它设置了一些上下文标记(inReportHost
和inReportEvent
),这些标记告诉我当前在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
的值会是None(在cvss = elem.text
赋值之后),尽管在文件的早些地方,相同的条目都能正常解析。
如果我在赋值后加上类似下面的代码:
if cvss is None: cvss = "0"
那么后面很多cvss
就能正确赋值(但有些仍然是None)。
当我把导致解析错误的<ReportHost>...</reportHost>
提取出来,放到程序里运行时,它又能正常工作(也就是说,cvss
被赋值为9.3
,正如预期的那样)。
我现在搞不清楚代码哪里出错了,因为在一大堆类似的记录中,有些能正确处理,有些却不行(有些记录是完全相同的,但处理结果却不同)。我也找不到那些失败记录有什么特别之处——之前和之后的相同记录都能正常处理。
1 个回答
来自 iterparse() 文档 的内容:
注意: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)