使用lxml.etree.iterparse解析单个文件中的多个XML声明

4 投票
2 回答
3384 浏览
提问于 2025-04-16 15:39

我需要解析一个文件,这个文件里面包含了多个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输入的问题一样,最简单的解决办法可能就是修复产生糟糕输入的那个东西。

撰写回答