黑客新闻 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 章的地震项目中所做的那样,这样我们就可以探索返回的文章信息类型:

hn_article.py
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 首页上所有文章的摘要:

hn_submissions.py
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 的代码,然后跳过这些帖子。

亲身体验

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

17-2. 积极讨论:使用 hn_submissions.py 中的数据,制作一个条形图,显示目前在 Hacker News 上最活跃的讨论。每个条形图的高度应与每个提交的评论数相对应。每个条形图的标签应包括提交内容的标题,并作为该提交内容讨论页面的链接。如果在创建图表时出现 KeyError,请使用 try-except 块跳过宣传文章。

17-3. 测试 python_repos.py:在 python_repos.py 中,我们打印了 status_code 的值,以确保 API 调用成功。编写一个名为 test_python_repos.py 的程序,使用 pytest 来断言 status_code 的值是 200。想出一些其他断言:例如,返回的条目数是预期的,软件源总数大于一定数量。

17-4. 进一步探索:访问 Plotly 和 GitHub API 或 Hacker News API 的文档。利用你在那里找到的一些信息,定制我们已经绘制的图表的风格,或者提取一些不同的信息,创建你自己的可视化图表。如果你想探索其他 API,请访问 https://github.com/public-apis 查看 GitHub 存储库中提到的 API。

总结

在本章中,你学习了如何使用 API 编写自包含的程序,自动收集所需的数据,并使用这些数据创建可视化。您使用 GitHub API 探索了 GitHub 上最受好评的 Python 项目,还简单了解了 Hacker News API。您学会了如何使用 Requests 包自动发出 API 调用,以及如何处理该调用的结果。我们还介绍了一些 Plotly 设置,它们可以进一步自定义生成的图表的外观。

在下一章中,你将使用 Django 构建一个网络应用程序作为最终项目。