将XML文件解析为Python对象
我有一个看起来像这样的XML文件:
<encspot>
<file>
<Name>some filename.mp3</Name>
<Encoder>Gogo (after 3.0)</Encoder>
<Bitrate>131</Bitrate>
<Mode>joint stereo</Mode>
<Length>00:02:43</Length>
<Size>5,236,644</Size>
<Frame>no</Frame>
<Quality>good</Quality>
<Freq.>44100</Freq.>
<Frames>6255</Frames>
..... and so forth ......
</file>
<file>....</file>
</encspot>
我想把它读入一个Python对象,类似于字典的列表。因为这个文件的格式是固定的,我有点想用正则表达式(我对这个还挺在行的)。不过,我想先看看有没有人知道怎么在这里轻松避免使用正则表达式。我对SAX或其他解析方法没有太多经验,但我愿意学习。
我期待有人能教我怎么在Python中快速做到这一点,而不需要用正则表达式。谢谢大家的帮助!
5 个回答
我也在寻找一种简单的方法,可以在XML文档和Python数据结构之间转换数据,类似于Golang的XML库,它允许你以声明的方式指定如何将数据结构映射到XML。
我没有找到这样的Python库,所以我自己写了一个,叫做declxml,用于声明式的XML处理。
使用declxml,你可以创建处理器,这些处理器可以声明性地定义你的XML文档的结构。处理器不仅用于解析和序列化数据,还可以进行基本的验证。
用declxml将XML数据解析成字典列表非常简单
import declxml as xml
xml_string = """
<encspot>
<file>
<Name>some filename.mp3</Name>
<Encoder>Gogo (after 3.0)</Encoder>
<Bitrate>131</Bitrate>
</file>
<file>
<Name>another filename.mp3</Name>
<Encoder>iTunes</Encoder>
<Bitrate>128</Bitrate>
</file>
</encspot>
"""
processor = xml.dictionary('encspot', [
xml.array(xml.dictionary('file', [
xml.string('Name'),
xml.string('Encoder'),
xml.integer('Bitrate')
]), alias='files')
])
xml.parse_from_string(processor, xml_string)
这将产生以下结果
{'files': [
{'Bitrate': 131, 'Encoder': 'Gogo (after 3.0)', 'Name': 'some filename.mp3'},
{'Bitrate': 128, 'Encoder': 'iTunes', 'Name': 'another filename.mp3'}
]}
想把数据解析成对象而不是字典?这也可以做到
import declxml as xml
class AudioFile:
def __init__(self):
self.name = None
self.encoder = None
self.bit_rate = None
def __repr__(self):
return 'AudioFile(name={}, encoder={}, bit_rate={})'.format(
self.name, self.encoder, self.bit_rate)
processor = xml.array(xml.user_object('file', AudioFile, [
xml.string('Name', alias='name'),
xml.string('Encoder', alias='encoder'),
xml.integer('Bitrate', alias='bit_rate')
]), nested='encspot')
xml.parse_from_string(processor, xml_string)
这将产生以下输出
[AudioFile(name=some filename.mp3, encoder=Gogo (after 3.0), bit_rate=131),
AudioFile(name=another filename.mp3, encoder=iTunes, bit_rate=128)]
可以使用ElementTree这个库。你不需要去搞一些只会解析的工具,比如pyexpat
,因为那样你只会部分且糟糕地重新发明一个ElementTree。
还有一个选择是lxml,这是一个第三方的包,它实现了ElementTree的接口,并且功能更多。
更新 有人开始玩代码高尔夫;这是我的一个代码示例,实际上创建了你所需要的数据结构:
# xs = """<encspot> etc etc </encspot"""
>>> import xml.etree.cElementTree as et
>>> from pprint import pprint as pp
>>> pp([dict((attr.tag, attr.text) for attr in el) for el in et.fromstring(xs)])
[{'Bitrate': '131',
'Encoder': 'Gogo (after 3.0)',
'Frame': 'no',
'Frames': '6255',
'Freq.': '44100',
'Length': '00:02:43',
'Mode': 'joint stereo',
'Name': 'some filename.mp3',
'Quality': 'good',
'Size': '5,236,644'},
{'Bitrate': '0', 'Name': 'foo.mp3'}]
>>>
你可能想要一个字典,把“属性”名称映射到转换函数:
converters = {
'Frames': int,
'Size': lambda x: int(x.replace(',', '')),
# etc
}
如果你觉得正则表达式比这个简单,那我真是佩服你,我的SD Chargers帽子都要脱下来了:
#!/usr/bin/env python
import xml.etree.cElementTree as et
sxml="""
<encspot>
<file>
<Name>some filename.mp3</Name>
<Encoder>Gogo (after 3.0)</Encoder>
<Bitrate>131</Bitrate>
</file>
<file>
<Name>another filename.mp3</Name>
<Encoder>iTunes</Encoder>
<Bitrate>128</Bitrate>
</file>
</encspot>
"""
tree=et.fromstring(sxml)
for el in tree.findall('file'):
print '-------------------'
for ch in el.getchildren():
print '{:>15}: {:<30}'.format(ch.tag, ch.text)
print "\nan alternate way:"
el=tree.find('file[2]/Name') # xpath
print '{:>15}: {:<30}'.format(el.tag, el.text)
输出结果:
-------------------
Name: some filename.mp3
Encoder: Gogo (after 3.0)
Bitrate: 131
-------------------
Name: another filename.mp3
Encoder: iTunes
Bitrate: 128
an alternate way:
Name: another filename.mp3
如果你喜欢正则表达式的简洁,这里有一段同样让人看不懂的列表推导式,用来创建一个数据结构:
[(ch.tag,ch.text) for e in tree.findall('file') for ch in e.getchildren()]
这段代码会生成一个包含XML中<file>
标签下子元素的元组列表,顺序和文档中的顺序一致:
[('Name', 'some filename.mp3'),
('Encoder', 'Gogo (after 3.0)'),
('Bitrate', '131'),
('Name', 'another filename.mp3'),
('Encoder', 'iTunes'),
('Bitrate', '128')]
当然,如果你多写几行代码,想得再仔细一点,你就可以用ElementTree从XML中创建任何你想要的数据结构。这个工具是Python自带的。
编辑
代码比赛开始了!
[{item.tag: item.text for item in ch} for ch in tree.findall('file')]
[ {'Bitrate': '131',
'Name': 'some filename.mp3',
'Encoder': 'Gogo (after 3.0)'},
{'Bitrate': '128',
'Name': 'another filename.mp3',
'Encoder': 'iTunes'}]
如果你的XML文件只有file
这一部分,你可以随意发挥。如果你的XML还有其他标签和部分,你就需要考虑子元素所在的部分,并且需要使用findall
。
关于ElementTree的教程可以在Effbot.org找到。