二刷《Python编程 从入门到实践》(13)
第十七章 使用API
本章程序将使用Web应用编程接口 (API) 自动请求网站的特定信息而不是整个网页,再对这些信息进行可视化。由于这样编写的程序始终使用最新的数据来生成可视化,因此即便数据瞬息万变,它呈现的信息也都是最新的。
17.1 使用 Web API
Web API 是网站的一部分,用于与使用非常具体的URL请求特定信息的程序交互。这种请求称为 API 调用。请求的数据将以易于处理的格式(如JSON或CSV)返回。
17.1.2 使用 API 调用请求数据
GitHub 的API让你能够通过API 调用来请求各种信息。要知道API 调用是什么样的,请在浏览器的地址栏中输入如下地址并按回车键:
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
指定将项目按其获得的星级进行排序。
调用结果:
{
"total_count": 5182955,
"incomplete_results": true,
"items": [
{
"id": 54346799,
"node_id": "MDEwOlJlcG9zaXRvcnk1NDM0Njc5OQ==",
"name": "public-apis",
"full_name": "public-apis/public-apis",
--snip--
17.1.3 安装 requests
书上没有细说。我的安装步骤是:
1、激活已经建立的 anaconda 环境。
>>> activate tensorflow
2、直接使用pip
安装。
>>> pip install requests
17.1.4 处理 API 响应
找出GitHub上星级最高的Python项目:
import requests
# 执行API调用并存储响应
url = 'https://api.github.com/search/repositories?q=language:python&sort=stars'
r = requests.get(url)
print("Status code:", r.status_code)
# 将API响应存储在一个变量中
response_dict = r.json()
# 处理结果
print(response_dict.keys())
调用get()
并将URL
传递给它,再将响应对象储存在变量r
中。响应对象包含一个名为status_code
的属性,它让我们知道请求是否成功了(状态码200表示请求成功)。这个API返回JSON格式的信息,因此使用方法json()
将这些信息转换为一个Python字典。
书上的结果:
Status code: 200
dict_keys(['items', 'total_count', 'incomplete_results'])
我调用的结果:
Traceback (most recent call last):
--snip--
requests.exceptions.ConnectionError: ('Connection aborted.', ConnectionResetError(10054, '远程主机强迫关闭了一个现有的连接。', None, 10054, None))
被服务器拒绝了。
17.1.5 处理响应字典
import requests
# 执行API调用并存储响应
url = 'https://api.github.com/search/repositories?q=language:python&sort=stars'
r = requests.get(url)
print("Status code:", r.status_code)
# 将API响应存储在一个变量中
response_dict = r.json()
print("Total repositories:", response_dict['total_count'])
# 探索有关仓库的信息
repo_dicts = response_dict['items']
print("Repositories returned:", len(repo_dicts))
# 研究第一个仓库
repo_dict = repo_dicts[0]
print("\nSelected information about first repository:")
print('Name:', repo_dict['name'])
print('Owner:', repo_dict['owner']['login'])
print('Stars:', repo_dict['stargazers_count'])
print('Repository:', repo_dict['html_url'])
print('Created:', repo_dict['created_at'])
print('Updated:', repo_dict['updated_at'])
print('Description:', repo_dict['description'])
上述代码中,使用键owner
来访问表示所有者的字典,再使用键key
来获取所有者的登录名。
17.1.6 概述最受欢迎的仓库
17.1.7 监视 API 的速率限制
大多数API都存在速率限制,即在特定时间内可执行的请求数存在限制。要获悉GitHub的限制,可以在浏览器中输入https://api.github.com/rate_limit
。
{
"resources":{
"core":{
"limit":60,
"remaining":60,
"reset":1591064977
},
"graphql":{
"limit":0,
"remaining":0,
"reset":1591064977
},
"integration_manifest":{
"limit":5000,
"remaining":5000,
"reset":1591064977
},
"search":{
"limit":10,
"remaining":10
"reset":1591061437
}
},
"rate":{
"limit":60,
"remaining":60,
"reset":1591064977
}
}
17.2 使用 Pygal 可视化仓库
import requests
import pygal
from pygal.style import LightColorizedStyle as LCS, LightenStyle as LS
# 执行API调用并存储响应
URL = 'https://api.github.com/search/repositories?q=language:python&sort=star'
r = requests.get(URL)
print("Status code:", r.status_code)
# 将API响应存储在一个变量中
response_dict = r.json()
print("Total repositories:", response_dict['total_count'])
# 研究有关仓库的信息
repo_dicts = response_dict['items']
names, stars = [], []
for repo_dict in repo_dicts:
names.append(repo_dict['name'])
stars.append(repo_dict['stargazers_count'])
# 可视化
my_style = LS('#333366', base_style=LCS)
chart = pygal.Bar(style=my_style, x_label_rotation=45, show_legend=False)
chart.title = 'Most-Starred Python Projects on GitHub'
chart.x_labels = names
chart.add('', stars)
chart.render_to_file('python_repos.svg')
调用pygal.Bar()
创建条形图时,将样式my_style
传递给style
,将标签绕 x 轴旋转45°(x_label_rotation=45
),并隐藏图例(show_legend=False
)。由于不需要给这个数据系列添加标签,所以调用add()
时,传递的第一个实参为空字符串。
17.2.1 改进 Pygal 图表
可以先创建一个配置对象my_config
,再将要传递给Bar()
的所有定制都放在里面。
--snip--
# 可视化
my_style = LS('#333366', base_style=LCS)
my_config = pygal.Config() # 创建 Config 实例
my_config.x_label_rotation = 45
my_config.show_legend = False
my_config.title_font_size = 24
my_config.label_font_size = 14 # 设置标签
my_config.major_label_font_size = 18 # 设置主标签
my_config.truncate_label = 15 # x 轴上项目名只显示前15个字
my_config.show_y_guides = False # 隐藏图表中的 y 轴水平线
my_config.width = 1000 # 自定义图表宽度
chart = pygal.Bar(my_config, style=my_style)
chart.title = 'Most-Starred Python Projects on GitHub'
chart.x_labels = names
chart.add('', stars)
chart.render_to_file('python_repos.svg')
17.2.2 添加自定义工具提示
在 Pygal 中,将鼠标指向条形将显示它表示的信息,这通常称为 工具提示。
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']
plot_dicts = [
{'value': 16101, 'label': 'Description of httpie.'},
{'value': 15028, 'label': 'Description of django.'},
{'value': 14798, 'label': 'Description of flask.'},
]
chart.add('', plot_dicts)
chart.render_to_file('bar_descriptions.svg')
Pygal 根据与键value
相关联的数字来确定条形的高度,并使用与label
相关联的字符串给条形创建工具提示。
17.2.3 根据数据绘图
--snip--
# 研究有关仓库的信息
repo_dicts = response_dict['items']
print("Number of items:", len(repo_dicts))
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'],
}
plot_dicts.append(plot_dict)
# 可视化
my_style = LS('#333366', base_style=LCS)
--snip--
chart.add('', plot_dicts)
chart.render_to_file('python_repos.svg')
17.2.4 在图表中添加可单击的链接
Pygal 允许将图表中的每个条形用作网站的链接,只需在字典中添加一个键为'xlink'
的键—值对:
--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--
17.3 Hacker News API
import requests
from operator import itemgetter
# 执行API调用并存储响应
url = 'https://hacker-news.firebaseio.com/v0/topstories.json'
r = requests.get(url)
print("Status code:", r.status_code)
# 处理有关每篇文章的信息
submission_ids = r.json()
submission_dicts = [] # 包含所以文章信息的字典列表
for submission_id in submission_ids[:30]: # 遍历前30篇文章信息
# 对于每篇文章,都执行一个API调用
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()
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)
submission_dicts = sorted(submission_dicts, key=itemgetter('comments'),
reverse=True) # 按评论数倒序
for submission_dict in submission_dicts:
print("\nTitle:", submission_dict['title'])
print("Discussion link:", submission_dict['link'])
print("Comments:", submission_dict['comments'])
在字典中response_dict
存储了评论数。如果文章还没有评论,响应字典中将没有键descendants
。不确定某个键是否包含在字典中时,可使用方法dict.get()
,它在指定的键存在时返回与之相关联的值,并在指定的键不存在时返回你指定的值(这里是0)。
根据评论数对字典列表submission_dicts
进行排序,为此,使用了模块operator
中的函数itemgetter()
,向其传递键comments
,这样它将从这个列表的每个字典中提取与键comments
相关联的值。reverse=True
表示倒叙。
小结
1、利用模块requests
中的get()
函数调用URL,例如 r = requests.get(url)
。
2、使用方法json()
将API 调用获得的信息转换为一个Python字典,例如 response_dict = r.json()
。
3、使用键owner
来访问表示所有者的字典,再使用键key
来获取所有者的登录名,例如 print('Owner:', repo_dict['owner']['login'])
。
4、调用pygal.Bar()
创建条形图时,将样式my_style
传递给style
,将标签绕 x 轴旋转45°(x_label_rotation=45
),并隐藏图例(show_legend=False
),例如 chart = pygal.Bar(style=my_style, x_label_rotation=45, show_legend=False)
。
5、不需要给这个数据系列添加标签时,调用函数add()
传递的第一个实参为空字符串,例如 chart.add('', stars)
。
6、可以先创建一个配置对象my_config
,再将要传递给Bar()
的所有定制都放在里面,例如 chart = pygal.Bar(my_config, style=my_style)
。
7、Pygal 根据与键value
相关联的数字来确定条形的高度,并使用与label
相关联的字符串给条形创建工具提示,例如 plot_dicts = [{'value': 16101, 'label': 'Description of httpie.'}, ...]
。
8、Pygal 允许将图表中的每个条形用作网站的链接,只需在字典中添加一个键为'xlink'
的键—值对,例如 plot_dict = { 'value': repo_dict['stargazers_count'], 'label': repo_dict['description'], 'xlink': repo_dict['html_url'], }
。
9、不确定某个键是否包含在字典中时,可使用方法dict.get()
,它在指定的键存在时返回与之相关联的值,并在指定的键不存在时返回你指定的值, 例如 submission_dict = { ..., 'comments': response_dict.get('descendants', 0)}
。
10、使用了模块operator
中的函数itemgetter()
,向其传递键comments
,可以根据评论数对字典列表submission_dicts
进行排序,例如 submission_dicts = sorted(submission_dicts, key=itemgetter('comments'), reverse=True)
。