我们在本章使用的数据源,是由世界银行提供的服务,它是为发展中国家提供资金和知识的国际组织。作为其工作的一部分,它需要识别哪种类型的支持最有效,确定需求在哪里,评估对发展中国家的经济,生活质量,环境是否产生了影响。世界银行有一组数据,称为世界发展指标(World Development Indicators),包含了许多国家的信息,而且数据在线可用。在本章,我们将使用有关环境信息,更具体的森林覆盖区域信息。由世界银行提供的数据可免费使用,但是,首先需要在它的网站上进行注册。
提示
要注册,先去 http://developer.worldbank.org,填写完表格,收到确认的电子邮件后,就可以返回网站,获取 API 密钥,用于将请求发送到世界银行服务。网站还提供了有关服务的文档和简明教程;你可以在那里看,但是,我们在这一章,也会解释所使用到的内容。网页上有一项重要的功能,是查询生成器,可以交互地运行、配置查询,显示的 URL 可以用于以编程方式请求数据。
世界银行使用简单的基于 HTTP 的服务公开数据,因此,我们可以使用早前创建的 downloadUrl 函数。如果你看过文档,或者体验过查询生成器一段时间,将很快学会请求 URL 的结构。地址总是指向服务器上的同一页面,所有额外属性在 URL 中以键-值对的形式提供。在清单 13.5 中,我们首先创建一个函数,来构造请求 URL,键-值对包含在 F# 列表中,这样,我们可以更方便地访问数据。
清单13.5 生成请求 URL (F#)
open System.Web
let worldBankKey = "xxxxxxxxxx" <-- 在这里,提供世界银行的密钥
let worldBankUrl(functions, props) =
seq { yield "http://open.worldbank.org"
for item in functions do | [1]
yield "/" + HttpUtility.UrlEncode(item:string) |
yield "?per_page=100"
yield "&api_key=" + worldBankKey
for key, value in props do
yield "&" + key + "=" + HttpUtility.UrlEncode(value:string) } [2]
|> String.concat "&"
函数 worldBankUrl 包含一个序列表达式,生成字符串集合,然后,再将它们连接成一个 URL。
在序列表达式中,我们首先返回 URL 的基本部分。接下来,添加路径,指向由服务器提供所需的函数,例如,函数可能是“/keywords/Wood”,因此,我们可以得到描述函数名各部分的列表,再把所有这些用分隔符“/”连接起来[1]。指定函数以后,再添加 API 密钥和页面长度,这部分也是在这一章我们需要的所有请求所共享的。最后,处理由用户指定的额外属性,迭代由参数值 props 指定的所有键-值对,返回字符串“&键=值” [2]。
要确保 URL 格式正确,我们使用了 System.Web 命名空间下的 HttpUtility 类。如果编译文件,作为项目的一部分,需要添加对 System.Web 程序集引用,因此它不是默认引用。这个工具可以将任意字符串编码为可以包含在 URL 中的字符串。UrlEncode 方法有不同的重载,我们使用类型注解(type annotation)指定参数值 value 的类型是字符串。
在本章,我们将创建 F# 脚本文件,而不是传统的应用程序,因此,下一步就是写几个 F# 交互命令,可以立即执行,检查写的函数运行是否正确。这种“测试请求(test request)”对于银行所使用的数据格式,也是非常有用,因为,我们知道后面要解析数据。
由世界银行提供的统计数据既可用于个别国家,也可以按地区或收入分组,这些汇总统计更利于看到总体趋势。我们要做的第一件事,就是获取所有有关可用的分组信息,在网站上可以尝试使用查询生成器。首先,在 Country Calls 选项卡上选择 Countries(国家),并输入 API 密钥。要得到按国家分组的汇总列表,从Region(区域)列表中选择 Aggregates(汇总),然后,运行请求。清单 13.6 显示了使用 F# Interactive 运行相同的请求。
清单13.6 测试世界银行数据服务 (F# Interactive)
> let url =
worldBankUrl(["countries"], | [1]
["region", "NA" ];; | 用指定属性生成 URL
val url : string =
http://open.worldbank.org/countries?per_page=100&
api_key=hq8byg8k7t2fxc6hp7jmbx26®ion=NA"
> Async.RunSynchronously(downloadUrl(url));; [2] <-- 下载页面为字符串
val it : string = "<?xml version=\"1.0\" encoding=\"utf-8\" (...)"
我们首先使用刚刚实现的函数创建 URL[1],把 countries 作为我们想要调用的函数名,传递给它;另一个参数 region 指定我们想要列举的国家类型,NA 值表示我们对汇总的国家信息感兴趣。因为我们使用 F# Interactive,能立即看到组成的 URL,它包含了所有指定的参数,世界银行的密钥,以及为指定每个页面至多返回100 条记录的标志。后面,当我们需要获得更大数量的指标时,会讨论分页输出。
有了 URL 以后,我们就可以将其复制到浏览器,看看世界银行返回的数据。如果以编程方式下载页面,可以使用 downloadUrl 函数(来自清单 13.1)。因为使用任何网络操作,下载都可能会失败;如果我们手工运行请求,这并不重要,但是,当我们执行批量操作,并行从 URL 下载数据的时候,就需要代码有从非致命故障中恢复的能力。