Анализ разбитого XML с помощью lxml.etree.iterparse

Я пытаюсь разобрать огромный XML-файл с lxml в эффективном режиме памяти (то есть поточно лениво с диска вместо загрузки всего файла в память). К сожалению, файл содержит некоторые плохие символы ascii, которые разбивают по умолчанию парсер. Парсер работает, если я устанавливаю recover = True, но метод iterparse не принимает параметр восстановления или собственный объект парсера. Кто-нибудь знает, как использовать iterparse для разбора разбитого xml?

#this works, but loads the whole file into memory parser = lxml.etree.XMLParser(recover=True) #recovers from bad characters. tree = lxml.etree.parse(filename, parser) #how do I do the equivalent with iterparse? (using iterparse so the file can be streamed lazily from disk) context = lxml.etree.iterparse(filename, tag='RECORD') #record contains 6 elements that I need to extract the text from 

Спасибо за вашу помощь!

EDIT – вот пример типов ошибок кодирования, которыми я сталкиваюсь:

 In [17]: data Out[17]: '\t<articletext>&lt;p&gt;The cafeteria rang with excited voices. Our barbershop quartet, The Bell \r Tones was asked to perform at the local Home for the Blind in the next town. We, of course, were glad to entertain such a worthy group and immediately agreed . One wag joked, "Which uniform should we wear?" followed with, "Oh, that\'s right, they\'ll never notice." The others didn\'t respond to this, in fact, one said that we should wear the nicest outfit we had.&lt;/p&gt;&lt;p&gt;A small stage was set up for us and a pretty decent PA system was donated for the occasion. The audience was made up of blind persons of every age, from the thirties to the nineties. Some sported sighted companions or nurses who stood or sat by their side, sharing the moment equally. I observed several German shepherds lying at their feet, adoration showing in their eyes as they wondered what was going on. After a short introduction in which we identified ourselves, stating our voice part and a little about our livelihood, we began our program. Some songs were completely familiar and others, called "Oh, yeah" songs, only the chorus came to mind. We didn\'t mind at all that some sang along \x1e they enjoyed it so much.&lt;/p&gt;&lt;p&gt;In fact, a popular part of our program is when the audience gets to sing some of the old favorites. The harmony parts were quite evident as they tried their voices to the different parts. I think there was more group singing in the old days than there is now, but to blind people, sound and music is more important. We received a big hand at the finale and were made to promise to return the following year. Everyone was treated to coffee and cake, our quartet going around to the different circles of friends to sing a favorite song up close and personal. As we approached a new group, one blind lady amazed me by turning to me saying, "You\'re the baritone, aren\'t you?" Previously no one had ever been able to tell which singer sang which part but this lady was listening with her whole heart.&lt;/p&gt;&lt;p&gt;Retired portrait photographer. Main hobby - quartet singing.&lt;/p&gt;</articletext>\n' In [18]: lxml.etree.from lxml.etree.fromstring lxml.etree.fromstringlist In [18]: lxml.etree.fromstring(data) --------------------------------------------------------------------------- XMLSyntaxError Traceback (most recent call last) /mnt/articles/<ipython console> in <module>() /usr/lib/python2.5/site-packages/lxml-2.2.4-py2.5-linux-i686.egg/lxml/etree.so in lxml.etree.fromstring (src/lxml/lxml.etree.c:48270)() /usr/lib/python2.5/site-packages/lxml-2.2.4-py2.5-linux-i686.egg/lxml/etree.so in lxml.etree._parseMemoryDocument (src/lxml/lxml.etree.c:71812)() /usr/lib/python2.5/site-packages/lxml-2.2.4-py2.5-linux-i686.egg/lxml/etree.so in lxml.etree._parseDoc (src/lxml/lxml.etree.c:70673)() /usr/lib/python2.5/site-packages/lxml-2.2.4-py2.5-linux-i686.egg/lxml/etree.so in lxml.etree._BaseParser._parseDoc (src/lxml/lxml.etree.c:67442)() /usr/lib/python2.5/site-packages/lxml-2.2.4-py2.5-linux-i686.egg/lxml/etree.so in lxml.etree._ParserContext._handleParseResultDoc (src/lxml/lxml.etree.c:63824)() /usr/lib/python2.5/site-packages/lxml-2.2.4-py2.5-linux-i686.egg/lxml/etree.so in lxml.etree._handleParseResult (src/lxml/lxml.etree.c:64745)() /usr/lib/python2.5/site-packages/lxml-2.2.4-py2.5-linux-i686.egg/lxml/etree.so in lxml.etree._raiseParseError (src/lxml/lxml.etree.c:64088)() XMLSyntaxError: PCDATA invalid Char value 30, line 1, column 1190 In [19]: chardet.detect(data) Out[19]: {'confidence': 1.0, 'encoding': 'ascii'} 

Как вы можете видеть, chardet считает, что это файл ascii, но в середине этого примера есть «\ x1e», что делает lxml вызывать исключение.

3 Solutions collect form web for “Анализ разбитого XML с помощью lxml.etree.iterparse”

Редактировать:

Это более старый ответ, и сегодня я бы сделал это по-другому. И я говорю не только о глухом snark … с тех пор BeutifulSoup4 доступен, и это действительно очень приятно. Я рекомендую это всем, кто споткнется здесь.


В настоящее время принятый ответ – это не то, что нужно делать. Сам вопрос также имеет плохое предположение:

parser = lxml.etree.XMLParser (recover = True) # восстанавливается от плохих символов.

Фактически recover=True для восстановления из искаженного XML . Однако есть опция «кодирования», которая бы устранила вашу проблему.

 parser = lxml.etree.XMLParser(encoding='utf-8' #Your encoding issue. recover=True, #I assume you probably still want to recover from bad xml, it's quite nice. If not, remove. ) 

Вот и все, это решение.


BTW – для тех, кто борется с разбором XML в python, особенно из сторонних источников. Я знаю, я знаю, документация плохая, и есть много SO красных сельдей; много плохих советов.

  • lxml.etree.fromstring ()? – Это для отлично сформированного XML, глупого
  • BeautifulStoneSoup? – Медленный, и имеет способ-тупой политики для закрывающих тегов
  • lxml.etree.HTMLParser ()? – (потому что xml сломан) Вот секрет – HTMLParser () – это … Parser with recover = True
  • lxml.html.soupparser? – Предполагается, что обнаружение кодировки будет лучше, но оно имеет те же недостатки BeautifulSoup, что и для самозакрывающихся тегов. Возможно, вы можете объединить XMLParser с UnicodeDammit от BeautifulSoup
  • UnicodeDammit и другие материалы для какамели для исправления кодировок? – Ну, UnicodeDammit – это милое, мне нравится имя, и оно полезно для вещей за пределами xml, но обычно они исправляются, если вы поступаете правильно с XMLParser ()

Вы можете попробовать всевозможные вещи из того, что доступно в Интернете. Документация lxml может быть лучше. Код выше – это то, что вам нужно для 90% случаев разбора XML. Здесь я повторю его:

 magical_parser = XMLParser(encoding='utf-8', recover=True) tree = etree.parse(StringIO(your_xml_string), magical_parser) #or pass in an open file object 

Пожалуйста. Мои головные боли – ваше здравомыслие. Кроме того, у вас есть другие возможности, которые вам могут понадобиться, вы знаете, XML.

Я решил проблему, создав класс с файловым интерфейсом объекта. Метод read () класса читает строку из файла и заменяет любые «плохие символы» перед возвратом строки к iterparse.

 #psudo code class myFile(object): def __init__(self, filename): self.f = open(filename) def read(self, size=None): return self.f.next().replace('\x1e', '').replace('some other bad character...' ,'') #iterparse context = lxml.etree.iterparse(myFile('bigfile.xml', tag='RECORD') 

Мне пришлось несколько раз отредактировать класс myFile, добавив еще несколько заметок replace () для нескольких других символов, создающих jxml-дроссель. Я думаю, что синтаксический анализ SAX от lxml также сработал бы (кажется, поддерживает вариант восстановления), но это решение работало как шарм!

Измените свой вопрос, сообщив, что происходит (точное сообщение об ошибке и трассировка (копирование / вставка, не печатайте из памяти)), чтобы вы считали, что проблема «плохой юникод» является проблемой.

Получите награду и подайте ей свой дамп MySQL. Расскажите нам, что он говорит.

Покажите нам первые 200-300 байт вашего дампа, используя, например, print repr(dump[:300])

Update Вы написали «» «Как вы можете видеть, chardet считает, что это файл ascii, но в середине этого примера есть« \ x1e », что делает lxml причиной исключения.» «"

Здесь я не вижу «плохого юникода».

чарт правильно. Что заставляет вас думать, что «\ x1e» не ASCII? Это символ ASCII, управляющий символ C0 с именем «RECORD SEPARATOR».

В сообщении об ошибке указано, что у вас есть недопустимый символ. Это тоже правильно. Единственными управляющими символами, которые действительны в XML, являются "\t" , "\r" и "\n" . MySQL должен ворчать об этом и / или предложить вам способ избежать его, например, _x001e_ (yuk!)

Учитывая контекст, похоже, что этот символ можно удалить без потерь. Вы можете исправить свою базу данных или, возможно, захотите удалить подобные символы из своего дампа (после проверки того, что все они пропущены), или вы можете выбрать менее привлекательный и менее впечатляющий формат вывода, чем XML.

Обновление 2 Вы, по-видимому, хотите, чтобы пользователь iterparse() не потому, что это ваша конечная цель, а потому, что вы хотите сохранить память. Если вы использовали формат CSV, у вас не было бы проблемы с памятью.

Обновление 3 В ответ на комментарий @Purrell:

попробуйте сами, чувак. pastie.org/3280965

Вот содержание этого пасты; он заслуживает сохранения:

 from lxml.etree import etree data = '\t<articletext>&lt;p&gt;The cafeteria rang with excited voices. Our barbershop quartet, The Bell \r Tones was asked to perform at the local Home for the Blind in the next town. We, of course, were glad to entertain such a worthy group and immediately agreed . One wag joked, "Which uniform should we wear?" followed with, "Oh, that\'s right, they\'ll never notice." The others didn\'t respond to this, in fact, one said that we should wear the nicest outfit we had.&lt;/p&gt;&lt;p&gt;A small stage was set up for us and a pretty decent PA system was donated for the occasion. The audience was made up of blind persons of every age, from the thirties to the nineties. Some sported sighted companions or nurses who stood or sat by their side, sharing the moment equally. I observed several German shepherds lying at their feet, adoration showing in their eyes as they wondered what was going on. After a short introduction in which we identified ourselves, stating our voice part and a little about our livelihood, we began our program. Some songs were completely familiar and others, called "Oh, yeah" songs, only the chorus came to mind. We didn\'t mind at all that some sang along \x1e they enjoyed it so much.&lt;/p&gt;&lt;p&gt;In fact, a popular part of our program is when the audience gets to sing some of the old favorites. The harmony parts were quite evident as they tried their voices to the different parts. I think there was more group singing in the old days than there is now, but to blind people, sound and music is more important. We received a big hand at the finale and were made to promise to return the following year. Everyone was treated to coffee and cake, our quartet going around to the different circles of friends to sing a favorite song up close and personal. As we approached a new group, one blind lady amazed me by turning to me saying, "You\'re the baritone, aren\'t you?" Previously no one had ever been able to tell which singer sang which part but this lady was listening with her whole heart.&lt;/p&gt;&lt;p&gt;Retired portrait photographer. Main hobby - quartet singing.&lt;/p&gt;</articletext>\n' magical_parser = etree.XMLParser(encoding='utf-8', recover=True) tree = etree.parse(StringIO(data), magical_parser) 

Чтобы запустить его, нужно импортировать один импорт, а другой – в другой. Данные чудовищны. Для вывода результата нет вывода. Вот замена с данными, которые вырезали до нужных предметов. 5 частей текста ASCII (исключая &lt; и &gt; ), которые являются действительными символами XML, заменяются на t1 , …, t5 . \x1e окружено t2 и t3 .

 [output wraps at column 80] Python 2.7.2 (default, Jun 12 2011, 15:08:59) [MSC v.1500 32 bit (Intel)] on win 32 Type "help", "copyright", "credits" or "license" for more information. >>> from lxml import etree >>> from cStringIO import StringIO >>> data = '<article>&lt;p&gt;t1&lt;/p&gt;&lt;p&gt;t2\x1et3&lt;/p&gt;&lt;p&gt;t4 &lt;/p&gt;&lt;p&gt;t5&lt;/p&gt;</article>' >>> magical_parser = etree.XMLParser(encoding='utf-8', recover=True) >>> tree = etree.parse(StringIO(data), magical_parser) >>> print(repr(tree.getroot().text)) '<p>t1</p><p>t2t3/ppt4/ppt5/p' 

Не то, что я бы назвал «выздоровлением»; после плохого символа символы < и > исчезают.

Пасти было в ответ на мой вопрос «Что дает вам представление о том, что кодировка =« utf-8 »решит его проблему?». Это было вызвано утверждением «Однако есть опция« кодирования », которая бы устранила вашу проблему». Но encoding = ascii производит одинаковый вывод. Таким образом, опуская кодировку arg. Это НЕ проблема кодирования. Дело закрыто.

  • Unescape _xHHHH_ escape-последовательности XML с использованием Python
  • Предотвращение использования BeautifulSoup для преобразования моих тегов XML в нижний регистр
  • xml.dom.minidom: получение значений CDATA
  • как найти рекурсивно для тега xml с использованием LXML?
  • Эффективный анализатор для больших XML-данных
  • Анализ сжатого XML-потока в ElementTree
  • Как передавать данные POST в запросы Python?
  • Использование urllib и minidom для извлечения данных XML
  • Python - лучший язык программирования в мире.