Почему ElementTree вызывает ошибку ParseError? - PullRequest
11 голосов
/ 08 октября 2011

Я пытался проанализировать файл с 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-файл, содержащий этот узел и несколько его соседей, синтаксический анализ работает!

Кажется, это тоже не проблема размера. Мне удалось разобрать гораздо большие файлы без проблем.

Есть идеи?

Ответы [ 4 ]

8 голосов
/ 08 октября 2011

Как предположил @Джон Мачин, в данных файлах есть сомнительные числовые объекты, хотя сообщения об ошибках, похоже, указывают на неправильное место в тексте.Возможно, потоковая природа и буферизация затрудняют сообщение о точных позициях.

Фактически, все эти объекты появляются в тексте:

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.

8 голосов
/ 08 октября 2011

Вот несколько идей:

(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) Бросьте свой файл в веб-службу проверки 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
>>>

Не все допустимые символы Юникода допустимы в XML. См. XML 1.0 Спецификация .

Возможно, вы захотите проверить свои файлы с помощью регулярных выражений, таких как r'&#([0-9]+);' и r'&#x([0-9A-Fa-f]+);', преобразовать сопоставленный текст в порядковый номер и проверить действительный список из спецификации, т.е. #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 &#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;
3 голосов
/ 06 октября 2014

Я не уверен, отвечает ли это на ваш вопрос, но если вы хотите использовать исключение с ParseError, вызванным деревом элементов, вы должны сделать это:

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

Источник: http://effbot.org/zone/elementtree-13-intro.htm

0 голосов
/ 19 мая 2016

Я чувствую, что здесь также может быть важно отметить, что вы можете довольно легко поймать свою ошибку и избежать необходимости полностью останавливать свою программу, просто используя то, что вы уже используете позже в функции, поместив свое утверждение:

it = ET.iterparse(file(xml))

внутри скобки try & кроме:

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

Конечно, это не исправит ваш файл XML или метод предварительной обработки, но может помочь определить, какой файл (если выповторный разбор лотов) вызывает вашу ошибку.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...