将XML文件解析为Python对象

Fel*_*bek 16 python regex xml xml-parsing

我有一个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>
Run Code Online (Sandbox Code Playgroud)

我想把它读成一个python对象,就像一个字典列表.因为标记是绝对固定的,所以我很想使用正则表达式(我很擅长使用它们).但是,我想我会检查是否有人知道如何在这里轻松避免正则表达式.我对SAX或其他解析没有太多经验,但我愿意学习.

我期待着在没有Python的正则表达式的情况下快速展示如何完成这项工作.谢谢你的帮助!

the*_*olf 26

如果您认为正则表达式比这更容易,那么我心爱的SD充电器帽对你不利:

#!/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)  
Run Code Online (Sandbox Code Playgroud)

输出:

-------------------
           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  
Run Code Online (Sandbox Code Playgroud)

如果您对正则表达式的吸引力正在简洁,那么创建数据结构的列表理解也同样难以理解:

[(ch.tag,ch.text) for e in tree.findall('file') for ch in e.getchildren()]
Run Code Online (Sandbox Code Playgroud)

这将按<file>文档顺序创建XML子元素的元组列表:

[('Name', 'some filename.mp3'), 
 ('Encoder', 'Gogo (after 3.0)'), 
 ('Bitrate', '131'), 
 ('Name', 'another filename.mp3'), 
 ('Encoder', 'iTunes'), 
 ('Bitrate', '128')]
Run Code Online (Sandbox Code Playgroud)

显然,通过更多行和更多思考,您可以使用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'}]
Run Code Online (Sandbox Code Playgroud)

如果您的XML只有该file部分,您可以选择您的高尔夫.如果您的XML有其他标记,其他部分,您需要考虑子项所在的部分,您需要使用findall

Effbot.org上有一个关于ElementTree的教程

  • 使用dict理解(2.7+)进行罚球 (3认同)

Joh*_*hin 8

使用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'}]
>>>
Run Code Online (Sandbox Code Playgroud)

您可能希望将dict映射"属性"名称转换为转换函数:

converters = {
    'Frames': int,
    'Size': lambda x: int(x.replace(',', '')),
    # etc
    }
Run Code Online (Sandbox Code Playgroud)


小智 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)
Run Code Online (Sandbox Code Playgroud)

产生以下结果

{'files': [
  {'Bitrate': 131, 'Encoder': 'Gogo (after 3.0)', 'Name': 'some filename.mp3'},
  {'Bitrate': 128, 'Encoder': 'iTunes', 'Name': 'another filename.mp3'}
]}
Run Code Online (Sandbox Code Playgroud)

想要将数据解析为对象而不是字典?你也可以这样做

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)
Run Code Online (Sandbox Code Playgroud)

产生输出

[AudioFile(name=some filename.mp3, encoder=Gogo (after 3.0), bit_rate=131),
 AudioFile(name=another filename.mp3, encoder=iTunes, bit_rate=128)]
Run Code Online (Sandbox Code Playgroud)