- 本书赞誉
- 前言
- 目标读者
- 不适合阅读本书的读者
- 本书结构
- 什么是数据处理
- 遇到困难怎么办
- 排版约定
- 使用代码示例
- 致谢
- 第 1 章 Python 简介
- 第 2 章 Python 基础
- 第 3 章 供机器读取的数据
- 第 4 章 处理 Excel 文件
- 第 5 章 处理 PDF 文件 以及用 Python 解决问题
- 第 6 章 数据获取与存储
- 第 7 章 数据清洗:研究、匹配与格式化
- 第 8 章 数据清洗:标准化和脚本化
- 第 9 章 数据探索和分析
- 第 10 章 展示数据
- 第 11 章 网页抓取:获取并存储网络数据
- 第 12 章 高级网页抓取:屏幕抓取器与爬虫
- 第 13 章 应用编程接口
- 第 14 章 自动化和规模化
- 第 15 章 结论
- 附录 A 编程语言对比
- 附录 B 初学者的 Python 学习资源
- 附录 C 学习命令行
- 附录 D 高级 Python 设置
- 附录 E Python 陷阱
- 附录 F IPython 指南
- 附录 G 使用亚马逊网络服务
- 关于作者
- 关于封面
3.3 XML 数据
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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论