对recently asked question进行冥想,我开始怀疑是否有一种非常简单的方式来处理Python中的XML文档。一种pythonic方式,如果你愿意的话。
如果我举一个例子,也许我可以解释得最好:让我们说以下 - 我认为这是一个很好的例子,说明如何(错误地)在Web服务中使用XML - 是我从http请求到{{3}的响应}
<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的实际问题 - 请参阅下面的示例注释。
答案 0 :(得分:14)
已提到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'
答案 1 :(得分:8)
你想要一个薄薄的贴面?这很容易做饭。尝试使用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 :(得分: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]
答案 3 :(得分:3)
看看Amara 2,特别是this tutorial的Bindery部分。
它的工作方式与您描述的方式非常相似。
另一方面。 ElementTree的find*()方法可以为您提供90%的方法,并与Python一起打包。
答案 4 :(得分:2)
如果您不介意使用第三方库,那么BeautifulSoup几乎完全符合您的要求:
>>> from BeautifulSoup import BeautifulStoneSoup
>>> soup = BeautifulStoneSoup('''<snip>''')
>>> soup.xml_api_reply.weather.current_conditions.temp_f['data']
u'68'
答案 5 :(得分:1)
我相信内置的python xml模块可以解决问题。看看“xml.parsers.expat”
答案 6 :(得分:1)
我找到了以下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
答案 7 :(得分:0)
suds项目提供了一个Web服务客户端库,其工作方式几乎与您描述的一样 - 为其提供wsdl,然后使用工厂方法创建已定义的类型(并处理响应!)。
答案 8 :(得分:-1)
如果您还没有,我建议您查看DOM API for Python。 DOM是一种非常广泛使用的XML解释系统,所以它应该非常强大。
它可能比你描述的要复杂得多,但它来自于它试图保留XML标记中隐含的所有信息,而不是来自糟糕的设计。