在Python中处理XML的简单方法?
在思考一个最近被问到的问题时,我开始想,是否有一种非常简单的方法可以在Python中处理XML文档。也就是说,想找一种更符合Python风格的方法。
我觉得用一个例子来解释会更清楚:假设我从http://www.google.com/ig/api?weather=94043这个网址发出HTTP请求后,得到的响应是这样的——我认为这是一个很好的例子,展示了XML在网络服务中是如何(被误)使用的。
<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时还是需要一些摸索。另一方面,我想的其实并不复杂——可能只是一个简单的解析器的外层包装——但这可以减少处理XML时的烦恼。有没有这样的神奇方法呢?(如果没有,那是为什么呢?)
附言:我已经尝试过BeautifulSoup
,虽然我喜欢它的处理方式,但它在处理空的<element/>
时确实存在一些问题——具体例子可以在下面的评论中看到。
9 个回答
19
提到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'
10
你想要一个简单的外壳?这很容易实现。可以试试下面这个简单的包装器,围绕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'