lxml.etree fromsting () и tostring () не возвращают одинаковые данные - PullRequest
3 голосов
/ 27 января 2012

Я изучаю lxml (после использования ElementTree) и меня сбивает с толку, почему .fromstring и .tostring не являются обратимыми. Вот мой пример:

import lxml.etree as ET
f = open('somefile.xml','r')
data = f.read()
tree_in = ET.fromstring(data)
tree_out = ET.tostring(tree_in)
f2 = open('samefile.xml','w')
f2.write(tree_out)
f2.close

'somefile.xml' было 132 КБ. 'samefile.xml' - вывод - был 113 КБ, и в некоторой точке arbirtrary отсутствует конец файла. Закрывающие теги всего дерева и несколько фрагментов конечного элемента просто исчезли.

Что-то не так с моим кодом или что-то не так с вложенностью в исходный XML-файл? Если да, я вынужден снова использовать BeautifulSoup из ElementTree (без xpath)?

Одно примечание: текст во многих элементах содержал кучу дерьма, преобразованного в текст, но не в этом ли причина этой проблемы?

Пример:

<QuestionIndex Id="Perm"><Answer><![CDATA[confirm]]></Answer><Answer><![CDATA[NotConfirm]]></Answer></QuestionIndex>
<QuestionIndex Id="Actor"><Answer><![CDATA[GirlLt16]]></Answer><Answer><![CDATA[Fem17to25]]></Answer><Answer><![CDATA[BoyLt16]]></Answer><Answer><![CDATA[Mal17to25]]></Answer><Answer><![CDATA[Moth]]></Answer><Answer><![CDATA[Fath]]></Answer><Answer><![CDATA[Elder]]></Answer><Answer><![CDATA[RelLead]]></Answer><Answer><![CDATA[Auth]]></Answer><Answer><![CDATA[Teach]]></Answer><Answer><![CDATA[Oth]]></Answer></QuestionIndex>

Ответы [ 2 ]

9 голосов
/ 28 января 2012

Проблему «пропущенный конец файла в некоторой точке arbirtrary» трудно объяснить без полного воспроизводимого примера.

Но я подозреваю, что то, что вы называете «кучей дерьма», CDATA разделы .У вас есть несколько таких в вашем примере (кстати, это не один правильно сформированный XML-документ).

Как правило, синтаксический анализатор XML не обязан сохранять разделы CDATA без изменений.Такая разметка, как

<Answer><![CDATA[confirm]]></Answer>

эквивалентна

<Answer>confirm</Answer>    

Однако класс lxml.etree.XMLParser принимает параметр strip_cdata, который можно использовать для сохранения CDATAразделы.Экземпляр синтаксического анализатора может быть передан в etree.fromstring().Вот пример:

from lxml import etree 

XML = '<QuestionIndex Id="Perm"><Answer><![CDATA[confirm]]></Answer></QuestionIndex>'

print "Original size:", len(XML)
tree1 = etree.fromstring(XML)

out = etree.tostring(tree1)
print "With CDATA stripped:", len(out)
print out

parser = etree.XMLParser(strip_cdata=False)
tree2 = etree.fromstring(XML, parser)

out = etree.tostring(tree2)
print "With CDATA kept:", len(out)
print out

=>

Original size: 77
With CDATA stripped: 65
<QuestionIndex Id="Perm"><Answer>confirm</Answer></QuestionIndex>
With CDATA kept: 77
<QuestionIndex Id="Perm"><Answer><![CDATA[confirm]]></Answer></QuestionIndex>
2 голосов
/ 31 января 2012

Эта проблема оказалась намного проще, чем кажется, и ответ скрыт в предоставленном мною коде.

f.close

должно было быть

f.close()

Разница в том,оставшийся буфер из нескольких десятков символов, который так и не попал в файл notepad ++, в котором я проверял результаты. Закрытие файла для реального внесло все изменения, и код работает.

...