代码之家  ›  专栏  ›  技术社区  ›  Nas Banov

用Python处理XML真的很简单吗?

  •  24
  • Nas Banov  · 技术社区  · 16 年前

    recently asked question ,我开始怀疑是否有 真的很简单

    如果我举一个例子,也许我能解释得最好:假设下面的例子——我认为这是XML在web服务中如何使用的一个很好的例子——是我从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的麻烦。有这样的魔法吗(如果没有-为什么?)

    注:我试过了 BeautifulSoup <element/> 示例见下面的注释。

    9 回复  |  直到 9 年前
        1
  •  8
  •   Owen S.    16 年前

    你想要薄的贴面吗?那很容易做。首先尝试以下围绕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
  •  15
  •   Ryan Ginstrom    16 年前

    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
  •  4
  •   Jerub    16 年前

    我强烈建议使用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]
    
        4
  •  3
  •   Walter Mundt    16 年前

    this tutorial .

    它的工作方式和你描述的非常相似。

    另一方面。元素树 find*() 方法可以为您提供90%的支持,并使用Python打包。

        5
  •  2
  •   Mike Boers    16 年前

    如果您不介意使用第三方库,那么 BeautifulSoup

    >>> from BeautifulSoup import BeautifulStoneSoup
    >>> soup = BeautifulStoneSoup('''<snip>''')
    >>> soup.xml_api_reply.weather.current_conditions.temp_f['data']
    u'68'
    
        6
  •  1
  •   iform    16 年前
        7
  •  1
  •   Nas Banov    16 年前

    我发现如下 python-simplexml 模块,在作者试图从PHP中获得接近SimpleXML的东西时,它确实是一个 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
  •  0
  •   David Harks    16 年前

    suds项目提供了一个Web服务客户机库,它几乎完全按照您所描述的那样工作——向它提供一个wsdl,然后使用工厂方法来创建定义的类型(并处理响应!)。

        9
  •  -1
  •   tlayton    16 年前

    如果你还没有,我建议你调查一下 DOM API for Python

    它可能比您描述的要复杂一点,但这是因为它试图保留XML标记中隐含的所有信息,而不是因为设计不当。