Действительно простой способ работы с XML в Python?
Размышляя над недавно заданным вопросом, я начал задаваться вопросом, существует ли действительно простой способ обработки XML-документов в Python. Пифонический путь, если хотите.
Возможно, я могу лучше объяснить, если я приведу пример: скажем следующее: я думаю, что это хороший пример того, как XML (неправильно) используется в веб-сервисах - это ответ, который я получаю от http-запроса до http://www.google.com/ig/api?weather=94043
<xml_api_reply version="1">
<weather module_id="0" tab_id="0" mobile_row="0" mobile_zipped="1" row="0" section="0" >
<forecast_information>
<city data="Mountain View, CA"/>
<postal_code data="94043"/>
<latitude_e6 data=""/>
<longitude_e6 data=""/>
<forecast_date data="2010-06-23"/>
<current_date_time data="2010-06-24 00:02:54 +0000"/>
<unit_system data="US"/>
</forecast_information>
<current_conditions>
<condition data="Sunny"/>
<temp_f data="68"/>
<temp_c data="20"/>
<humidity data="Humidity: 61%"/>
<icon data="/ig/images/weather/sunny.gif"/>
<wind_condition data="Wind: NW at 19 mph"/>
</current_conditions>
...
<forecast_conditions>
<day_of_week data="Sat"/>
<low data="59"/>
<high data="75"/>
<icon data="/ig/images/weather/partly_cloudy.gif"/>
<condition data="Partly Cloudy"/>
</forecast_conditions>
</weather>
</xml_api_reply>
После загрузки/разбора такого документа я хотел бы получить доступ к информации так же просто, как сказать
>>> xml['xml_api_reply']['weather']['forecast_information']['city'].data
'Mountain View, CA'
или
>>> xml.xml_api_reply.weather.current_conditions.temp_f['data']
'68'
Из того, что я видел до сих пор, кажется, что ElementTree
ближе всего к тому, о чем я мечтаю. Но это не так, есть еще некоторое неудобство, когда вы потребляете XML. OTOH, я думаю, что это не так сложно - возможно, только тонкий шпон поверх анализатора, - и все же он может уменьшить раздражение при работе с XML. Есть ли такая магия? (А если нет - почему?)
PS. Примечание. Я уже пробовал BeautifulSoup
и, хотя мне нравится его подход, он имеет реальные проблемы с пустым <element/>
- см. Ниже в комментариях для примеров.
Ответы
Ответ 1
Вам нужен тонкий шпон? Это легко приготовить. Попробуйте следующую тривиальную оболочку вокруг ElementTree в качестве запуска:
# geetree.py
import xml.etree.ElementTree as ET
class GeeElem(object):
"""Wrapper around an ElementTree element. a['foo'] gets the
attribute foo, a.foo gets the first subelement foo."""
def __init__(self, elem):
self.etElem = elem
def __getitem__(self, name):
res = self._getattr(name)
if res is None:
raise AttributeError, "No attribute named '%s'" % name
return res
def __getattr__(self, name):
res = self._getelem(name)
if res is None:
raise IndexError, "No element named '%s'" % name
return res
def _getelem(self, name):
res = self.etElem.find(name)
if res is None:
return None
return GeeElem(res)
def _getattr(self, name):
return self.etElem.get(name)
class GeeTree(object):
"Wrapper around an ElementTree."
def __init__(self, fname):
self.doc = ET.parse(fname)
def __getattr__(self, name):
if self.doc.getroot().tag != name:
raise IndexError, "No element named '%s'" % name
return GeeElem(self.doc.getroot())
def getroot(self):
return self.doc.getroot()
Вы вызываете его так:
>>> import geetree
>>> t = geetree.GeeTree('foo.xml')
>>> t.xml_api_reply.weather.forecast_information.city['data']
'Mountain View, CA'
>>> t.xml_api_reply.weather.current_conditions.temp_f['data']
'68'
Ответ 2
lxml. Вы можете также проверить lxml.objectify для некоторых действительно простых манипуляций.
>>> from lxml import objectify
>>> tree = objectify.fromstring(your_xml)
>>> tree.weather.attrib["module_id"]
'0'
>>> tree.weather.forecast_information.city.attrib["data"]
'Mountain View, CA'
>>> tree.weather.forecast_information.postal_code.attrib["data"]
'94043'
Ответ 3
Взгляните на Amara 2, особенно на Bindery часть этот учебник.
Он работает так, как будто вы описываете.
С другой стороны. ElementTree find *() методы могут дать вам 90% этого и упакованы с Python.
Ответ 4
Я очень рекомендую lxml.etree и xpath анализировать и анализировать ваши данные. Вот полный пример. Я усекал xml, чтобы было легче читать.
import lxml.etree
s = """<?xml version="1.0" encoding="utf-8"?>
<xml_api_reply version="1">
<weather module_id="0" tab_id="0" mobile_row="0" mobile_zipped="1" row="0" section="0" >
<forecast_information>
<city data="Mountain View, CA"/> <forecast_date data="2010-06-23"/>
</forecast_information>
<forecast_conditions>
<day_of_week data="Sat"/>
<low data="59"/>
<high data="75"/>
<icon data="/ig/images/weather/partly_cloudy.gif"/>
<condition data="Partly Cloudy"/>
</forecast_conditions>
</weather>
</xml_api_reply>"""
tree = lxml.etree.fromstring(s)
for weather in tree.xpath('/xml_api_reply/weather'):
print weather.find('forecast_information/city/@data')[0]
print weather.find('forecast_information/forecast_date/@data')[0]
print weather.find('forecast_conditions/low/@data')[0]
print weather.find('forecast_conditions/high/@data')[0]
Ответ 5
Если вы не против использовать стороннюю библиотеку, то BeautifulSoup будет делать почти то, что вы просите:
>>> from BeautifulSoup import BeautifulStoneSoup
>>> soup = BeautifulStoneSoup('''<snip>''')
>>> soup.xml_api_reply.weather.current_conditions.temp_f['data']
u'68'
Ответ 6
Я считаю, что встроенный модуль xml-модуля python сделает трюк. Посмотрите на "xml.parsers.expat"
xml.parsers.expat
Ответ 7
Я нашел следующий python-simplexml модуль, который в попытках автора получить что-то близкое к SimpleXML от PHP действительно small wrapper around ElementTree
. Он находится под 100 строками, но, похоже, делает то, что было запрошено:
>>> import SimpleXml
>>> x = SimpleXml.parse(urllib.urlopen('http://www.google.com/ig/api?weather=94043'))
>>> print x.weather.current_conditions.temp_f['data']
58
Ответ 8
Проект suds предоставляет клиентскую библиотеку Web-сервисов, которая работает почти так же, как вы описали, - предоставляйте ей wsdl, а затем используйте методы factory для создания определенных типов (и обрабатывайте ответы тоже!).
Ответ 9
Если вы еще этого не сделали, я бы предложил изучить DOM API для Python. DOM - довольно широко используемая система интерпретации XML, поэтому она должна быть довольно надежной.
Вероятно, это немного сложнее, чем то, что вы описываете, но это связано с его попытками сохранить всю информацию, неявную в разметке XML, а не от плохой конструкции.