Действительно простой способ разобраться с XML в Python? - PullRequest
24 голосов
/ 24 июня 2010

Обдумывая недавно заданный вопрос , я начал задумываться, существует ли действительно простой способ работы с 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/> s - примеры см. Ниже в комментариях.

Ответы [ 9 ]

14 голосов
/ 24 июня 2010

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'
8 голосов
/ 24 июня 2010

Вы хотите тонкий шпон?Это легко приготовить.Попробуйте в качестве начала следующую тривиальную оболочку вокруг 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'
4 голосов
/ 24 июня 2010

Я настоятельно рекомендую 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]
3 голосов
/ 24 июня 2010

Посмотрите на Amara 2, особенно на часть Bindery этого урока .

Это работает очень похоже на то, что вы описываете.

С другой стороны. Методы ElementTree find * () могут дать вам 90% этого и поставляются с Python.

2 голосов
/ 24 июня 2010

Если вы не возражаете против использования сторонней библиотеки, то BeautifulSoup сделает почти то, что вы просите:

>>> from BeautifulSoup import BeautifulStoneSoup
>>> soup = BeautifulStoneSoup('''<snip>''')
>>> soup.xml_api_reply.weather.current_conditions.temp_f['data']
u'68'
1 голос
/ 24 июня 2010

Я нашел следующий 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
1 голос
/ 24 июня 2010

Я верю, что встроенный модуль Python xml поможет. Посмотрите на "xml.parsers.expat"

xml.parsers.expat

0 голосов
/ 24 июня 2010

Проект suds предоставляет клиентскую библиотеку веб-служб, которая работает почти так же, как вы описали, - предоставьте ей wsdl, а затем используйте фабричные методы для создания определенных типов (и обработки ответов тоже!).

0 голосов
/ 24 июня 2010

Если вы еще этого не сделали, я бы посоветовал изучить DOM API для Python .DOM - довольно широко используемая система интерпретации XML, поэтому она должна быть довольно надежной.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...