Pull to refresh

Comments 55

да, elementree конечно вкуснее, чем стандартный dom api.

но скажите, зачем вот это?:
app_window = etree.tostring(root)

root.append(etree.XML(app_window))

поглядел в мануал.
у _ElementInterface есть методы эмуляции списков, и есть метод .append(childelement)
Это конкретно у меня в коде, ибо эти две части находятся в разных функциях, а передавать строку нагляднее и проще. Да и потом, я же тоже изучал новые возможности модуля, вот, собственно, такое простое и красивое преобразование — тоже хорошая вещь :)
Спасибо! вижу что есть смысл попробовать lxml.
Сам довольно долго промучался с minidom, особенно с проблемами кодировки, однако задача была решена.
Кстати, toprettyxml() является лишь оберткой toxml(), а у последнего есть еще параметры, задающие символы отступа строки (по умолчанию \t) и разделитель самих строк (\n). Однако опять-таки это не всегда может помочь.
В избранное! =) Пока не надо, но уверен, что понадобится.
как можно жить в современном мире, не работая с xml?
И уж тем более с Питоном? ;)
Много вещей можно получать в json.
Много вещей пишутся не для веба.

Хотя, конечно, на JS можно, в принципе, писать и консольные/десктопные/серверные приложения…
Никто не спорит :)
Json можно использовать во многих языках, ну и он является подмножеством Yaml.
YAML, JSON.
а XML не для человеков.
UFO just landed and posted this here
Насколько я помню, он предназначен для парсинга HTML с незакрытыми тэгами и прочим говном. Поэтому медленный. И использовать его для XML никто в здравом уме не будет.
Ну и не факт, что он является обёрткой над кодом на Си, как lxml. Отсюда тоже могут быть потери производительности (скорее всего так и есть).
UFO just landed and posted this here
а не эффективнее ль будет tidy-фицировать исходный html в xhtml и потом работать как с xml?
То есть парсить в два прохода? Не думаю, что получится эффективнее.

Если вы о том, чтобы исходный HTML tidy-фицировать заранее, то это не всегда возможно (например, если парсим чужой сайт). Надеюсь, понятно о чём я, не люблю допоздна сидеть, в голове всё путаться начинает.
я не пользовал Soup, но подозреваю, что тиди-фикация в той или иной форме там внутри присутствует всёравно.
UFO just landed and posted this here
обнаружился еще один неприятный баг — со времен Python 2.4 функция toprettyxml() <…> зачем-то добавляет к каждой строчке символ перевода каретки.

Это не баг, она потому и называется pretty, что выводит отформатированный по-человечески xml. Однако ее можно заставить не делать индентацию и не вставлять символ строки: app_xml.toprettyxml(indent='', newl='').
Тогда главный вопрос — почему этого не сделали по умолчанию? Это играет роль только тогда, когда вы собираетесь читать потом xml какими-нибудь нестандартными средствами, например, регэкспами. А так — и вы нормально прочитаете, и любой парсер нормально распознает.
Все pretty-функции в питоне по умолчанию выводят читабельные строки. В вашем случае, лучше было использовать app_xml.toxml(), который бы выдал простую строку. Думаю, здесь надо просто запомнить, что pretty — это всегда для людей. Даже такой модуль есть pprint — от pretty print.
Прошу прощения, хабр тормозил, поэтому ответил два раза.
В питоне все функции, имеющие pretty в названии, предназначены для людей. Это что-то вроде общественного договора. Существует даже специальный модуль pprint (от pretty print), который позволяет по-человечески форматировать и выводить стандартные объекты питона.

Почти всегда существуют неpretty-функции. В вашем случае это app_xml.toxml(), которая выдаст просто обычную строку.
Эмм… а никто не задумывался, что тем самым функция ИЗМЕНЯЕТ содержимое xml node. Все-таки добавление в тело ноды доп. символов перевода каретки и символов табуляции\пробела — это некорректно.

Теги форматировать можно как угодно, но содержимое — нельзя. Потому как с точки зрения любого парсера вот эти 2 ноды:
Мама мыла раму
и
Мама мыла раму

разные, причем сильно. Т.е. ни хранить такой результат ни передавать для дальнейшей обрабтки нельзя -только показать человеку. И машине плевать что человек не видит whitespace символы и перевод каретки, она проводит анализ по своим правилам, «не-человеческим».

И правильно делают парсеры которые на это ругаются.
Я не читал документации по prettyprint, но если доки писались вменяемыми людьми — там должна быть оговорка крупными буквами — недопустимо применять результаты работы функции для какой-либо машинной обработки, только для вывода информации, потому что информация искажена.

Такое поведение (дописывание текста XML ноды), в моем понимании равносильно тому, что в строки базы данных при каждом селекте некая хрень дописывала бы теги форматирования html, мотивируя это тем, что человек то все равно их не увидит…
хабр съел тэги в комментариях — мама мыла раму в обоих случаях — в тегах < x >, в первом без переноса, во втором — с переносом ( или см пример с simple.App в статье)
Хотелось бы увидеть сравнение скорости парсинга xml с помощью различных библиотек.
Преимущество ElementTree — подхода очевидно.

Не соглашусь. В первой ссылке сравнивают ElementTree с lxml и данные там представлены количеством милисекунд на каждый проход, т.е. чем меньше, тем лучше. lxml в разы быстрее (~10) при сериализации и примерно в два раза медленее при парсинге, чем cElementTree. Без си-оптимизаций ElementTree вообще не стоит принимать во внимание. Во второй ссылке сравнивают cElementTree и sax. Sax медленне в ~5 раз.

В целом:
Если есть возможность установить libxml2, тогда лучше использовать lxml.
Если нет, стоит обратиться к cElementTree.
Sax можно использовать тогда, когда данных мало; еще он, по-моему, несколько удобнее в обращении.
Я про то, что lxml использует тот же подход, просто через сишные библиотеки :) А сам алгоритм получается заметно быстрее dom'а
Sax, в отличие от dom-подходов, не формирует никакой модели документа, это потоковый событийный парсер.
sax нужен, когда данных много. много-много-много, сотни мегабайт — dom умирает в таких случаях.
Ну, не умирает. Просто потребление памяти будет в 1.5-2 раза выше. В случае с lxml — где-то в 4, т.к. приходится приходится дублировать структуру документа libxml в объектах питона.
возьмите машину с 4 gb памяти и отпарсите на ней файл размером гигабайта в 2 через dom. умереть не умрёт, конечно, но на жизнь это будет мало похоже.
И часто у вас такое происходит?)
Нет, потому как лечим, ищем обходные пути, выкидываем xml вообще и живём счастливо.
UFO just landed and posted this here
lxml является не чистым python-модулем, а интерфейсом к C-библиотекам libxml2 и libxslt. Поэтому он очень быстр, что особенно заметно на больших объемах данных.
Вот из-за всех таких заморочек предпочитаю пользоваться атрибутами, а текстовыми нодами. Хотя в данном случае приходится работать с тем, что есть.

> зачем-то добавляет к каждой строчке символ перевода каретки
Не существует pretty-стандарта xml, так что прямой вины описанного парсера тут нет.

В любом случае, используя pretty вы рискуете накосячить с итоговым xml.
Да, с текстовыми нодами та ещё беда. Иногда манипуляция с ними вообще нелогична. Например, вот случай:

<a>текст1<b/>текст2</a&gt

Пусть элемент <a> хранится в объекте под именем a, а элемент <a> — в b. Тогда текст1 попадёт в атрибут a.text, а текст2 — в b.tail.
Опечатался.

элемент <b> — в b
Так, насколько я понимаю, тут вы вообще стандарт нарушаете.

Правилами XML допустима вложенность типа 1<b />2, но никак не some<b />text.

А невалидный xml обрабатывается черте как.
Парсер — лох.

<a><b>111</b><b /></a>

И, соответственно

<a>some<b />text</a>
Почему это невалидный? Возьмите хоть XHTML для примера: Привет !
Действительно, парсер лох.

<p>Привет <img src=«smile.gif» />! </p>
Это допускается, т.к. в текст (с точки зрения libxml и xml в целом) — это та же нода, только текстовая. В libxml чистом было бы p.childNodes() == Node[b: TextNode[Привет ], Node[img], TextNode[! ]]. Соответственно «хвост» — это следующий текстовый атрибут после текущего в lxml. Не вижу способа сделать это иным образом, посему lxml молодец и в этом случае.

А вообще с точки зрения xhtml/xml «внутренне» (для браузера) ваш пример аналогичен этому:
<p><span>Привет </span><img src="ass.bmp" /><span>!</span></p>


Вот только парсить его один фиг сложнее чем через text/tail.
Вы правы, действительно иных способов как-то не придумаешь… Наверное мне просто неочевидным когда-то показался факт, что текст надо доставать через предыдущий элемент.
Ещё во всех питоновских библиотеках для XML, которые я встречал (lxml тоже) не очень приятно работать с XML namespaces.

Если входной XML весь лежит в каком-то безымянном пространстве имён (к примеру, обычный XHTML), то во всех селекторах в коде придётся указывать URI этого пространства имён. Иными словами, плохо, что нет возможности задать дефолтный URI.

Когда-то при генерации XHTML я препопочёл использовать хак и просто задать атрибут xmlns :-)

html = etree.Element('html')
# HACK. Use this because lxml library lacks functions to set default
# XML namespace without inserting namespace URI's into each XML element
# name. For example:
#
# * element.findall('{%s}something' % namespace)
#
# * element.xpath('prefix:something', prefixMap)
#
# Also it seems imposible to use element names that does not have prefix,
# but have namespace (i.e., non-default anonymous namespace) in xpath().
# The xpath() method complains when prefixMap has None key and does not
# consider prefixMap[''] when it is specified… We require anonymous
# namespace in order not to get too verbose output
#
html.set('xmlns', 'http://www.w3.org/1999/xhtml')
head = etree.SubElement(html, 'head')
UFO just landed and posted this here
Спасибо за новость — уже больше года использую эту либу для парсинга xml :)
Если серьезно, то либа довольно приятная, легко разбирать пропарсенное. Есть только одна серьезная проблема — память подтекает.
lxml — обвязка над libxml2\libxslt, так что ничего странного в его скорости нет. Ещё не упомянут 4Suite, но поскольку он целиком написан на питоне, то работает на порядок медленее.

В lxml нельзя достучаться до чистого sax-интерфейса, даже при iterparse приходится вставать на голову и применять такие трюки, например, для экономии памяти — www.ibm.com/developerworks/xml/library/x-hiperfparse/

Но у lxml есть очень большое преимущество — активная разработка и обратная связь. Штефан (Stefan Behnel) всегда оперативно реагирует на просьбы о помощи и на feature requests. В общем, мы используем именно lxml и довольны настолько, насколько можно быть довольными при работе с xml.
А как прописать доктайп с помощью lxml?
Или просто потом открыть файл для записи и добавить туда строку с доктайпом?
Так у toprettyxml() в minidom есть параметр newl="\n". Можно заменить его чем хочется. Хотя с elementree/lxml работать всё одно поприятнее.
Sign up to leave a comment.

Articles