17.2.2 添加自定义工具提示
在Pygal中,将鼠标指向条形将显示它表示的信息,这通常称为工具提示。在这个示例中, 当前显示的是项目获得了多少个星。下面来创建一个自定义工具提示,以同时显示项目的描述。 来看一个简单的示例,它可视化前三个项目,并给每个项目对应的条形都指定自定义标签。 为此,我们向add()传递一个字典列表,而不是值列表:
bar_descriptions.py
import pygal from pygal.style import LightColorizedStyle as LCS, LightenStyle as LS my_style = LS('#333366', base_style=LCS) chart = pygal.Bar(style=my_style, x_label_rotation=45, show_legend=False) chart.title = 'Python Projects' chart.x_labels = ['httpie', 'django', 'flask'] 1 plot_dicts = [ 2 {'value': 16101, 'label': 'Description of httpie.'}, {'value': 15028, 'label': 'Description of django.'}, {'value': 14798, 'label': 'Description of flask.'}, ] 3 chart.add('', plot_dicts) chart.render_to_file('bar_descriptions.svg')
在1处,我们定义了一个名为plot_dicts的列表,其中包含三个字典,分别针对项目HTTPie、 Django和Flask。每个字典都包含两个键:'value'和'label'。Pygal根据与键'value'相关联的数 字来确定条形的高度,并使用与'label'相关联的字符串给条形创建工具提示。例如,处的第 一个字典将创建一个条形,用于表示一个获得了16 101颗星、工具提示为Description of httpie的 项目。 方法add()接受一个字符串和一个列表。这里调用add()时,我们传入了一个由表示条形的字 典组成的列表(plot_dicts)(见3)。图17-3显示了一个工具提示:除默认工具提示(获得的星 数)外,Pygal还显示了我们传入的自定义提示。
17.2.3 根据数据绘图
为根据数据绘图,我们将自动生成plot_dicts,其中包含API调用返回的30个项目的信息。 完成这种工作的代码如下:
python_repos.py
--snip-- # 研究有关仓库的信息 repo_dicts = response_dict['items'] print("Number of items:", len(repo_dicts)) 1 names, plot_dicts = [], [] for repo_dict in repo_dicts: names.append(repo_dict['name']) 2 plot_dict = { 'value': repo_dict['stargazers_count'], 'label': repo_dict['description'], } 3 plot_dicts.append(plot_dict) # 可视化 my_style = LS('#333366', base_style=LCS) --snip-- 4 chart.add('', plot_dicts) chart.render_to_file('python_repos.svg')
在1处,我们创建了两个空列表names和plot_dicts。为生成x轴上的标签,我们依然需要列 表names。
在循环内部,对于每个项目,我们都创建了字典plot_dict(见2)。在这个字典中,我们使 用键'value'存储了星数,并使用键'label'存储了项目描述。接下来,我们将字典plot_dict附加 到plot_dicts末尾(见3)。在4处,我们将列表plot_dicts传递给了add()。图17-4显示了生成的 图表。
17.2.4 在图表中添加可单击的链接
Pygal还允许你将图表中的每个条形用作网站的链接。为此,只需添加一行代码,在为每个 项目创建的字典中,添加一个键为'xlink'的键—值对:
python_repos.py
--snip-- names, plot_dicts = [], [] for repo_dict in repo_dicts: names.append(repo_dict['name']) plot_dict = { 'value': repo_dict['stargazers_count'], 'label': repo_dict['description'], 'xlink': repo_dict['html_url'], } plot_dicts.append(plot_dict) --snip--
Pygal根据与键'xlink'相关联的URL将每个条形都转换为活跃的链接。单击图表中的任何条 形时,都将在浏览器中打开一个新的标签页,并在其中显示相应项目的GitHub页面。至此,你对 API获取的数据进行了可视化,它是交互性的,包含丰富的信息!
17.3 Hacker News API
为探索如何使用其他网站的API调用,我们来看看Hacker News(http://news.ycombinator. com/)。在Hacker News网站,用户分享编程和技术方面的文章,并就这些文章展开积极的讨论。Hacker News的API让你能够访问有关该网站所有文章和评论的信息,且不要求你通过注册获得密钥。 下面的调用返回本书编写时最热门的文章的信息:
https://hacker-news.firebaseio.com/v0/item/9884165.json
响应是一个字典,包含ID为9884165的文章的信息:
{ 1 'url': 'http://www.bbc.co.uk/news/science-environment-33524589', 'type': 'story', 2 'title': 'New Horizons: Nasa spacecraft speeds past Pluto', 3 'descendants': 141, 'score': 230, 'time': 1436875181, 'text': '', 'by': 'nns', 'id': 9884165, 4 'kids': [9884723, 9885099, 9884789, 9885604, 9885844] }
这个字典包含很多键,如'url'(见1)和'title'(见2)。与键'descendants'相关联的值是 文章被评论的次数(见3)。与键'kids'相关联的值包含对文章所做的所有评论的ID(见4)。每 个评论自己也可能有kid,因此文章的后代(descendant)数量可能比其kid数量多。 下面来执行一个API调用,返回Hacker News上当前热门文章的ID,再查看每篇排名靠前的文章:
hn_submissions.py
import requests from operator import itemgetter # 执行API调用并存储响应 1 url = 'https://hacker-news.firebaseio.com/v0/topstories.json' r = requests.get(url) print("Status code:", r.status_code) # 处理有关每篇文章的信息 2 submission_ids = r.json() 3 submission_dicts = [] for submission_id in submission_ids[:30]: # 对于每篇文章,都执行一个API调用 4 url = ('https://hacker-news.firebaseio.com/v0/item/' + str(submission_id) + '.json') submission_r = requests.get(url) print(submission_r.status_code) response_dict = submission_r.json() 5 submission_dict = { 'title': response_dict['title'], 'link': 'http://news.ycombinator.com/item?id=' + str(submission_id), 'comments': response_dict.get('descendants', 0) } submission_dicts.append(submission_dict) 7 submission_dicts = sorted(submission_dicts, key=itemgetter('comments'), reverse=True) 8 for submission_dict in submission_dicts: print("\nTitle:", submission_dict['title']) print("Discussion link:", submission_dict['link']) print("Comments:", submission_dict['comments'])
首先,我们执行了一个API调用,并打印了响应的状态(见1)。这个API调用返回一个列表, 其中包含Hacker News上当前最热门的500篇文章的ID。接下来,我们将响应文本转换为一个 Python列表(见2),并将其存储在submission_ids中。我们将使用这些ID来创建一系列字典,其 中每个字典都存储了一篇文章的信息。
在3处,我们创建了一个名为submission_dicts的空列表,用于存储前面所说的字典。接下 来,我们遍历前30篇文章的ID。对于每篇文章,我们都执行一个API调用,其中的URL包含 submission_id的当前值(见4)。我们打印每次请求的状态,以便知道请求是否成功了。
在3处,我们为当前处理的文章创建一个字典,并在其中存储文章的标题以及到其讨论页面 的链接。在4处,我们在这个字典中存储了评论数。如果文章还没有评论,响应字典中将没有键 'descendants'。不确定某个键是否包含在字典中时,可使用方法dict.get(),它在指定的键存在 时返回与之相关联的值,并在指定的键不存在时返回你指定的值(这里是0)。最后,我们将 submission_dict附加到submission_dicts末尾。
Hacker News上的文章是根据总体得分排名的,而总体得分取决于很多因素,其中包含被推 荐的次数、评论数以及发表的时间。我们要根据评论数对字典列表submission_dicts进行排序, 为此,使用了模块operator中的函数itemgetter()(见7)。我们向这个函数传递了键'comments', 因此它将从这个列表的每个字典中提取与键'comments'相关联的值。这样,函数sorted()将根据 这种值对列表进行排序。我们将列表按降序排列,即评论最多的文章位于最前面。
对列表排序后,我们遍历这个列表(见8),对于每篇热门文章,都打印其三项信息:标题、 到讨论页面的链接以及文章现有的评论数:
Status code: 200 200 200 200 --snip-- Title: Firefox deactivates Flash by default Discussion link: http://news.ycombinator.com/item?id=9883246 Comments: 231 Title: New Horizons: Nasa spacecraft speeds past Pluto Discussion link: http://news.ycombinator.com/item?id=9884165 Comments: 142 Title: Iran Nuclear Deal Is Reached With World Powers Discussion link: http://news.ycombinator.com/item?id=9884005 Comments: 141 Title: Match Group Buys PlentyOfFish for $575M Discussion link: http://news.ycombinator.com/item?id=9884417 Comments: 75 Title: Our Nexus 4 devices are about to explode Discussion link: http://news.ycombinator.com/item?id=9885625 Comments: 14 --snip--
使用任何API来访问和分析信息时,流程都与此类似。有了这些数据后,你就可以进行可视 化,指出最近哪些文章引发了最激烈的讨论。
17.4 小结
在本章中,你学习了:如何使用API来编写独立的程序,它们自动采集所需的数据并对其进 行可视化;使用GitHub API来探索GitHub上星级最高的Python项目,还大致地了解了Hacker News API;如何使用requests包来自动执行GitHub API调用,以及如何处理调用的结果。我们还简要地 介绍了一些Pygal设置,使用它们可进一步定制生成的图表的外观。 在本书的最后一个项目中,我们将使用Django来创建一个Web应用程序。