我一直在尝试使用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文件时,解析就可以了!
这似乎也不是尺寸问题。我已经设法解析了更大的文件而没有任何问题。
有什么想法吗?
答案 0 :(得分:8)
以下是一些想法:
(0)解释“文件”和“偶尔”:你是否真的有时意味着它有效并且有时会使用相同的文件失败?
对每个失败的文件执行以下操作:
(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></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'&#([0-9]+);'
和r'&#x([0-9A-Fa-f]+);'
等正则表来检查文件,将匹配的文本转换为int序数并检查规范中的有效列表,即#x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]
...或者数字字符引用可能在语法上无效,例如未被;
',&#not-a-digit
等等终止
更新2 我错了,ElementTree错误消息中的数字是计算Unicode代码点,而不是字节。请参阅下面的代码,并从输出中获取对两个错误文件运行它的片段。
# 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 
10205764 �
10213901 �
10213936 �
10214123 �
13292514 
...
155656543 
155656564 
157344876 
157722583 
posts.xml
7607143 
12982273 
12982282 
12982292 
12982302 
12982310 
16085949 
16085955 
...
36303479 
36303494  <<=== whoops
38942863 
...
785292911 
801282472 
848911592 
答案 1 :(得分:7)
正如@John Machin建议的那样,有问题的文件中确实存在可疑的数字实体,尽管错误信息似乎指向文本中的错误位置。也许流媒体性质和缓冲使得报告准确位置变得困难。
事实上,所有这些实体都出现在文本中:
set(['', '', '', '', '', '', '
', '', '', '', '', '�', '', '', '
', '', '', '	', '', '', '', '', ''])
绝大多数都不允许。看起来这个解析器非常严格,你需要找到另一个不那么严格的解析器,或者预先处理XML。
答案 2 :(得分:3)
我不确定这是否能回答你的问题,但是如果你想对元素树引发的ParseError使用异常,你会这样做:
except ET.ParseError:
print("catastrophic failure")
print("last successful: {0}".format(last))
答案 3 :(得分:0)
我觉得在这里注意一点也许很重要,你可以很容易地发现你的错误并避免完全停止你的程序,只需使用你以后在函数中使用的内容,放置你的声明:
it = ET.iterparse(file(xml))
试试&amp;除了括号:
try:
it = ET.iterparse(file(xml))
except:
print('iterparse error')
当然,这不会修复您的XML文件或预处理技术,但可以帮助识别哪个文件(如果您正在解析批次)导致您的错误。