第 17 章 使用API

第 17 章 使用API - 图1

在本章中,你将学习如何编写一个独立的程序,并对其获取的数据进行可视化。这个程序将使用Web应用编程接口(API)自动请求网站的特定信息而不是整个网页,再对这些信息进行可视化。由于这样编写的程序始终使用最新的数据来生成可视化,因此即便数据瞬息万变,它呈现的信息也都是最新的。

17.1 使用Web API

Web API是网站的一部分,用于与使用非常具体的URL请求特定信息的程序交互。这种请求称为API调用。请求的数据将以易于处理的格式(如JSON或CSV)返回。依赖于外部数据源的大多数应用程序都依赖于API调用,如集成社交媒体网站的应用程序。

17.1.1 Git和GitHub

本章的可视化将基于来自GitHub的信息,这是一个让程序员能够协作开发项目的网站。我们将使用GitHub的API来请求有关该网站中Python项目的信息,然后使用Pygal生成交互式可视化,以呈现这些项目的受欢迎程度。

GitHub(https://github.com/)的名字源自Git,Git是一个分布式版本控制系统,让程序员团队能够协作开发项目。Git帮助大家管理为项目所做的工作,避免一个人所做的修改影响其他人所做的修改。你在项目中实现新功能时,Git将跟踪你对每个文件所做的修改。确定代码可行后,你提交所做的修改,而Git将记录项目最新的状态。如果你犯了错,想撤销所做的修改,可轻松地返回以前的任何可行状态(要更深入地了解如何使用Git进行版本控制,请参阅附录D)。GitHub上的项目都存储在仓库中,后者包含与项目相关联的一切:代码、项目参与者的信息、问题或bug报告等。

对于喜欢的项目,GitHub用户可给它加星(star)以表示支持,用户还可跟踪他可能想使用的项目。在本章中,我们将编写一个程序,它自动下载GitHub上星级最高的Python项目的信息,并对这些信息进行可视化。

17.1.2 使用API调用请求数据

GitHub的API让你能够通过API调用来请求各种信息。要知道API调用是什么样的,请在浏览器的地址栏中输入如下地址并按回车键:

  1. https://api.github.com/search/repositories?q=language:python&sort=stars

这个调用返回GitHub当前托管了多少个Python项目,还有有关最受欢迎的Python仓库的信息。下面来仔细研究这个调用。第一部分(https://api.github.com/)将请求发送到GitHub网站中响应API调用的部分;接下来的一部分(search/repositories)让API搜索GitHub上的所有仓库。

repositories后面的问号指出我们要传递一个实参。q表示查询,而等号让我们能够开始指定查询(q=)。通过使用language:python,我们指出只想获取主要语言为Python的仓库的信息。最后一部分(&sort=stars)指定将项目按其获得的星级进行排序。

下面显示了响应的前几行。从响应可知,该URL并不适合人工输入。

  1. {
  2. "total_count": 713062,
  3. "incomplete_results": false,
  4. "items": [
  5. {
  6. "id": 3544424,
  7. "name": "httpie",
  8. "full_name": "jkbrzt/httpie",
  9. --snip--

从第二行输出可知,编写本书时,GitHub总共有713 062个Python项目。"incomplete_results"的值为false,据此我们知道请求是成功的(它并非不完整的)。倘若GitHub无法全面处理该API,它返回的这个值将为true。接下来的列表中显示了返回的"items",其中包含GitHub上最受欢迎的Python项目的详细信息。

17.1.3 安装requests

requests包让Python程序能够轻松地向网站请求信息以及检查返回的响应。要安装requests,请执行类似于下面的命令:

  1. $ pip install --user requests

如果你还没有使用过pip,请参阅12.2.1节(根据系统的设置,你可能需要使用这个命令的稍微不同的版本)。

17.1.4 处理API响应

下面来编写一个程序,它执行API调用并处理结果,找出GitHub上星级最高的Python项目:

python_repos.py

  1. import requests
  2. # 执行API调用并存储响应
  3. url = 'https://api.github.com/search/repositories?q=language:python&sort=stars'
  4. r = requests.get(url)
  5. print("Status code:", r.status_code)
  6. # 将API响应存储在一个变量中
  7. response_dict = r.json()
  8. # 处理结果
  9. print(response_dict.keys())

在❶处,我们导入了模块requests。在❷处,我们存储API调用的URL,然后使用requests来执行调用(见❸)。我们调用get()并将URL传递给它,再将响应对象存储在变量r中。响应对象包含一个名为status_code的属性,它让我们知道请求是否成功了(状态码200表示请求成功)。在❹处,我们打印status_code,核实调用是否成功了。

这个API返回JSON格式的信息,因此我们使用方法json()将这些信息转换为一个Python字典(见❺)。我们将转换得到的字典存储在response_dict中。

最后,我们打印response_dict中的键。输出如下:

  1. Status code: 200
  2. dict_keys(['items', 'total_count', 'incomplete_results'])

状态码为200,因此我们知道请求成功了。响应字典只包含三个键:'items''total_count''incomplete_results'

注意 像这样简单的调用应该会返回完整的结果集,因此完全可以忽略与'incomplete_results'相关联的值。但执行更复杂的API调用时,程序应检查这个值。

17.1.5 处理响应字典

将API调用返回的信息存储到字典中后,就可以处理这个字典中的数据了。下面来生成一些概述这些信息的输出。这是一种不错的方式,可确认收到了期望的信息,进而可以开始研究感兴趣的信息:

python_repos.py

  1. import requests
  2. # 执行API调用并存储响应
  3. url = 'https://api.github.com/search/repositories?q=language:python&sort=stars'
  4. r = requests.get(url)
  5. print("Status code:", r.status_code)
  6. # 将API响应存储在一个变量中
  7. response_dict = r.json()
  8. print("Total repositories:", response_dict['total_count'])
  9. # 探索有关仓库的信息
  10. repo_dicts = response_dict['items']
  11. print("Repositories returned:", len(repo_dicts))
  12. # 研究第一个仓库
  13. repo_dict = repo_dicts[0]
  14. print("\nKeys:", len(repo_dict))
  15. for key in sorted(repo_dict.keys()):
  16. print(key)

在❶处,我们打印了与'total_count'相关联的值,它指出了GitHub总共包含多少个Python仓库。

'items'相关联的值是一个列表,其中包含很多字典,而每个字典都包含有关一个Python仓库的信息。在❷处,我们将这个字典列表存储在repo_dicts中。接下来,我们打印repo_dicts的长度,以获悉我们获得了多少个仓库的信息。

为更深入地了解返回的有关每个仓库的信息,我们提取了repo_dicts中的第一个字典,并将其存储在repo_dict中(见❸)。接下来,我们打印这个字典包含的键数,看看其中有多少信息(见❹)。在❺处,我们打印这个字典的所有键,看看其中包含哪些信息。

输出让我们对实际包含的数据有了更清晰的认识:

  1. Status code: 200
  2. Total repositories: 713062
  3. Repositories returned: 30
  4. Keys: 68
  5. archive_url
  6. assignees_url
  7. blobs_url
  8. --snip--
  9. url
  10. watchers
  11. watchers_count

GitHub的API返回有关每个仓库的大量信息:repo_dict包含68个键(见❶)。通过仔细查看这些键,可大致知道可提取有关项目的哪些信息(要准确地获悉API将返回哪些信息,要么阅读文档,要么像此处这样使用代码来查看这些信息)。

下面来提取repo_dict中与一些键相关联的值:

python_repos.py

  1. --snip--
  2. # 研究有关仓库的信息
  3. repo_dicts = response_dict['items']
  4. print("Repositories returned:", len(repo_dicts))
  5. # 研究第一个仓库
  6. repo_dict = repo_dicts[0]
  7. print("\nSelected information about first repository:")
  8. print('Name:', repo_dict['name'])
  9. print('Owner:', repo_dict['owner']['login'])
  10. print('Stars:', repo_dict['stargazers_count'])
  11. print('Repository:', repo_dict['html_url'])
  12. print('Created:', repo_dict['created_at'])
  13. print('Updated:', repo_dict['updated_at'])
  14. print('Description:', repo_dict['description'])

在这里,我们打印了表示第一个仓库的字典中与很多键相关联的值。在❶处,我们打印了项目的名称。项目所有者是用一个字典表示的,因此在❷处,我们使用键owner来访问表示所有者的字典,再使用键key来获取所有者的登录名。在❸处,我们打印项目获得了多少个星的评级,以及项目在GitHub仓库的URL。接下来,我们显示项目的创建时间(见❹)和最后一次更新的时间(见❺)。最后,我们打印仓库的描述。输出类似于下面这样:

  1. Status code: 200
  2. Total repositories: 713065
  3. Repositories returned: 30
  4. Selected information about first repository:
  5. Name: httpie
  6. Owner: jkbrzt
  7. Stars: 16101
  8. Repository: https://github.com/jkbrzt/httpie
  9. Created: 2012-02-25T12:39:13Z
  10. Updated: 2015-07-13T14:56:41Z
  11. Description: CLI HTTP client; user-friendly cURL replacement featuring intuitive UI, JSON support, syntax highlighting, wget-like downloads, extensions, etc.

从上述输出可知,编写本书时,GitHub上星级最高的Python项目为HTTPie,其所有者为用户jkbrzt,有16 000多个GitHub用户给这个项目加星。我们可以看到这个项目的仓库的URL,其创建时间为2012年2月,且最近更新了。最后,描述指出HTTPie用于帮助从终端执行HTTP调用(CLI是命令行界面的缩写)。

17.1.6 概述最受欢迎的仓库

对这些数据进行可视化时,我们需要涵盖多个仓库。下面就来编写一个循环,打印API调用返回的每个仓库的特定信息,以便能够在可视化中包含所有这些信息:

python_repos.py

  1. --snip--
  2. # 研究有关仓库的信息
  3. repo_dicts = response_dict['items']
  4. print("Repositories returned:", len(repo_dicts))
  5. print("\nSelected information about each repository:")
  6. for repo_dict in repo_dicts:
  7. print('\nName:', repo_dict['name'])
  8. print('Owner:', repo_dict['owner']['login'])
  9. print('Stars:', repo_dict['stargazers_count'])
  10. print('Repository:', repo_dict['html_url'])
  11. print('Description:', repo_dict['description'])

在❶处,我们打印了一条说明性消息。在❷处,我们遍历repo_dicts中的所有字典。在这个循环中,我们打印每个项目的名称、所有者、星级、在GitHub上的URL以及描述:

  1. Status code: 200
  2. Total repositories: 713067
  3. Repositories returned: 30
  4. Selected information about each repository:
  5. Name: httpie
  6. Owner: jkbrzt
  7. Stars: 16101
  8. Repository: https://github.com/jkbrzt/httpie
  9. Description: CLI HTTP client; user-friendly cURL replacement featuring intuitive UI, JSON support, syntax highlighting, wget-like downloads, extensions, etc.
  10. Name: django
  11. Owner: django
  12. Stars: 15028
  13. Repository: https://github.com/django/django
  14. Description: The Web framework for perfectionists with deadlines.
  15. --snip--
  16. Name: powerline
  17. Owner: powerline
  18. Stars: 4315
  19. Repository: https://github.com/powerline/powerline
  20. Description: Powerline is a statusline plugin for vim, and provides statuslines and prompts for several other applications, including zsh, bash, tmux, IPython, Awesome and Qtile.

上述输出中有一些有趣的项目,可能值得再看一眼。但不要在这上面花费太多时间,因为我们即将创建的可视化可让你更容易地看清结果。

17.1.7 监视API的速率限制

大多数API都存在速率限制,即你在特定时间内可执行的请求数存在限制。要获悉你是否接近了GitHub的限制,请在浏览器中输入https://api.github.com/rate_limit,你将看到类似于下面的响应:

  1. {
  2. "resources": {
  3. "core": {
  4. "limit": 60,
  5. "remaining": 58,
  6. "reset": 1426082320
  7. },
  8. "search": {
  9. "limit": 10,
  10. "remaining": 8,
  11. "reset": 1426078803
  12. }
  13. },
  14. "rate": {
  15. "limit": 60,
  16. "remaining": 58,
  17. "reset": 1426082320
  18. }
  19. }

我们关心的信息是搜索API的速率限制(见❶)。从❷处可知,极限为每分钟10个请求,而在当前这一分钟内,我们还可执行8个请求(见❸)。reset值指的是配额将重置的Unix时间或新纪元时间(1970年1月1日午夜后多少秒)(见❹)。用完配额后,你将收到一条简单的响应,由此知道已到达API极限。到达极限后,你必须等待配额重置。

注意 很多API都要求你注册获得API密钥后才能执行API调用。编写本书时,GitHub没有这样的要求,但获得API密钥后,配额将高得多。

17.2 使用Pygal可视化仓库

有了一些有趣的数据后,我们来进行可视化,呈现GitHub上Python项目的受欢迎程度。我们将创建一个交互式条形图:条形的高度表示项目获得了多少颗星。单击条形将带你进入项目在GitHub上的主页。下面是首次尝试这样做:

python_repos.py

  1. import requests
  2. import pygal
  3. from pygal.style import LightColorizedStyle as LCS, LightenStyle as LS
  4. # 执行API调用并存储响应
  5. URL = 'https://api.github.com/search/repositories?q=language:python&sort=star'
  6. r = requests.get(URL)
  7. print("Status code:", r.status_code)
  8. # 将API响应存储在一个变量中
  9. response_dict = r.json()
  10. print("Total repositories:", response_dict['total_count'])
  11. # 研究有关仓库的信息
  12. repo_dicts = response_dict['items']
  13. names, stars = [], []
  14. for repo_dict in repo_dicts:
  15. names.append(repo_dict['name'])
  16. stars.append(repo_dict['stargazers_count'])
  17. # 可视化
  18. my_style = LS('#333366', base_style=LCS)
  19. chart = pygal.Bar(style=my_style, x_label_rotation=45, show_legend=False)
  20. chart.title = 'Most-Starred Python Projects on GitHub'
  21. chart.x_labels = names
  22. chart.add('', stars)
  23. chart.render_to_file('python_repos.svg')

我们首先导入了pygal以及要应用于图表的Pygal样式。接下来,打印API调用响应的状态以及找到的仓库总数,以便获悉API调用是否出现了问题。我们不再打印返回的有关项目的信息,因为将通过可视化来呈现这些信息。

在❶处,我们创建了两个空列表,用于存储将包含在图表中的信息。我们需要每个项目的名称,用于给条形加上标签,我们还需要知道项目获得了多少个星,用于确定条形的高度。在循环中,我们将项目的名称和获得的星数附加到这些列表的末尾❷。

接下来,我们使用LightenStyle类(别名LS)定义了一种样式,并将其基色设置为深蓝色(见❸)。我们还传递了实参base_style,以使用LightColorizedStyle类(别名LCS)。然后,我们使用Bar()创建一个简单的条形图,并向它传递了my_style(见❹)。我们还传递了另外两个样式实参:让标签绕x轴旋转45度(x_label_rotation=45),并隐藏了图例(show_legend=False),因为我们只在图表中绘制一个数据系列。接下来,我们给图表指定了标题,并将属性x_labels设置为列表names

由于我们不需要给这个数据系列添加标签,因此在❺处添加数据时,将标签设置成了空字符串。生成的图表如图17-1所示。从中可知,前几个项目的受欢迎程度比其他项目高得多,但所有这些项目在Python生态系统中都很重要。

{%}

图17-1 GitHub上受欢迎程度最高的Python项目

17.2.1 改进Pygal图表

下面来改进这个图表的样式。我们将进行多个方面的定制,因此先来稍微调整代码的结构,创建一个配置对象,在其中包含要传递给Bar()的所有定制:

python_repos.py

  1. --snip--
  2. # 可视化
  3. my_style = LS('#333366', base_style=LCS)
  4. my_config = pygal.Config()
  5. my_config.x_label_rotation = 45
  6. my_config.show_legend = False
  7. my_config.title_font_size = 24
  8. my_config.label_font_size = 14
  9. my_config.major_label_font_size = 18
  10. my_config.truncate_label = 15
  11. my_config.show_y_guides = False
  12. my_config.width = 1000
  13. chart = pygal.Bar(my_config, style=my_style)
  14. chart.title = 'Most-Starred Python Projects on GitHub'
  15. chart.x_labels = names
  16. chart.add('', stars)
  17. chart.render_to_file('python_repos.svg')

在❶处,我们创建了一个Pygal类Config的实例,并将其命名为my_config。通过修改my_config的属性,可定制图表的外观。在❷处,我们设置了两个属性——x_label_rotationshow_legend,它们原来是在创建Bar实例时以关键字实参的方式传递的。在❸处,我们设置了图表标题、副标签和主标签的字体大小。在这个图表中,副标签是 x 轴上的项目名以及 y 轴上的大部分数字。主标签是 y 轴上为5000整数倍的刻度;这些标签应更大,以与副标签区分开来。在❹处,我们使用truncate_label将较长的项目名缩短为15个字符(如果你将鼠标指向屏幕上被截短的项目名,将显示完整的项目名)。接下来,我们将show_y_guides设置为False,以隐藏图表中的水平线(见❺)。最后,在❻处设置了自定义宽度,让图表更充分地利用浏览器中的可用空间。

在❼处创建Bar实例时,我们将my_config作为第一个实参,从而通过一个实参传递了所有的配置设置。我们可以通过my_config做任意数量的样式和配置修改,而❼处的代码行将保持不变。图17-2显示了重新设置样式后的图表。

{%}

图17-2 改进了图表的样式

17.2.2 添加自定义工具提示

在Pygal中,将鼠标指向条形将显示它表示的信息,这通常称为工具提示。在这个示例中,当前显示的是项目获得了多少个星。下面来创建一个自定义工具提示,以同时显示项目的描述。

来看一个简单的示例,它可视化前三个项目,并给每个项目对应的条形都指定自定义标签。为此,我们向add()传递一个字典列表,而不是值列表:

bar_descriptions.py

  1. import pygal
  2. from pygal.style import LightColorizedStyle as LCS, LightenStyle as LS
  3. my_style = LS('#333366', base_style=LCS)
  4. chart = pygal.Bar(style=my_style, x_label_rotation=45, show_legend=False)
  5. chart.title = 'Python Projects'
  6. chart.x_labels = ['httpie', 'django', 'flask']
  7. plot_dicts = [
  8. {'value': 16101, 'label': 'Description of httpie.'},
  9. {'value': 15028, 'label': 'Description of django.'},
  10. {'value': 14798, 'label': 'Description of flask.'},
  11. ]
  12. chart.add('', plot_dicts)
  13. chart.render_to_file('bar_descriptions.svg')

在❶处,我们定义了一个名为plot_dicts的列表,其中包含三个字典,分别针对项目HTTPie、Django和Flask。每个字典都包含两个键:'value''label'。Pygal根据与键'value'相关联的数字来确定条形的高度,并使用与'label'相关联的字符串给条形创建工具提示。例如,❷处的第一个字典将创建一个条形,用于表示一个获得了16 101颗星、工具提示为Description of httpie的项目。

方法add()接受一个字符串和一个列表。这里调用add()时,我们传入了一个由表示条形的字典组成的列表(plot_dicts)(见❸)。图17-3显示了一个工具提示:除默认工具提示(获得的星数)外,Pygal还显示了我们传入的自定义提示。

{%}

图17-3 每个条形都有自定义的工具提示标签

17.2.3 根据数据绘图

为根据数据绘图,我们将自动生成plot_dicts,其中包含API调用返回的30个项目的信息。

完成这种工作的代码如下:

python_repos.py

  1. --snip--
  2. # 研究有关仓库的信息
  3. repo_dicts = response_dict['items']
  4. print("Number of items:", len(repo_dicts))
  5. names, plot_dicts = [], []
  6. for repo_dict in repo_dicts:
  7. names.append(repo_dict['name'])
  8. plot_dict = {
  9. 'value': repo_dict['stargazers_count'],
  10. 'label': repo_dict['description'],
  11. }
  12. plot_dicts.append(plot_dict)
  13. # 可视化
  14. my_style = LS('#333366', base_style=LCS)
  15. --snip--
  16. chart.add('', plot_dicts)
  17. chart.render_to_file('python_repos.svg')

在❶处,我们创建了两个空列表namesplot_dicts。为生成x轴上的标签,我们依然需要列表names

在循环内部,对于每个项目,我们都创建了字典plot_dict(见❷)。在这个字典中,我们使用键'value'存储了星数,并使用键'label'存储了项目描述。接下来,我们将字典plot_dict附加到plot_dicts末尾(见❸)。在❹处,我们将列表plot_dicts传递给了add()。图17-4显示了生成的图表。

{%}

图17-4 将鼠标指向条形将显示项目的描述

17.2.4 在图表中添加可单击的链接

Pygal还允许你将图表中的每个条形用作网站的链接。为此,只需添加一行代码,在为每个项目创建的字典中,添加一个键为'xlink'的键—值对:

python_repos.py

  1. --snip--
  2. names, plot_dicts = [], []
  3. for repo_dict in repo_dicts:
  4. names.append(repo_dict['name'])
  5. plot_dict = {
  6. 'value': repo_dict['stargazers_count'],
  7. 'label': repo_dict['description'],
  8. 'xlink': repo_dict['html_url'],
  9. }
  10. plot_dicts.append(plot_dict)
  11. --snip--

Pygal根据与键'xlink'相关联的URL将每个条形都转换为活跃的链接。单击图表中的任何条形时,都将在浏览器中打开一个新的标签页,并在其中显示相应项目的GitHub页面。至此,你对API获取的数据进行了可视化,它是交互性的,包含丰富的信息!

17.3 Hacker News API

为探索如何使用其他网站的API调用,我们来看看Hacker News(http://news.ycombinator.com/)。在Hacker News网站,用户分享编程和技术方面的文章,并就这些文章展开积极的讨论。Hacker News的API让你能够访问有关该网站所有文章和评论的信息,且不要求你通过注册获得密钥。

下面的调用返回本书编写时最热门的文章的信息:

  1. https://hacker-news.firebaseio.com/v0/item/9884165.json

响应是一个字典,包含ID为9884165的文章的信息:

  1. {
  2. 'url': 'http://www.bbc.co.uk/news/science-environment-33524589',
  3. 'type': 'story',
  4. 'title': 'New Horizons: Nasa spacecraft speeds past Pluto',
  5. 'descendants': 141,
  6. 'score': 230,
  7. 'time': 1436875181,
  8. 'text': '',
  9. 'by': 'nns',
  10. 'id': 9884165,
  11. 'kids': [9884723, 9885099, 9884789, 9885604, 9885844]
  12. }

这个字典包含很多键,如'url'(见❶)和'title'(见❷)。与键'descendants'相关联的值是文章被评论的次数(见❸)。与键'kids'相关联的值包含对文章所做的所有评论的ID(见❹)。每个评论自己也可能有kid,因此文章的后代(descendant)数量可能比其kid数量多。

下面来执行一个API调用,返回Hacker News上当前热门文章的ID,再查看每篇排名靠前的文章:

hn_submissions.py

  1. import requests
  2. from operator import itemgetter
  3. # 执行API调用并存储响应
  4. url = 'https://hacker-news.firebaseio.com/v0/topstories.json'
  5. r = requests.get(url)
  6. print("Status code:", r.status_code)
  7. # 处理有关每篇文章的信息
  8. submission_ids = r.json()
  9. submission_dicts = []
  10. for submission_id in submission_ids[:30]:
  11. # 对于每篇文章,都执行一个API调用
  12. url = ('https://hacker-news.firebaseio.com/v0/item/' +
  13. str(submission_id) + '.json')
  14. submission_r = requests.get(url)
  15. print(submission_r.status_code)
  16. response_dict = submission_r.json()
  17. submission_dict = {
  18. 'title': response_dict['title'],
  19. 'link': 'http://news.ycombinator.com/item?id=' + str(submission_id),
  20. 'comments': response_dict.get('descendants', 0)
  21. }
  22. submission_dicts.append(submission_dict)
  23. submission_dicts = sorted(submission_dicts, key=itemgetter('comments'),
  24. reverse=True)
  25. for submission_dict in submission_dicts:
  26. print("\nTitle:", submission_dict['title'])
  27. print("Discussion link:", submission_dict['link'])
  28. print("Comments:", submission_dict['comments'])

首先,我们执行了一个API调用,并打印了响应的状态(见❶)。这个API调用返回一个列表,其中包含Hacker News上当前最热门的500篇文章的ID。接下来,我们将响应文本转换为一个Python列表(见❷),并将其存储在submission_ids中。我们将使用这些ID来创建一系列字典,其中每个字典都存储了一篇文章的信息。

在❸处,我们创建了一个名为submission_dicts的空列表,用于存储前面所说的字典。接下来,我们遍历前30篇文章的ID。对于每篇文章,我们都执行一个API调用,其中的URL包含submission_id的当前值(见❹)。我们打印每次请求的状态,以便知道请求是否成功了。

在❺处,我们为当前处理的文章创建一个字典,并在其中存储文章的标题以及到其讨论页面的链接。在❻处,我们在这个字典中存储了评论数。如果文章还没有评论,响应字典中将没有键'descendants'。不确定某个键是否包含在字典中时,可使用方法dict.get(),它在指定的键存在时返回与之相关联的值,并在指定的键不存在时返回你指定的值(这里是0)。最后,我们将submission_dict附加到submission_dicts末尾。

Hacker News上的文章是根据总体得分排名的,而总体得分取决于很多因素,其中包含被推荐的次数、评论数以及发表的时间。我们要根据评论数对字典列表submission_dicts进行排序,为此,使用了模块operator中的函数itemgetter()(见❼)。我们向这个函数传递了键'comments',因此它将从这个列表的每个字典中提取与键'comments'相关联的值。这样,函数sorted()将根据这种值对列表进行排序。我们将列表按降序排列,即评论最多的文章位于最前面。

对列表排序后,我们遍历这个列表(见❽),对于每篇热门文章,都打印其三项信息:标题、到讨论页面的链接以及文章现有的评论数:

  1. Status code: 200
  2. 200
  3. 200
  4. 200
  5. --snip--
  6. Title: Firefox deactivates Flash by default
  7. Discussion link: http://news.ycombinator.com/item?id=9883246
  8. Comments: 231
  9. Title: New Horizons: Nasa spacecraft speeds past Pluto
  10. Discussion link: http://news.ycombinator.com/item?id=9884165
  11. Comments: 142
  12. Title: Iran Nuclear Deal Is Reached With World Powers
  13. Discussion link: http://news.ycombinator.com/item?id=9884005
  14. Comments: 141
  15. Title: Match Group Buys PlentyOfFish for $575M
  16. Discussion link: http://news.ycombinator.com/item?id=9884417
  17. Comments: 75
  18. Title: Our Nexus 4 devices are about to explode
  19. Discussion link: http://news.ycombinator.com/item?id=9885625
  20. Comments: 14
  21. --snip--

使用任何API来访问和分析信息时,流程都与此类似。有了这些数据后,你就可以进行可视化,指出最近哪些文章引发了最激烈的讨论。

动手试一试

17-1 其他语言:修改python_repos.py中的API调用,使其在生成的图表中显示使用其他语言编写的最受欢迎的项目。请尝试语言JavaScript、Ruby、C、Java、Perl、Haskell和Go等。

17-2 最活跃的讨论:使用hn_submissions.py中的数据,创建一个条形图,显示Hacker News上当前最活跃的讨论。条形的高度应对应于文章得到的评论数量,条形的标签应包含文章的标题,而每个条形应是到该文章讨论页面的链接。

17-3 测试python_repos.py:在python_repos.py中,打印status_code的值,以核实API调用是否成功了。请编写一个名为test_python_repos.py的程序,它使用unittest来断言status_code的值为200。想想你还可做出哪些断言,如返回的条目数符合预期,仓库总数超过特定的值等。

17.4 小结

在本章中,你学习了:如何使用API来编写独立的程序,它们自动采集所需的数据并对其进行可视化;使用GitHub API来探索GitHub上星级最高的Python项目,还大致地了解了Hacker News API;如何使用requests包来自动执行GitHub API调用,以及如何处理调用的结果。我们还简要地介绍了一些Pygal设置,使用它们可进一步定制生成的图表的外观。

在本书的最后一个项目中,我们将使用Django来创建一个Web应用程序。