【问题标题】:Why is ElementTree raising a ParseError?为什么 ElementTree 会引发 ParseError?
【发布时间】:2011-12-03 08:35:34
【问题描述】:

我一直在尝试用xml.etree.ElementTree解析一个文件:

import xml.etree.ElementTree as ET
from xml.etree.ElementTree import ParseError

def analyze(xml):
    it = ET.iterparse(file(xml))
    count = 0
    last = None

    try:        
        for (ev, el) in it:
            count += 1
            last = el

    except ParseError:
            print("catastrophic failure")
            print("last successful: {0}".format(last))

    print('count: {0}'.format(count))

这当然是我的代码的简化版本,但这足以破坏我的程序。如果我删除 try-catch 块,我会在某些文件中收到此错误:

Traceback (most recent call last):
  File "<pyshell#22>", line 1, in <module>
    from yparse import analyze; analyze('file.xml')
  File "C:\Python27\yparse.py", line 10, in analyze
    for (ev, el) in it:
  File "C:\Python27\lib\xml\etree\ElementTree.py", line 1258, in next
    self._parser.feed(data)
  File "C:\Python27\lib\xml\etree\ElementTree.py", line 1624, in feed
    self._raiseerror(v)
  File "C:\Python27\lib\xml\etree\ElementTree.py", line 1488, in _raiseerror
    raise err
ParseError: reference to invalid character number: line 1, column 52459

结果是确定性的,如果文件有效,它将始终有效。如果文件失败,它总是失败并且总是在同一点失败。

最奇怪的是我正在使用跟踪来查明是否有任何格式错误的 XML 破坏了解析器。然后我隔离导致故障的节点。但是当我创建一个包含该节点和它的一些邻居的 XML 文件时,解析就可以了!

这似乎也不是尺寸问题。我已经成功地解析了更大的文件。

有什么想法吗?

【问题讨论】:

  • 您必须展示一些有问题的 XML。有可能您的 XML 不好,然后当您对其进行编辑以缩小范围时,它就变成了好的 XML。编辑可以做鬼鬼祟祟的事情……
  • @NedBatchelder 文件很大,上传起来很困难。不过,我确实考虑过这种可能性。所以我直接使用 Python 的文件操作函数来提取部分 XML 并将它们写入另一个文件。
  • 你能显示导致这种情况的任何 XML 吗?
  • @pessimopoppotamus:根据您的错误消息,错误仅在文件中发生 52KB ...
  • @NedBatchelder 我正在开发一个 XML 分块器,它可以生成有效的 XML 块,直到达到一定的块大小。我将尝试使用它来生成失败的 XML 并上传。

标签: python xml parsing


【解决方案1】:

这里有一些想法:

(0) 解释“一个文件”和“偶尔”:你真的是说它有时会工作,有时会与 same 文件一起失败吗?

对每个失败的文件执行以下操作:

(1) 找出文件中抱怨的地方:

text = open("the_file.xml", "rb").read()
err_col = 52459
print repr(text[err_col-50:err_col+100]) # should include the error text
print repr(text[:50]) # show the XML declaration

(2) 将您的文件提交到基于 Web 的 XML 验证服务,例如http://www.validome.org/xml/http://validator.aborla.net/

并编辑您的问题以显示您的发现。

更新:这是说明您的问题的最小 xml 文件:

[badcharref.xml]
<a>&#1;</a>

[Python 2.7.1 output]
>>> import xml.etree.ElementTree as ET
>>> it = ET.iterparse(file("badcharref.xml"))
>>> for ev, el in it:
...     print el.tag
...
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "C:\python27\lib\xml\etree\ElementTree.py", line 1258, in next
    self._parser.feed(data)
  File "C:\python27\lib\xml\etree\ElementTree.py", line 1624, in feed
    self._raiseerror(v)
  File "C:\python27\lib\xml\etree\ElementTree.py", line 1488, in _raiseerror
    raise err
xml.etree.ElementTree.ParseError: reference to invalid character number: line 1, column 3
>>>

并非所有有效的 Unicode 字符在 XML 中都是有效的。请参阅XML 1.0 Specification

您可能希望使用 r'&amp;#([0-9]+);'r'&amp;#x([0-9A-Fa-f]+);' 等正则表达式检查您的文件,将匹配的文本转换为 int 序数并检查规范中的有效列表,即 #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]

... 或者数字字符引用在语法上无效,例如未被;'、&amp;#not-a-digit 等终止

更新 2 我错了,ElementTree 错误消息中的数字是计算 Unicode 代码点,而不是字节。请参阅下面的代码以及在两个坏文件上运行它的输出中的 sn-ps。

# coding: ascii
# Find numeric character references that refer to Unicode code points
# that are not valid in XML.
# Get byte offsets for seeking etc in undecoded file bytestreams.
# Get unicode offsets for checking against ElementTree error message,
# **IF** your input file is small enough. 

BYTE_OFFSETS = True
import sys, re, codecs
fname = sys.argv[1]
print fname
if BYTE_OFFSETS:
    text = open(fname, "rb").read()
else:
    # Assumes file is encoded in UTF-8.
    text = codecs.open(fname, "rb", "utf8").read()
rx = re.compile("&#([0-9]+);|&#x([0-9a-fA-F]+);")
endpos = len(text)
pos = 0
while pos < endpos:
    m = rx.search(text, pos)
    if not m: break
    mstart, mend = m.span()
    target = m.group(1)
    if target:
        num = int(target)
    else:
        num = int(m.group(2), 16)
    # #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]
    if not(num in (0x9, 0xA, 0xD) or 0x20 <= num <= 0xD7FF
    or 0xE000 <= num <= 0xFFFD or 0x10000 <= num <= 0x10FFFF):
        print mstart, m.group()
    pos = mend

输出:

comments.xml
6615405 &#x10;
10205764 &#x00;
10213901 &#x00;
10213936 &#x00;
10214123 &#x00;
13292514 &#x03;
...
155656543 &#x1B;
155656564 &#x1B;
157344876 &#x10;
157722583 &#x10;

posts.xml
7607143 &#x1F;
12982273 &#x1B;
12982282 &#x1B;
12982292 &#x1B;
12982302 &#x1B;
12982310 &#x1B;
16085949 &#x1C;
16085955 &#x1C;
...
36303479 &#x12;
36303494 &#xFFFF; <<=== whoops
38942863 &#x10;
...
785292911 &#x08;
801282472 &#x13;
848911592 &#x0B;

【讨论】:

  • (0) 有时表示“带有某些文件”。但是,结果是确定性的,如果文件有效,它将始终有效。如果文件失败,它总是失败并且总是在同一点失败。
  • (1) 我这样做了,我找不到任何明显的错误。 (2) 太大了做不到。
  • 我怀疑是这种情况,但是文件中出现错误的部分附近没有类似的字符。
  • 涉及预处理的解决方案不是一个好主意,因为文件太大了。理想情况下,XML 解析器应该有一种方法可以优雅地记录错误并继续解析,而不是灾难性地崩溃。
  • 但这是一个 +1 的努力。我现在完成了。我很快回来。希望我能验证我的分块器工作正常,我会上传一些示例。
【解决方案2】:

正如@John Machin 建议的那样,有问题的文件中确实包含可疑的数字实体,尽管错误消息似乎指向文本中的错误位置。也许流式传输的性质和缓冲使得报告准确的位置变得困难。

事实上,所有这些实体都出现在文本中:

set(['&#x08;', '&#x0E;', '&#x1E;', '&#x1C;', '&#x18;', '&#x04;', '&#x0A;', '&#x0C;', '&#x16;', '&#x14;', '&#x06;', '&#x00;', '&#x10;', '&#x02;', '&#x0D;', '&#x1D;', '&#x0F;', '&#x09;', '&#x1B;', '&#x05;', '&#x15;', '&#x01;', '&#x03;'])

大多数是不允许的。看起来这个解析器很严格,你需要找到另一个不那么严格的,或者对 XML 进行预处理。

【讨论】:

  • 确实,文件被破坏了。我在解析它之前做了一些预处理,它按预期工作。
【解决方案3】:

我不确定这是否能回答你的问题,但如果你想使用元素树引发的 ParseError 异常,你可以这样做:

except ET.ParseError:
            print("catastrophic failure")
            print("last successful: {0}".format(last))

来源:http://effbot.org/zone/elementtree-13-intro.htm

【讨论】:

  • 这是一个非常古老的问题,答案已被接受。如果您不确定是否可以在答案中添加任何内容,请不要为了回答而回答。
【解决方案4】:

我觉得这里可能还需要注意的是,您可以很容易地发现错误并避免完全停止您的程序,只需使用您稍后在函数中已经使用的内容,然后放置您的语句:

it = ET.iterparse(file(xml))

在 try & except 括号内:

try:
    it = ET.iterparse(file(xml))
except:
    print('iterparse error')

当然,这不会修复您的 XML 文件或预处理技术,但可以帮助确定哪个文件(如果您正在解析批次)导致您的错误。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-10-08
    • 2015-10-03
    • 2021-09-25
    • 1970-01-01
    • 2016-04-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多