返回介绍

3.3 XML 数据

发布于 2024-01-27 21:43:11 字数 18593 浏览 0 评论 0 收藏 0

XML 格式的数据既便于机器读取,也便于人工读取。但是对于本章的数据集来说,预览并理解 CSV 文件和 JSON 文件要比 XML 文件容易得多。幸运的是,数据本身是相同的,我们也比较熟悉。下载预期寿命数据的 XML 版本(下载地址:http://apps.who.int/gho/athena/data/GHO/WHOSIS_000001,WHOSIS_000002.xml?filter=COUNTRY:*;YEAR:2015),并将 XML 文件与本章其他内容保存在同一文件夹下。

 如果文件的扩展名是 .xml,那么它是 XML 数据。如果文件扩展名是 .html 或 .xhtml,有时也可以用 XML 解析器来解析。

在处理所有数据时,我们先在代码编辑器中打开文件来预览一下。如果你滚动查看一下文件,会发现我们在 CSV 的例子中已经熟悉的数据。但数据看起来又不大一样,因为它用的是 XML 格式,使用了一种叫作标签的东西。

 XML 是一种标记语言,也就是说,它具有包含格式化数据的文档结构。XML 文档本质上只是格式特殊的数据文件。

下面的数据片段是我们要处理的 XML 数据的一个样本。在这个例子中,<Observation />、<Dim /> 和 <Display /> 都是标签。标签(或节点)以层次化和结构化的方式保存数据:

<GHO ...>
  <Data>
    <Observation FactID="4543040" Published="true"
    Dataset="CYCU" EffectiveDate="2014-03-27" EndDate="2900-12-31">
      <Dim Category="COUNTRY" Code="SOM"/>
      <Dim Category="REGION" Code="EMR"/>
      <Dim Category="WORLDBANKINCOMEGROUP" Code="WB_LI"/>
      <Dim Category="GHO" Code="WHOSIS_000002"/>
      <Dim Category="YEAR" Code="2012"/>
      <Dim Category="SEX" Code="FMLE"/>
      <Dim Category="PUBLISHSTATE" Code="PUBLISHED"/>
      <Value Numeric="46.00000">
        <Display>46</Display>
      </Value>
    </Observation>
    <Observation FactID="4209598" Published="true"
    Dataset="CYCU" EffectiveDate="2014-03-25" EndDate="2900-12-31">
      <Dim Category="WORLDBANKINCOMEGROUP" Code="WB_HI"/>
      <Dim Category="YEAR" Code="2000"/>
      <Dim Category="SEX" Code="BTSX"/>
      <Dim Category="COUNTRY" Code="AND"/>
      <Dim Category="REGION" Code="EUR"/>
      <Dim Category="GHO" Code="WHOSIS_000001"/>
      <Dim Category="PUBLISHSTATE" Code="PUBLISHED"/>
      <Value Numeric="80.00000">
        <Display>80</Display>
      </Value>
    </Observation>
  </Data>
</GHO>

在 XML 文件中有两个位置可以保存数据值:一个位置是在两个标签之间,比如在 <Display>46</Display> 中,<Display> 标签的值是 46;另一个位置是标签的属性,比如在 <Dim Category="COUNTRY" Code="SOM"/> 中,Category 属性的值是"COUNTRY",Code 属性的值是"SOM"。XML 属性可以保存特定标签的额外信息,这些标签又嵌套在另一个标签中。

在 JSON 中你可以用键 / 值对来保存数据,而在 XML 中保存数据可以是两个一组甚至三四个一组。XML 用标签和属性来保存数据,类似于 JSON 中的键。所以我们再来看一下 Display 标签,这个标签的值保存在开始标签和结束标签之间。再来看一下 Dim 节点,它有两个不同的属性(Category 和 Code),两个属性都有对应的值。XML 可以在每个节点中保存不止一个属性。如果你熟悉 HTML 的话,应该很熟悉这一点。这是因为 HTML 与 XML 密切相关:它们都在节点(或标签)内包含有属性,它们也都是标记语言(想了解什么是标记语言,可以去 https://en.wikipedia.org/wiki/Markup_language 查看)。

 在 XML 标签结构和属性命名方面虽然有许多著名的标准,但主要结构其实是由设计或创建 XML 的人(或机器)决定的。如果你用的是来自不同来源的数据集,你不能认为它们的格式是一致的。想了解 XML 最佳实践的更多内容,IBM 给出了许多好的观点(参见 http://www.ibm.com/developerworks/library/x-eleatt/)。

如何导入XML数据

前面我们对数据已经有了一定的了解,下面将文件导入成 Python 可用的格式。为了从 XML 格式中提取数据并导入 Python,需要编写这些代码:

from xml.etree import ElementTree as ET

tree = ET.parse('data-text.xml')
root = tree.getroot()

data = root.find('Data')

all_data = []

for observation in data:
  record = {}
  for item in observation:

    lookup_key = item.attrib.keys()[0]

    if lookup_key == 'Numeric':
      rec_key = 'NUMERIC'
      rec_value = item.attrib['Numeric']
    else:
      rec_key = item.attrib[lookup_key]
      rec_value = item.attrib['Code']

    record[rec_key] = rec_value

  all_data.append(record)

print all_data

可以看出来,这比 CSV 和 JSON 的代码都要复杂一些。

我们来仔细看一下。在代码编辑器中创建一个新文件,并将其保存在之前保存代码的文件夹中。文件名叫作 import_xml_data.py。另外,如果你是从 WHO 网站直接下载的数据,而不是从本书的仓库中下载的,你需要将保存的 XML 文件重命名为 data-text.xml,并将它与代码放在同一个文件夹下。

首先导入 ElementTree(https://docs.python.org/2/library/xml.etree.elementtree.html),这是我们用来解析 XML 的内置库的一部分:

from xml.etree import ElementTree as ET

 前面说过,解决问题的方法往往有许多种。本例中用的是 ElementTree,你也可以用一个叫作 lxml 的库(http://lxml.de/),或者另一个叫作 minidom 的库(https://docs.python.org/2/library/xml.dom.minidom.html)。这三种方法都可以用来解决相同的问题,如果你发现一个很好的例子,用的是其中一个库,我们建议你再用另外一个库对数据进行探索。在学习 Python 的过程中,选择那些看起来最容易理解的库(在多数情况下,这也是最好的选择)。

与之前相比,这个 import 语句中多了一段:as ET。我们导入的是 ElementTree,但把它叫作 ET。为什么要这么做?因为我们很懒,不想每次用到这个库时都输入 ElementTree。在导入名字很长的类或函数时,这是很常见的做法,但并不是强制性的要求。as 告诉 Python,我们想用 ET 来代表 ElementTree。

接下来,调用 ET 类的 parse 方法,这一方法将会对我们传入文件中的数据进行解析。由于我们要解析的文件位于同一文件夹下,所以文件名中不需要包含文件路径:

tree = ET.parse('data-text.xml')

parse 方法返回一个 Python 对象,人们一般会把它保存在变量 tree 中。在谈论 XML 时,(tree)指的是整个 XML 对象,以 Python 能够理解并解析的方式保存。

为了理解遍历树(及其包含的数据)的方法,我们从树的根元素(root)开始。根节点是第一个 XML 标签。调用 getroot 函数来获取树的根元素:

root = tree.getroot()

如果你在上一条语句后面加上 print root,打印出 root 的内容,会发现,输出的是 XML 树中根元素的 Python 表示(看起来应该像这样:<Element 'GHO' at 0x1079e79d0>2)。从这个表示中我们很快可以看出,ElementTree 找出了 XML 文档的根标签或最外层标签,就是标签名为 GHO 的 XML 节点。

2对于由十六进制数字组成的长字符串表示的 Python 对象,这是 Python 显示内存地址信息的方法。我们的数据处理过程用不到这些内容,所以如果你的内存地址与我们的有所不同,请不必在意。

前面我们找到了根标签,下面要学习如何访问想要的数据。在本章 CSV 和 JSON 两节中,我们对数据进行了分析,知道要处理的是什么数据。我们需要遍历整个 XML 树,将同样的数据提取出来。为了理解要寻找的数据,需要先理解 XML 树的整体结构与格式。下面,将前面的 XML 文件简化一下,删除数据,这样就可以只看核心结构:

<GHO>
  <Data>
    <Observation>
      <Dim />
      <Dim />
      <Dim />
      <Dim />
      <Dim />
      <Dim />
      <Value>
        <Display>
        </Display>
      </Value>
    </Observation>
    <Observation>
      <Dim />
      <Dim />
      <Dim />
      <Dim />
      <Dim />
      <Dim />
      <Value>
        <Display>
        </Display>
      </Value>
    </Observation>
  </Data>
</GHO>

在纵览文件结构时可以看到,每一“行”数据都包含在一个 Observation 标签中。在每一个 Observation 节点内,这些数据行又分别包含在 Dim、Value 和 Display 节点中。

目前我们有三行代码。为了研究如何用 Python 将这些节点提取出来,在现有代码的末尾加上 print dir(root),然后保存文件并在命令行中运行:

python import_xml_data.py

你会看到变量 root 所有的方法和属性。你的代码应该是这样的:

from xml.etree import ElementTree as ET

tree = ET.parse('data-text.xml')
root = tree.getroot()

print dir(root)

运行该文件,你应该会看到下面的输出:

['__class__', '__delattr__', '__delitem__', '__dict__', '__doc__',
'__format__', '__getattribute__', '__getitem__', '__hash__', '__init__',
'__len__', '__module__', '__new__', '__nonzero__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__setitem__', '__sizeof__',
'__str__', '__subclasshook__', '__weakref__', '_children', 'append', 'attrib',
'clear', 'copy', 'extend', 'find', 'findall', 'findtext', 'get',
'getchildren', 'getiterator', 'insert', 'items', 'iter', 'iterfind',
'itertext', 'keys', 'makeelement', 'remove', 'set', 'tag', 'tail', 'text']

假设文件太大,无法打开,我们也不知道文件的结构。在处理大型 XML 数据集时经常会遇到这样的问题。我们能怎么做?首先调用 dir(root) 来查看 root 对象都有哪些方法。我们注意到 getchildren 方法,也许可以用来查看 Observation 节点的子元素。在查阅官方最新文档(https://docs.python.org/2/library/xml.etree.elementtree.html#xml.etree.ElementTree.Element.getchildren)以及 Stack Overflow 上的一个问题(http://stackoverflow.com/questions/10408927/how-to-get-all-sub-elements-of-an-element-tree-with-python-elementtree)之后,我们发现,getchildren 方法可以返回子元素,但官方文档不建议继续使用该方法。如果你想用的某个方法已经不建议使用或者即将弃用,你应该尝试换用库作者推荐的方法。

 如果不建议使用某个方法、类或函数的话,在库或模块的未来版本中很可能会删除它们对应的功能。因此,在任何时候你都应该避免使用不建议使用的方法或类,并通读文档,因为作者很可能会建议未来使用的替代方法或类。

根据官方文档的建议,如果想查看根元素的子元素,应该使用 list(root)。如果我们的文件很大,返回子元素可以让我们查看数据及其结构,而不会产生超级长的输出。让我们来试一下。

将这行代码:

print dir(root)

替换成:

print list(root)

在命令行中再次运行该文件。你应该会得到下面的输出,是一个由 Element 对象构成的列表(对于本例来说,元素指的是 XML 节点):

[<Element 'QueryParameter' at 0x101bfd290>,
<Element 'QueryParameter' at 0x101bfd350>,
<Element 'QueryParameter' at 0x101bfd410>,
<Element 'QueryParameter' at 0x101bfd590>,
<Element 'QueryParameter' at 0x101bfd610>,
<Element 'QueryParameter' at 0x101bfd650>,
<Element 'Copyright' at 0x101bfd690>,
<Element 'Disclaimer' at 0x101bfd710>,
<Element 'Metadata' at 0x101bfd790>,
<Element 'Data' at 0x102540250>]

列表包含的 Element 对象分别叫作 QueryParameter、Copyright、Disclaimer、Metadata 和 Data。我们可以遍历这些元素来探索它们的内容,这样才能更好地理解如何提取我们想要的数据。

在 XML 树里面搜索数据时,Data 元素可能是一个很好的出发点。现在我们已经找到了 Data 元素,可以重点研究这个子元素。获取 Data 元素有好几种方法,这里用 find 方法。根元素的 find 方法可以利用标签名来搜索子元素。然后,我们就可以获取 Data 元素的子元素,看看下一步应该做什么。

将这行代码:

print list(root)

替换成:

data = root.find('Data')

print list(data)

 我们还可以使用 findall 方法。find 和 findall 的区别在于,find 返回的是匹配的第一个元素,而 findall 返回的是匹配的所有元素。我们知道只有一个 Data 元素,所以我们用的是 find 而不是 findall。如果有不止一个元素,要用 findall 方法获取所有匹配元素的列表,然后遍历这些元素。

修改完代码后重新运行该文件,你会看到输出一个超级长的列表,列表由 Observation 元素组成。这些是我们的数据点。虽然输出里包含很多信息,但你可以看出它是一个列表,因为最后一个字符是 ],这是列表结束的符号。

我们来遍历这个列表中的数据。每个 Observation 元素代表一行数据,里面应该会包含更多的信息。我们可以分别遍历这些元素,看看都有什么子元素。对于 Python 中的 Element 对象,可以遍历其所有的子元素,就像遍历列表一样。因此,我们可以遍历每一个 Observation 元素及其每一个子元素。这是我们第一次使用包含循环的循环,从中我们应该可以知道是否还有更多包含数据的子元素。

 由于 XML 以节点、子节点和属性的方式保存数据,你会经常采用探索每一个节点和子节点(或者元素和子元素)的方法,直到你掌握了数据的结构,以及如何用 Python 来查看这些数据。

将这行代码:

print list(root)

替换成:

for observation in data:
  for item in observation:
    print item

然后重新运行该文件。

输出的是许多 Dim 和 Value 对象。我们尝试几种不同的方法,来探索这些元素中可能包含的内容。Python 的 Element 对象有好几种查看数据的方法。每个 Element 节点都有一个属性 text,可以给出节点内包含的文本。

将这行代码:

print item

替换成:

print item.text

然后重新运行该文件。

发生了什么?你得到的返回值应该是许多 None。这是因为很多元素的标签之间没有任何文本,所以这些元素的 item.text 都不存在。我们来看一下数据样本里 <Dim /> 的结构。例如:

<Dim Category="YEAR" Code="2000"/>

在 Python 中,只有在节点中包含文本的情况下,item.text 才是有用的,像这样:

<Dim Category="YEAR">2000</Dim>

对于第二个例子,item.text 返回的是 2000。

XML 数据可以有许多种结构。我们需要的信息包含在 XML 里,只是不在一眼就能发现的地方。我们来继续探索。

另一个要查看的地方在子元素中。我们来检查一下里面是否包含子元素。将这行代码:

print item.text

替换成:

print list(item)

修改后重新运行代码,从输出中可以看出,某些元素(但不是全部)具有子元素。有意思!这些元素其实是 Value 元素。我们来看一下数据样本中这些元素的结构:

<Value>
  <Display>
  </Display>
</Value>

如果想探索这些子元素,还需要写一个类似之前写过的循环,来遍历每一个 Observation 中的元素。

Python 中的 Element 对象还有另一个方法可以调用,叫作 attrib,它可以返回每一个节点的属性。在查看 XML 结构时我们已经知道,如果节点的标签之间没有值,那么在标签内通常会有属性。

想要查看节点的属性,将这行代码:

print list(item)

替换成:

print item.attrib

重新运行代码,可以看到,输出是由包含在属性中的数据组成的许多字典。我们希望将每一行数据保存成一个字典,而不是将每一个元素及其属性保存在不同的字典中。下面是 attrib 输出的一条记录:

{'Category': 'PUBLISHSTATE', 'Code': 'PUBLISHED'}
{'Category': 'COUNTRY', 'Code': 'ZWE'}
{'Category': 'WORLDBANKINCOMEGROUP', 'Code': 'WB_LI'}
{'Category': 'YEAR', 'Code': '2012'}
{'Category': 'SEX', 'Code': 'BTSX'}
{'Category': 'GHO', 'Code': 'WHOSIS_000002'}
{'Category': 'REGION', 'Code': 'AFR'}
{'Numeric': '49.00000'}

在 CSV 的例子中,我们得到了每条数据记录组成的字典,我门试着把上面的输出转换成类似的格式。XML 数据字典的键稍有不同,因为 WHO 在 XML 数据集中提供的数据与 CSV 数据集并不相同。我们将会把数据转换成下面的格式,但键名可以不同。这基本不会影响我们对数据的使用。

提醒一下,CSV reader 的样本数据记录如下:

{
   'Indicator': 'Healthy life expectancy (HALE) at birth (years)',
   'Country': 'Zimbabwe',
   'Comments': '',
   'Display Value': '51',
   'World Bank income group': 'Low-income',
   'Numeric': '51.00000',
   'Sex': 'Female',
   'High': '',
   'Low': '',
   'Year': '2012',
   'WHO region': 'Africa',
   'PUBLISH STATES': 'Published'
}

对于样本数据记录,我们希望将 XML 数据转换成下面的样子。在解析完 XML 树之后,我们希望数据的格式就是这样:

{
   'COUNTRY': 'ZWE',
   'GHO': 'WHOSIS_000002',
   'Numeric': '49.00000',
   'PUBLISHSTATE': 'PUBLISHED',
   'REGION': 'AFR',
   'SEX': 'BTSX',
   'WORLDBANKINCOMEGROUP': 'WB_LI',
   'YEAR': '2012'
}

注意 High 和 Low 字段是缺失的。如果 XML 数据集中这两个字段没有缺失的话,我们会把它们添加到新字典的键中。Display 的值也是缺失的。我们决定不用这个值,因为它和 Numeric 的值相同。

现在你的代码应该是像这样的:

from xml.etree import ElementTree as ET

tree = ET.parse('data-text.xml')
root = tree.getroot()

data = root.find('Data')

for observation in data:
  for item in observation:
    print item.attrib

想要创建数据结构,首先要为每一条数据记录创建一个空字典。我们向空字典中添加键值对,然后将每一条数据记录添加到一个列表中,这样我们最终的列表中就包含了所有的数据记录(类似于前面的 CSV 数据)。

首先创建用来保存数据的空字典和空列表。在最外层 for 循环上面,添加一行代码:all_data = [],然后将 record = {} 作为 for 循环的第一行,像这样:

all_data = []

for observation in data:
  record = {}
  for item in observation:
    print item.attrib

现在要找出每一行的键和值,并将其添加到数据记录的字典中。每一次调用 attrib,都会得到包含一个或多个键值对的字典,像这样:

{'Category': 'YEAR', 'Code': '2012'}

看上去 Category 键的值(这里是 YEAR)应该是新字典的键,而 Code 的值(这里是 2012)应该被设成对应的值。回想第 2 章学过的内容,字典的键应该易于检索(比如 YEAR),字典的值应该包含与键对应的值(比如 2012)。认识到这一点,前面一行将变成:

'YEAR': '2012'

将代码中的 print item.attrib 修改成 print item.attrib.keys(),然后重新运行该文件:

for item in observation:
  print item.attrib.keys()

输出的是每一个属性字典的键。我们想查看字典的键,这样才能构建新字典的键值对。我们发现只有两种不同的输出:['Category', 'Code'] 和 ['Numeric']。我们对两种情况分别处理。根据前面的研究,对于同时具有 Category 和 Code 的元素,需要用 Category 的值作为键,用 Code 的值作为值。

要做到这一点,在 item.attrib.keys() 的结尾添加 [0]:

for item in observation:
  lookup_key = item.attrib.keys()[0]
  print lookup_key

这叫索引(indexing)。返回的是列表的第一个元素。

使用列表索引

对于 Python 中的列表或其他可迭代对象,索引指的是取出列表的第 n 个对象。Python 的索引从 0 开始,也就是说,第一个元素的编号是 0,第二个元素是 1,以此类推。由于我们的列表中有一个或两个元素,我们只想要第一个元素,所以在代码中加了 [0]。

回头看一下上一章中狗狗的例子:

dog_names = ['Joker', 'Simon', 'Ellie', 'Lishka', 'Fido']

如果想从列表中提取出Ellie,那你想要的是列表的第三个元素。由于索引编号从 0 开始,你可以用下面的代码提取出这个元素:

dog_names[2]

在 Python 解释器中试一下上面的代码,然后尝试提取出 Simon。如果用负数作为索引会怎么样?(提示:负数索引是从列表末尾倒着向前数!)

重新运行代码,输出应该是这样的:

Category
Category
Category
Category
Category
Category
Category
Numeric

现在我们有了键的名字,下面要寻找键对应的值。我们要用 Category 键的值作为新字典的键。在内层 for 循环中创建一个新变量 rec_key,用来保存 item.attrib[lookup_key] 返回的值:

for item in observation:
  lookup_key = item.attrib.keys()[0]
  rec_key = item.attrib[lookup_key]
  print rec_key

修改完成后,在命令行中重新运行代码。对于每一条数据记录,我们得到下面的值:

PUBLISHSTATE
COUNTRY
WORLDBANKINCOMEGROUP
YEAR
SEX
GHO
REGION
49.00000

看起来都很适合做新字典的键,除了最后一个。这是因为最后一个元素是 Numeric 字典,而不是我们要处理的 Category 字典。如果想保留这些数据供后续使用,需要用 if 语句为这些数值元素创建一种特殊情况。

Python 的 if 语句

if 语句最基本的形式,是用来控制代码流的方法。if 语句是在告诉代码:如果满足该条件,那就执行给定的命令。

if 语句的另一种用法是与 else 一起使用。if-else 语句的意思是:如果满足第一个条件,那么执行相应的命令;但如果不满足该条件,那么执行 else 语句中的命令。

除了 if 和 if-else 之外,你还会将 == 作为比较运算符。== 用来给变量赋值,而 == 用来检验两个值是否相等。另外,!= 用来检验两个值是否不相等。这两个运算符返回的都是布尔值:True 或 False。

在 Python 解释器中试试下面的例子:

x = 5
    
if x == 5:
  print 'x is equal to 5.'

你看到了什么? x == 5 返回的是 True,所以打印出了相应的文字。现在试一下这个:

x = 3
    
if x == 5:
  print 'x is equal to 5.'
else:
  print 'x is not equal to 5.'

本例中 x 等于 3,不等于 5,所以你应该会看到 else 代码块中 print 语句的输出。在 Python 中,你可以用 if 和 if-else 语句来帮助控制代码流的逻辑。

当 lookup_key 等于 Numeric 时,我们希望使用 Numeric 作为新字典的键,而不是用它对应的值作为新字典的键(就像 Category 键那样)。将代码修改成:

for item in observation:

  lookup_key = item.attrib.keys()[0]
  if lookup_key == 'Numeric':
    rec_key = 'NUMERIC'
  else:
    rec_key = item.attrib[lookup_key]

  print rec_key

运行修改后的代码,现在所有的键看起来应该都是我们想要的。下面提取出想要保存在新字典中的值,并将它们与这些键相关联。对于 Numeric,问题比较简单,因为我们只想要 Numeric 键对应的值。对代码作如下修改:

  if lookup_key == 'Numeric':
    rec_key = 'NUMERIC'
    rec_value = item.attrib['Numeric']
  else:
    rec_key = item.attrib[lookup_key]
    rec_value = None

  print rec_key, rec_value

运行修改后的代码,你会发现 Numeric 的 rec_value 已经匹配好了。比如:

NUMERIC 49.00000

对于其他所有的值,我们将 rec_value 设置成 None。在 Python 中,None 用来表示一个空值。我们来将这些空值填上真实的数值。记得每一条数据记录都有一个 Category 键和一个 Code 键,像这样:{'Category': 'YEAR', 'Code': '2012'}。对于这些元素,我们想将 Code 的值保存为 rec_value。修改这一行代码:rec_value = None,将 if-else 语句改成下面这样:

  if lookup_key == 'Numeric':
    rec_key = 'NUMERIC'
    rec_value = item.attrib['Numeric']
  else:
    rec_key = item.attrib[lookup_key]
    rec_value = item.attrib['Code']

  print rec_key, rec_value

重新运行代码,你现在应该会看到,rec_key 和 rec_value 都有相应的值。下面来创建字典:

if lookup_key == 'Numeric':
  rec_key = 'NUMERIC'
  rec_value = item.attrib['Numeric']
else:
  rec_key = item.attrib[lookup_key]
  rec_value = item.attrib['Code']

record[rec_key] = rec_value ➊

❶ 将每一个键值对添加到 record 字典中。

我们还需要将每一条数据记录添加到 all_data 列表中。在 2.3.3 节中讲过,可以用列表的 append 方法向列表中添加元素。在外层 for 循环的结尾,record 中已经包含了每一个子元素的键,这时我们将 record 添加到列表中。最后,在文件结尾添加 print 来查看数据。

将 XML 树转换成字典的全部代码应该是这样的:

from xml.etree import ElementTree as ET

tree = ET.parse('data-text.xml')
root = tree.getroot()

data = root.find('Data')

all_data = []

for observation in data:
  record = {}
  for item in observation:

    lookup_key = item.attrib.keys()[0]

    if lookup_key == 'Numeric':
      rec_key = 'NUMERIC'
      rec_value = item.attrib['Numeric']
    else:
      rec_key = item.attrib[lookup_key]
      rec_value = item.attrib['Code']

    record[rec_key] = rec_value

  all_data.append(record)

print all_data

运行上面的代码,你会看到一个长列表,列表的元素是每一条数据记录组成的字典,与 CSV 例子中的相同:

{'COUNTRY': 'ZWE', 'REGION': 'AFR', 'WORLDBANKINCOMEGROUP': 'WB_LI',
'NUMERIC': '49.00000', 'SEX': 'BTSX', 'YEAR': '2012',
'PUBLISHSTATE': 'PUBLISHED', 'GHO': 'WHOSIS_000002'}

可以看出来,从 XML 中提取数据要稍复杂一些。有时 CSV 文件和 JSON 文件也并不像本章的例子那样容易处理,但它们通常比 XML 文件容易处理一些。但是,作为 Python 开发人员,处理 XML 数据让你可以深入探索并成长,让你可以创建空列表和字典,然后向里面填充数据。在研究如何从 XML 树结构里提取数据的过程中,你还锻炼了自己的调试能力。在通往成为更优秀的数据分析员的路上,这些都是宝贵的经验。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文