练习17-1:其他语言
见17-3的被测试程序java_repos.py
练习17-2:最活跃的讨论
本题需要注意ID:26735905没有评论数,需要跳过
from operator import itemgetter
# 函数itemgetter用于根据‘conmments’对字典列表submission_dicts排序
from plotly.graph_objs import Bar
from plotly import offline
import requests
import json
# 执行API调用并存储响应
url_s = 'https://hacker-news.firebaseio.com/v0/topstories.json'
r_s = requests.get(url_s) # r_s是一个ID按阅读量排位的列表,后文r是具体某个ID文章的信息
print(f"Status code: {r_s.status_code}")
# 处理每篇文章的信息
submission_ids = r_s.json()
submission_dicts = []
for submission_id in submission_ids[:30]:
# 对于每篇文章,都执行一个API调用
url = f"https://hacker-news.firebaseio.com/v0/item/{submission_id}.json"
r = requests.get(url)
print(f"id: {submission_id}\tstatus: {r.status_code}")
response_dict = r.json()
# 对于每篇文章,都创建一个字典
try: # 在try代码块中尝试执行可能出错的代码
subsmission_dict = {
'title': response_dict['title'],
'hn_link': f'https://news.ycombinator.com/item?id={submission_id}',
'comments': response_dict['descendants'],
'owner': response_dict['by'],
'type': response_dict['type']
}
except KeyError: # 26735905没有评论数,需要这样跳过
print(f"{submission_id} cannot find request word!")
continue
submission_dicts.append(subsmission_dict)
submission_dicts = sorted(submission_dicts, key=itemgetter('comments'), reverse=True)
"""
函数itemgetter()用于根据‘conmments’对字典列表submission_dicts排序
我们向itemgetter()函数传递了键‘comments’,因此它从每个字典中提取与键‘comments’关联的值。
而后函数sorted()将根据这个值对列表进行排序,reverse=True为降序排列,评论数多的在前面
"""
# 为画图进行信息的预处理
repo_links, stars, labels = [], [], []
for subsmission_dict in submission_dicts:
repo_name = subsmission_dict['title']
repo_url = subsmission_dict['hn_link']
repo_link = f"<a href='{repo_url}'>{repo_name}</a>"
# 创建一个指向项目的链接,为此使用了HTML标记<a>,其格式为<a href='URL'>link_text</a>>
print(repo_url)
repo_links.append(repo_link)
stars.append(subsmission_dict['comments'])
owner = subsmission_dict['owner']
description = subsmission_dict['type']
label = f"By: {owner}<br />Type: {description}" # <br />为换行符
labels.append(label)
# 可视化
data = [{
'type': 'bar',
'x': repo_links,
'y': stars,
'hovertext': labels, # 工具提示
'marker': { # 条形设计
'color': 'rgb(60,100,150)', # 条形指定为蓝色
'line': {'width': 1.5, 'color': 'rgb(25,25,25)'} # 1.5像素宽的深灰色轮廓
},
'opacity': 0.6 # 不透明度
}]
my_layout = {
'title': 'GitHub最受欢迎的Python项目',
'xaxis': {
'title': 'Repository',
'titlefont': {'size': 20}, # 轴标题字号
'tickfont': {'size': 10} # 刻度标签字号
},
'yaxis': {
'title': 'Stars',
'titlefont': {'size': 24}, # 标签字号
'tickfont': {'size': 14} # 刻度标签字号
}
}
fig = {'data': data, 'layout': my_layout}
offline.plot(fig, filename='17-2.html')
练习17-3 测试 python_repos.py
这里不小心用了17-1的程序,把这题从python做成了java,不过不打紧,基本就这个词不同
注意事项:
- 必须将程序进行重构才能进行测试
- 主函数放在if中,这样直接运行时一切正常,而在测试程序中导入时不会直接运行
修改后的被测试程序java_repos.py
如下:
import requests
from plotly.graph_objs import Bar
from plotly import offline
def get_response(web):
"""存储一个问题, 并为存储答案建立列表"""
url = web # 这个有点多余,改def时懒得调整
headers = {'Accept': 'application/vnd.github.v4+json'}
# 通过指定headers显式地要求使用第4版本的GitHub API, 返回json格式信息
r = requests.get(url, headers=headers)
return r
def get_repo_dicts(r):
# 使用requests调用API;调用get()并将URL传递给它,再将响应对象赋给变量r
print(f"Status code: {r.status_code}")
# 响应对象包含一个名为status_code的属性, 指出请求是否成功(状态码200表示请求成功)
# 将api响应赋给一个变量
response_dict = r.json()
# 使用json()方法将这些信息转换为Python字典,并将结果存储在response_dict中
print(f"Total repositories: {response_dict['total_count']}")
# 探索有关仓库的信息
repo_dicts = response_dict['items']
return repo_dicts
def get_names_plot_dicts(repo_dicts):
repo_links, stars, labels = [], [], []
for repo_dict in repo_dicts:
repo_name = repo_dict['name']
repo_url = repo_dict['html_url']
repo_link = f"<a href='{repo_url}'>{repo_name}</a>"
# 创建一个指向项目的链接,为此使用了HTML标记<a>,其格式为<a href='URL'>link_text</a>>
print(repo_url)
repo_links.append(repo_link)
stars.append(repo_dict['stargazers_count'])
owner = repo_dict['owner']['login']
description = repo_dict['description']
label = f"By: {owner}<br />Type: {description}" # <br />为换行符
labels.append(label)
return repo_links, stars, labels
def make_visualization(repo_links, stars, labels):
# 可视化
data = [{
'type': 'bar',
'x': repo_links,
'y': stars,
'hovertext': labels, # 工具提示
'marker': { # 条形设计
'color': 'rgb(60,100,150)', # 条形指定为蓝色
'line': {'width': 1.5, 'color': 'rgb(25,25,25)'} # 1.5像素宽的深灰色轮廓
},
'opacity': 0.6 # 不透明度
}]
my_layout = {
'title': 'GitHub最受欢迎的Python项目',
'xaxis': {
'title': 'Repository',
'titlefont': {'size': 24}, # 轴标题字号
'tickfont': {'size': 14} # 刻度标签字号
},
'yaxis': {
'title': 'Stars',
'titlefont': {'size': 24}, # 标签字号
'tickfont': {'size': 14} # 刻度标签字号
}
}
fig = {'data': data, 'layout': my_layout}
offline.plot(fig, filename='Java_repos.html')
if __name__ == '__main__': # 主函数放在if中,这样导入时不会直接运行
web = 'https://api.github.com/search/repositories?q=language:java&sort=stars'
r = get_response(web)
repo_dicts = get_repo_dicts(r)
repo_links, stars, labels = get_names_plot_dicts(repo_dicts)
make_visualization(repo_links, stars, labels)
测试程序text_java_repos.py
如下:
import unittest
import java_repos as jr # 不小心把这题从python做成了java,不打紧,基本就这个词不同
class TestJavaRepos(unittest.TestCase):
"""测试文件java_repos.py"""
def setUp(self):
"""
创建一个调查对象和一组答案, 供使用的测试方法使用
"""
web = 'https://api.github.com/search/repositories?q=language:java&sort=stars'
self.r = jr.get_response(web)
self.response_dict = self.r.json()
self.repo_dicts = jr.get_repo_dicts(self.r)
self.repo_dict = self.repo_dicts[0]
self.repo_links, self.stars, self.labels = \
jr.get_names_plot_dicts(self.repo_dicts)
def test_get_status_code(self):
"""测试API响应是否成功"""
my_status_code = self.r.status_code
self.assertEqual(str(my_status_code), "200")
def test_get_repos_number(self):
"""测试仓库总数是否超过500000"""
my_repos_number = self.response_dict['total_count']
self.assertLess(500000, my_repos_number)
def test_get_repos_returned_number(self):
"""测试返回条目数是否为30"""
self.assertEqual(len(self.repo_dicts), 30)
def test_repositories_keys(self):
"""测试一个仓库是否包含所需的键"""
required_keys = ['name', 'owner', 'description',
'description', 'stargazers_count', 'html_url']
for key in required_keys:
self.assertTrue(key in self.repo_dict.keys())
if __name__ == '__main__':
unittest.main()