使用lxml.etree.iterparse解析单个文件中的多个XML声明
我需要解析一个文件,这个文件里面包含了多个XML文件,也就是像<xml></xml>这样的格式,可能会有很多个。使用etree.iterparse的时候,我遇到了一个(正确的)错误:
lxml.etree.XMLSyntaxError: XML declaration allowed only at the start of the document
现在,我可以先处理一下这个输入文件,把每个包含的XML文件都单独生成一个文件。这可能是最简单的解决办法。但我在想,是否有更好的方法来解决这个“问题”。
谢谢!
2 个回答
0
我用正则表达式来解决这个问题。假设数据是一个字符串,里面包含了多个 XML 文档,而 handle 是一个函数,用来处理每个文档。在执行这个循环后,数据要么会变成空,要么会包含一个不完整的 XML 文档,而 handle 函数会被调用零次或多次。
while True:
match = re.match (r'''
\s* # ignore leading whitespace
( # start first group
<(?P<TAG>\S+).*?> # opening tag (with optional attributes)
.*? # stuff in the middle
</(?P=TAG)> # closing tag
) # end of first xml document
(?P<REM>.*) # anything else
''',
data, re.DOTALL | re.VERBOSE)
if not match:
break
document = match.group (1)
handle (document)
data = match.group ('REM')
3
你提供的示例数据显示了一个问题,而你提问和给出的错误信息又暗示了另一个问题。你是有多个XML文档拼接在一起,每个文档都有自己的XML声明呢,还是说你有一个XML片段,其中包含多个顶层元素?
如果是前者,那解决方案就需要把输入流分成多个流,然后分别解析每一个。这并不一定意味着要实现一个XML解析器。你可以在字符串中查找XML声明,而不需要解析里面的其他内容,只要你的输入不包含未转义的XML声明的CDATA部分。你可以写一个像文件一样的对象,从底层流中返回字符,直到遇到XML声明,然后用一个生成器函数把它包裹起来,继续返回流,直到到达文件末尾。这并不简单,但也不是特别困难。
如果你有一个包含多个顶层元素的XML片段,你可以把它们包裹在一个XML元素中,然后解析整个内容。
当然,像大多数涉及糟糕XML输入的问题一样,最简单的解决办法可能就是修复产生糟糕输入的那个东西。