Анализ XML в Python с использованием примера ElementTree
Мне сложно найти хороший базовый пример того, как разбирать XML в python с помощью Element Tree. Из того, что я могу найти, это самая простая библиотека, используемая для синтаксического анализа XML. Вот пример XML, с которым я работаю:
<timeSeriesResponse>
<queryInfo>
<locationParam>01474500</locationParam>
<variableParam>99988</variableParam>
<timeParam>
<beginDateTime>2009-09-24T15:15:55.271</beginDateTime>
<endDateTime>2009-11-23T15:15:55.271</endDateTime>
</timeParam>
</queryInfo>
<timeSeries name="NWIS Time Series Instantaneous Values">
<values count="2876">
<value dateTime="2009-09-24T15:30:00.000-04:00" qualifiers="P">550</value>
<value dateTime="2009-09-24T16:00:00.000-04:00" qualifiers="P">419</value>
<value dateTime="2009-09-24T16:30:00.000-04:00" qualifiers="P">370</value>
.....
</values>
</timeSeries>
</timeSeriesResponse>
Я могу делать то, что мне нужно, используя жесткий код. Но мне нужно, чтобы мой код был немного более динамичным. Вот что сработало:
tree = ET.parse(sample.xml)
doc = tree.getroot()
timeseries = doc[1]
values = timeseries[2]
print child.attrib['dateTime'], child.text
#prints 2009-09-24T15:30:00.000-04:00, 550
Вот несколько вещей, которые я пробовал, ни один из них не работал, сообщая, что они не смогли найти timeSeries (или что-то еще, что я пробовал):
tree = ET.parse(sample.xml)
tree.find('timeSeries')
tree = ET.parse(sample.xml)
doc = tree.getroot()
doc.find('timeSeries')
В принципе, я хочу загрузить файл xml, искать тег timeSeries и перебирать теги значений, возвращая DateTime и значение самого тега; все, что я делаю в приведенном выше примере, но не жесткое кодирование разделов xml, которые меня интересуют. Может ли кто-нибудь указать мне на некоторые примеры или дать мне несколько советов о том, как с этим справиться?
Спасибо за помощь. Тем не менее, использование обоих приведенных ниже рекомендаций работало над предоставленным образцом, но они не работали над полным файлом. Вот ошибка, которую я получаю из реального файла, когда я использую метод Ed Carrel:
(<type 'exceptions.AttributeError'>, AttributeError("'NoneType' object has no attribute 'attrib'",), <traceback object at 0x011EFB70>)
Я подумал, что в реальном файле ему что-то не нравится, поэтому я постепенно удалял вещи, пока это не сработало. Вот строки, которые я изменил:
originally: <timeSeriesResponse xsi:schemaLocation="a URL I removed" xmlns="a URL I removed" xmlns:xsi="a URL I removed">
changed to: <timeSeriesResponse>
originally: <sourceInfo xsi:type="SiteInfoType">
changed to: <sourceInfo>
originally: <geogLocation xsi:type="LatLonPointType" srs="EPSG:4326">
changed to: <geogLocation>
Удаление атрибутов, имеющих "xsi:...", устраняет проблему. Является ли "xsi:..." недействительным XML? Мне будет сложно удалить их программно. Любая предлагаемая работа вокруг?
Вот полный XML файл: http://www.sendspace.com/file/lofcpt
Когда я изначально задал этот вопрос, я не знал о пространствах имен в XML. Теперь, когда я знаю, что происходит, мне не нужно удалять атрибуты "xsi", которые являются объявлениями пространства имен. Я просто включаю их в свои поисковые запросы. См. эту страницу для получения дополнительной информации об пространствах имен в lxml.
Ответы
Ответ 1
Итак, теперь у меня есть ElementTree 1.2.6, и вы запускали следующий код в блоке XML, который вы разместили:
import elementtree.ElementTree as ET
tree = ET.parse("test.xml")
doc = tree.getroot()
thingy = doc.find('timeSeries')
print thingy.attrib
и получил следующий ответ:
{'name': 'NWIS Time Series Instantaneous Values'}
Кажется, он нашел элемент timeSeries без необходимости использования числовых индексов.
Что было бы полезно сейчас, зная, что вы имеете в виду, когда говорите "это не работает". Так как это работает для меня, учитывая тот же ввод, маловероятно, что ElementTree будет разбит каким-то очевидным образом. Обновите свой вопрос с помощью любых сообщений об ошибках, обратных маршрутов или всего, что вы можете предоставить, чтобы помочь нам помочь.
Ответ 2
Если я правильно понял ваш вопрос:
for elem in doc.findall('timeSeries/values/value'):
print elem.get('dateTime'), elem.text
или если вы предпочитаете (и если есть только одно вхождение timeSeries/values
:
values = doc.find('timeSeries/values')
for value in values:
print value.get('dateTime'), elem.text
Метод findall()
возвращает список всех совпадающих элементов, тогда как find()
возвращает только первый соответствующий элемент. Первый пример пересекает все найденные элементы, второй - над дочерними элементами элемента values
, в этом случае приводя к тому же результату.
Я не вижу, откуда возникает проблема с поиском timeSeries
. Может быть, вы просто забыли вызов getroot()
? (обратите внимание, что вам это действительно не нужно, потому что вы можете работать и с самим элементом elementtree, если вы измените выражение пути, например, на /timeSeriesResponse/timeSeries/values
или //timeSeries/values
)