Python 用lxml解析结构怪异的XML
我有许多需要解析的XML文件。我已经写了一些代码,这些代码很有效,但是很难看,我想从比我更有XML经验的人那里得到一些建议 首先,我可能在错误的上下文中使用了一些术语,因为我对XML的经验有限。元素,除非另有说明,我的意思是:Python 用lxml解析结构怪异的XML,python,xml,parsing,lxml,Python,Xml,Parsing,Lxml,我有许多需要解析的XML文件。我已经写了一些代码,这些代码很有效,但是很难看,我想从比我更有XML经验的人那里得到一些建议 首先,我可能在错误的上下文中使用了一些术语,因为我对XML的经验有限。元素,除非另有说明,我的意思是: <root> <element> ... </element> <element> ... </element> </root> <root> &
<root>
<element>
...
</element>
<element>
...
</element>
</root>
<root>
<element>
<text_attribute>Some Text</text_attribute>
<attribute var="blah"/>
<bool_attribute><boolean.true/></bool_attribute>
</element>
<element>
<text_attribute>Some more Text</text_attribute>
<attribute var="blah again"/>
<bool_attribute><boolean.false/></bool_attribute>
</element>
</root>
[
{ "text_attribute":"Some Text", "attribute":"blah", "bool_attribute":True },
{ "text_attribute":"Some more Text", "attribute":"blah again", "bool_attribute":False }
]
其中“Person”和“Car”是标记,“Id”和“Color”是属性名
这使得迭代所有元素和检查每个标记变得很容易,如果字典中有匹配项(如果dict['file1.xml']['attributes']),则提取值
正如我所说,代码是有效的,但我不喜欢我的解决方案。此外,并非所有元素都具有所有子元素(例如,一个人可能没有汽车,那么该标记将完全丢失),并且我需要将这些值赋值为“无”。现在,我获取每个文件中每个元素应该存在的所有标记,将它们转换为一个集合,然后检查它们与我实际为该元素提取值的标记集合之间的差异。同样,代码非常难看
希望这种混乱有一定道理
编辑:
我使用了J.F.Sebastian的建议,将每个值的xpath存储在一个字典中,字段名作为键,xpath作为值。我不认为#3是合法的XML,因为没有与之关联的开始标记,即使它在其他地方,也不会正确嵌套在该示例中。表达式将被解释为结束标记,因为您可以使用与元素相关的xpath
表达式来简化输入代码,而不是使用复杂的数据结构,例如#1-4种情况:
标记/文本()
标记/@属性
名称(DTBoolean/*[1])
(tag1 | tag2)/*/text()
使用什么样的输出数据结构取决于您希望在以后的代码中如何使用它。您可以从最适合当前代码的结构开始。并在以后更好地理解需求时将其发展为更通用的解决方案
我将其输出到csv,其中每个元素都是csv文件中的一行。
...
我使用defaultdict存储元素,然后在将它们输出到csv之前将它们存储在列表中
您可以使用普通的dict和csv.DictWriter(fieldnames=xpathdict.keys()):
其中,xpathdict
是字段名和相应xpath表达式之间的映射。出于一般性考虑,您可以存储函数对象f(element)->csv字段,而不是/除了xpath表达式。我假设您希望这样:
<root>
<element>
...
</element>
<element>
...
</element>
</root>
<root>
<element>
<text_attribute>Some Text</text_attribute>
<attribute var="blah"/>
<bool_attribute><boolean.true/></bool_attribute>
</element>
<element>
<text_attribute>Some more Text</text_attribute>
<attribute var="blah again"/>
<bool_attribute><boolean.false/></bool_attribute>
</element>
</root>
[
{ "text_attribute":"Some Text", "attribute":"blah", "bool_attribute":True },
{ "text_attribute":"Some more Text", "attribute":"blah again", "bool_attribute":False }
]
要做到这一点,我会这样做(未经测试):
是的,这就是我想的,不幸的是我对此无能为力。你如何使用lxml
etree.fromstring(“”)
生成XMLSyntaxError
。和tree=etree.parse(StringIO(“”),parser=etree.XMLParser(recover=True))
eats
即etree.tostring(tree)
isparser=etree.XMLParser(recover=True)
doc=etree.parse(fs,parser)
root=doc.getroot()对于root中的child:您是否在输出中看到
:打印etree.tostring(doc)
?是的,但我意识到OP中的#3是错误的,这实际上看起来是这样的:
您可以纠正您的问题#3.如果我遗漏了什么,很抱歉,但是我如何指定每个子元素是哪种类型呢?我是否仍然需要使用类似于我当前使用的字典的东西来表示“Person是一个属性,Name是节点文本,等等…”这取决于您在以后的代码中如何使用。暂时忘记输入。一旦你得到数据,你会如何处理它?你说有些数据是可选的,你的代码在这种情况下做什么?我将其输出到csv,其中每个元素是csv文件中的一行。因此,每个XML文件都有一个csv文件。如果可选数据是指缺少的子元素,那么我只需检查每个元素都有哪些子元素,并将其与应该有哪些子元素进行比较。缺少的子元素的值为None。我使用defaultdict存储元素,然后在将它们输出到csv之前将它们存储在列表中。我正在根据您所说的内容进行测试。我仍然有一个字典,每个文件都是一个键,但是这些值是所有相关值的XPath。因此,我迭代所有子级到根,然后迭代该文件的每个xpath。这意味着如果子元素不存在,我将得到一个None值,这很好,到目前为止,代码更漂亮。我需要做一些进一步的测试,看看这是否是一个可行的解决方案,但谢谢你给我的想法@可行的继任者:你可能应该把这些信息添加到你的问题中。我已经添加了关于如何使用xpath表达式的大纲。生成csv。
# for each element
row_dict = dict.fromkeys(xpathdict.keys())
...
# for each key
row_dict[key] = element.xpath(xpathdict[key]) or None
...
dictwriter.writerow(row_dict)
<root>
<element>
<text_attribute>Some Text</text_attribute>
<attribute var="blah"/>
<bool_attribute><boolean.true/></bool_attribute>
</element>
<element>
<text_attribute>Some more Text</text_attribute>
<attribute var="blah again"/>
<bool_attribute><boolean.false/></bool_attribute>
</element>
</root>
[
{ "text_attribute":"Some Text", "attribute":"blah", "bool_attribute":True },
{ "text_attribute":"Some more Text", "attribute":"blah again", "bool_attribute":False }
]
# Helper function so we can extract a default from an xpath result if empty
def get_first(x, default_value):
if(len(x)>0) return x[0]
return default_value
# Parse one element
def process_element( e ):
retval = {}
retval['text_attribute'] = get_first(e.xpath("text_attribute/text()"), "default text")
retval['attribute'] = get_first( e.xpath("attribute/@var"), "default attribute")
retval['bool_attribute'] = get_first( e.xpath("bool_attribute/boolean.true"), False )
return retval
# Parse all the elements
elements = []
elements_xml = xml.xpath('/root/element')
for e in elements_xml:
elements.push( process_element(e) )