黑客新闻 API
为了探索如何在其他网站上使用 API 调用,让我们快速浏览一下 Hacker News ( https://news.ycombinator.com )。在 Hacker News 上,人们分享有关编程和技术的文章,并就这些文章展开热烈的讨论。通过 Hacker News API,可以访问网站上所有提交和评论的数据,而且无需注册密钥即可使用 API。
以下调用将返回截至本文撰写时的热门文章信息:
https://hacker-news.firebaseio.com/v0/item/31353677.json
当你在浏览器中输入这个 URL 时,你会发现页面上的文字都用大括号括起来了,这意味着这是一本字典。但是,如果没有更好的格式化,就很难检查响应。让我们通过 json.dumps() 方法来运行这个 URL,就像在第 16 章的地震项目中所做的那样,这样我们就可以探索返回的文章信息类型:
import requests
import json
# Make an API call, and store the response.
url = "https://hacker-news.firebaseio.com/v0/item/31353677.json"
r = requests.get(url)
print(f"Status code: {r.status_code}")
# Explore the structure of the data.
response_dict = r.json()
response_string = json.dumps(response_dict, indent=4)
print(response_string) # 1
这个程序中的一切看起来都很熟悉,因为我们在前两章中都用到过。这里的主要区别在于,我们可以打印格式化的响应字符串❶,而不是将其写入文件,因为输出并不是特别长。
输出是有关 ID 为 31353677 的文章的信息字典:
{
"by": "sohkamyung",
"descendants": 302, // 1
"id": 31353677,
"kids": [ // 2
31354987,
31354235,
--snip--
],
"score": 785,
"time": 1652361401,
"title": "Astronomers reveal first image of the black hole at the heart of our galaxy", // 3
"type": "story",
"url": "https://public.nrao.edu/news/.../" // 4
}
字典中包含许多我们可以使用的键。"descendants" 键告诉我们这篇文章收到的评论数量❶。关键字 "kids" 提供了所有直接针对该提交的评论的 ID ❷。每条评论都可能有自己的评论,因此一篇文章的后代数量通常大于它的子代数量。我们可以看到被讨论文章的标题❸ 和被讨论文章的 URL ❹。
下面的 URL 返回的是 Hacker News 上当前热门文章的所有 ID 的简单列表:
https://hacker-news.firebaseio.com/v0/topstories.json
我们可以使用该调用找出现在主页上有哪些文章,然后生成一系列类似于我们刚才研究过的 API 调用。通过这种方法,我们可以打印出目前 Hacker News 首页上所有文章的摘要:
from operator import itemgetter
import requests
# Make an API call and check the response.
url = "https://hacker-news.firebaseio.com/v0/topstories.json" # 1
r = requests.get(url)
print(f"Status code: {r.status_code}")
# Process information about each submission.
submission_ids = r.json() # 2
submission_dicts = [] # 3
for submission_id in submission_ids[:5]:
# Make a new API call for each submission.
url = f"https://hacker-news.firebaseio.com/v0/item/{submission_id}.json" # 4
r = requests.get(url)
print(f"id: {submission_id}\tstatus: {r.status_code}")
response_dict = r.json()
# Build a dictionary for each article.
submission_dict = { # 5
'title': response_dict['title'],
'hn_link': f"https://news.ycombinator.com/item?id={submission_id}",
'comments': response_dict['descendants'],
}
submission_dicts.append(submission_dict) # 6
submission_dicts = sorted(submission_dicts, key=itemgetter('comments'), reverse=True) # 7
for submission_dict in submission_dicts: # 8
print(f"\nTitle: {submission_dict['title']}")
print(f"Discussion link: {submission_dict['hn_link']}")
print(f"Comments: {submission_dict['comments']}")
首先,我们调用 API 并打印响应❶的状态。该 API 调用会返回一个列表,其中包含发出该调用时 Hacker News 上最受欢迎的 500 篇文章的 ID。然后,我们将响应对象转换为 Python 列表❷,并将其赋值给 submission_ids。我们将使用这些 ID 构建一组字典,每个字典都包含当前提交的一篇文章的信息。
我们建立一个名为 submission_dicts 的空列表来存储这些字典❸。然后,我们循环浏览排名前 30 的提交的 ID。我们通过生成包含 submission_id ❹ 当前值的 URL,为每个提交创建一个新的 API 调用。我们会打印每个请求的状态及其 ID,以便查看是否成功。
接下来,我们为当前正在处理的提交创建一个字典❺。我们会存储该提交的标题、指向该项目讨论页面的链接以及该文章迄今为止收到的评论数。然后,我们将每个 submission_dict 附加到 list submission_dicts ❻。
Hacker News 上的每项提交都会根据多项因素(包括被投票的次数、收到的评论数量以及提交的新近程度)得出一个总分排名。我们希望按照评论数量对词典列表进行排序。为此,我们要使用操作员模块中一个名为 itemgetter() ❼ 的函数。我们将关键字 "评论 "传递给该函数,它就会从列表中的每个字典中提取与该关键字相关的值。然后,sorted() 函数使用该值作为列表排序的基础。我们以相反的顺序对列表排序,将评论最多的故事放在前面。
一旦列表排序完毕,我们将循环浏览列表 ❽,并打印出每篇排名靠前的文章的三条信息:标题、讨论页面链接和当前评论数:
Status code: 200
id: 31390506 status: 200
id: 31389893 status: 200
id: 31390742 status: 200
--snip--
Title: Fly.io: The reclaimer of Heroku's magic
Discussion link: https://news.ycombinator.com/item?id=3139050
6
Comments: 134
Title: The weird Hewlett Packard FreeDOS option
Discussion link: https://news.ycombinator.com/item?id=3138989
3
Comments: 64
Title: Modern JavaScript Tutorial
Discussion link: https://news.ycombinator.com/item?id=3139074
2
Comments: 20
--snip--
您可以使用类似的流程来访问和分析任何 API 的信息。有了这些数据,你就可以制作可视化图表,显示哪些提交的内容激发了最近最活跃的讨论。这也是为 Hacker News 等网站提供定制化阅读体验的应用程序的基础。要进一步了解通过 Hacker News API 可以访问哪些信息,请访问文档页面 https://github.com/HackerNews/API 。
Hacker News 有时会允许其支持的公司发布特殊的招聘信息,这些信息上的评论是禁用的。如果在出现这些帖子时运行此程序,就会出现 KeyError。如果这引起了问题,你可以用一个 try-except 块来封装构建 submission_dict 的代码,然后跳过这些帖子。 |