我如何用 Python 抓取 Hacker News(2 种方法,附完整代码)

最后更新于 April 16, 2026

几个月前,我想给 Thunderbit 团队做一份每天更新的 Hacker News 热点摘要。最开始我的想法特别简单:把网站收藏起来,每天早上手动过一遍。结果只坚持了大概三天,我就发现自己每天都要花 20 分钟,盯着标题、把链接复制到表格里,实在太耗时间了。

Hacker News 是互联网上最集中、最有价值的技术情报来源之一——每月大约有 ,每天会新增大约 1300 条故事,产生约 1.3 万条评论。无论你是想追踪新兴技术趋势、监测品牌声量、从 “Who's Hiring” 线程里挖招聘线索,还是单纯想跟上开发者社区最近在关注什么,靠人工盯这些内容都注定会很吃力。

好消息是:用 Python 抓取 Hacker News 出乎意料地简单。在这篇指南里,我会带你完整走过两种方案——用 BeautifulSoup 做 HTML 抓取,以及用官方 HN Firebase API——还会讲到分页、数据导出、适合生产环境的实践,以及一个在 Python 显得太重时可以直接上手的无代码替代方案。

为什么要用 Python 抓取 Hacker News?

Hacker News 不只是一个普通的链接聚合站。它更像是一个由社区筛选出来的资讯流,最值得关注的科技故事会通过点赞和活跃讨论自然冲到前面。用户群也明显偏技术圈(约 ),再加上网站 66% 的直接流量占比,说明这里聚集的是一批忠实、习惯性阅读的人,而不是随便路过看一眼的用户。

人们之所以会抓取 HN 数据,主要就是这些场景:

使用场景你能获得什么
每日技术简报将热门故事、分数和链接推送到邮箱或 Slack
品牌/竞品监控当你的公司或产品被提及时发出提醒
趋势分析追踪哪些技术、语言或话题在一段时间内热度上升
招聘从 “Who's Hiring” 线程中解析职位、技术栈和薪资信号
内容选题研究找到高表现话题,用于写作或分享
情绪分析判断社区对产品、发布或行业变化的看法

Stripe、Dropbox、Airbnb 这些合计市值超过 4000 亿美元的公司,都曾把 Hacker News 当成早期反馈和用户获取的重要来源。Drew Houston 曾在 2007 年 4 月把 Dropbox 演示发到 HN,上榜第一名后,测试名单在一天内从 5000 人暴涨到 7.5 万人。HN 数据不仅有意思,而且很有商业价值。

这些数据本身是公开可得的,但网站结构让手动收集变得特别麻烦。用 Python 自动化处理,才是更现实的办法。

用 Python 抓取 Hacker News 的两种方式:概览

本指南会介绍两种完整、可直接运行的方法:

  1. 使用 requests + BeautifulSoup 抓取 HTML —— 直接获取 news.ycombinator.com 的原始 HTML,并解析出故事数据。适合学习抓取基础,也适合拿到页面上实际展示的内容。
  2. 使用官方 Hacker News Firebase API —— 直接请求 JSON 接口,不需要解析 HTML。更适合稳定的数据管道、评论抓取以及历史数据分析。

下面这个对比表可以帮你判断哪种更适合你:

对比项HTML 抓取(requests + BS4)HN Firebase APIThunderbit(无代码)
上手复杂度中等(需要解析 HTML 选择器)低(JSON 接口)无(2 步 Chrome 扩展)
数据时效性首页实时实时(按 ID 获取任意条目)实时
触发限流风险中等(robots.txt 要求 30 秒抓取间隔)低(官方接口,限制宽松)由 Thunderbit 托管管理
评论获取较难(嵌套 HTML)很容易(递归 item ID)支持子页面抓取
历史数据有限可通过 Algolia Search API 获取不适用
最适合学习抓取基础稳定的数据管道非开发者、快速导出

这两种方法都附带完整可运行的 Python 代码。如果你只是想把数据拿到手,而不想写任何代码,我也会讲到那个方案。

开始之前

  • 难度: 初级到中级
  • 预计时间: 每种方法约 15–20 分钟
  • 你需要准备:
    • 已安装 Python 3.11+
    • 终端或代码编辑器
    • Chrome 浏览器(如果你想检查 HN 的 HTML 或试试无代码方案)
    • (可选,用于无代码方法)

scrape-hacker-news-methods.webp

搭建 Python 环境

在真正碰 HN 数据之前,先把环境准备好。我建议先建一个虚拟环境,这样项目依赖会更清爽。

1# 创建并激活虚拟环境
2python3 -m venv hn-scraper
3# macOS/Linux:
4source hn-scraper/bin/activate
5# Windows:
6hn-scraper\Scripts\activate
7# 安装两种方法都要用到的包
8pip install requests==2.33.1 beautifulsoup4==4.14.3 pandas==3.0.2 openpyxl==3.1.5

如果后面还要做生产级实践,比如缓存和重试,建议再装这些:

1pip install requests-cache==1.3.1 tenacity==9.1.4

不需要特殊 API Key,也不需要认证令牌。HN 的数据是开放的。

方法 1:使用 BeautifulSoup 通过 Python 抓取 Hacker News

这是最经典的做法——先拿 HTML,再解析并提取想要的数据。大多数人都是从这种方式入门网页抓取的,而 HN 简洁的表格布局,非常适合练手。

第 1 步:获取 Hacker News 首页

打开编辑器,创建一个名为 scrape_hn_bs4.py 的文件。起始代码如下:

1import requests
2from bs4 import BeautifulSoup
3> This paragraph contains content that cannot be parsed and has been skipped.
4print(f"Status: {response.status_code}, Page length: {len(response.text)} chars")

运行后,你应该会看到 Status: 200,以及大约 40,000–50,000 个字符的页面长度。这就是 HN 首页的原始 HTML,已经读进内存里了,接下来就可以直接解析。

第 2 步:理解 HTML 结构

HN 用的是表格布局——没有现代的 CSS grid 或 flexbox。页面里的每个故事都由两个关键的 <tr> 行组成:

  • 故事行<tr class="athing submission">):包含排名、标题和链接
  • 信息行(紧接着的 <tr>):包含分数、作者、发布时间和评论数

重要的选择器如下:

  • span.titleline > a —— 故事标题和 URL
  • span.score —— 投票数(例如 “118 points”)
  • a.hnuser —— 作者用户名
  • span.age —— 发布时间
  • .subtext 中文本包含 “comment” 的最后一个 <a> —— 评论数

如果你在 Chrome 里右键任意故事标题并点“检查”,会看到类似这样的结构:

1<span class="titleline">
2  <a href="https://darkbloom.dev">DarkbloomPrivate inference on idle Macs</a>
3</span>

下面对应的信息行会像这样:

1<span class="score" id="score_47788542">118 points</span>
2by <a href="user?id=twapi" class="hnuser">twapi</a>
3<span class="age" title="2026-04-16T04:06:39 1776312399">
4  <a href="item?id=47788542">2 hours ago</a>
5</span>
6| <a href="item?id=47788542">65&nbsp;comments</a>

搞懂这些选择器很关键——如果 HN 以后改了页面标记,你就得跟着更新。(顺带一提:API 方案可以直接绕开这个问题。)

第 3 步:提取标题、链接和分数

现在进入核心步骤。我们会遍历每个故事行,先从故事行里拿标题和链接,再从它下面紧挨着的信息行里取分数。

1import requests
2from bs4 import BeautifulSoup
3from pprint import pprint
4> This paragraph contains content that cannot be parsed and has been skipped.
5stories = []
6story_rows = soup.select("tr.athing")
7for row in story_rows:
8    # 从故事行中获取标题和 URL
9    title_tag = row.select_one("span.titleline > a")
10    if not title_tag:
11        continue
12    title = title_tag.get_text()
13    link = title_tag.get("href", "")
14    # 从下一行信息行中获取元数据
15    meta_row = row.find_next_sibling("tr")
16    score = 0
17    author = ""
18    comments = 0
19> This paragraph contains content that cannot be parsed and has been skipped.
20> This paragraph contains content that cannot be parsed and has been skipped.
21# 过滤出 50 分以上的故事,并按分数排序
22top_stories = sorted(
23    [s for s in stories if s["score"] >= 50],
24    key=lambda x: x["score"],
25    reverse=True,
26)
27pprint(top_stories[:10])

这段代码有几点要注意:

  • 海象运算符(:=)从 Python 3.8 开始支持。它可以让你在一行里完成赋值和判断,对 span.score 这种不一定存在的元素特别方便(比如招聘帖就没有分数)。
  • HN 在数字和 “comments” 之间用的是 \xa0(不换行空格),所以这里要按这个来拆分。
  • 有些指向 HN 站内页面的故事(比如 “Ask HN” 帖子)会用 item?id= 这种相对 URL。你可能需要在前面补上 https://news.ycombinator.com/

第 4 步:运行并查看结果

保存后执行:

1python scrape_hn_bs4.py

你应该会看到类似这样的输出:

1[{'author': 'twapi',
2  'comments': 65,
3  'score': 118,
4  'title': 'Darkbloom – Private inference on idle Macs',
5  'url': 'https://darkbloom.dev'},
6 {'author': 'sebg',
7  'comments': 203,
8  'score': 247,
9  'title': 'Show HN: I built an open-source Perplexity alternative',
10  'url': 'https://github.com/...'},
11 ...]

这就是第 1 页的 30 条故事。但 HN 在任何时间点都有几百条活跃故事。分页抓取我会在后面讲。

方法 2:使用官方 API 通过 Python 抓取 Hacker News

HN Firebase API 是官方认可的 Hacker News 数据访问方式。它不需要认证、不需要 API Key,也不需要解析 HTML,直接返回干净的 JSON。我在需要稳定运行的生产场景里,通常会优先用这个方法。

你需要知道的关键 API 端点

基础地址是 https://hacker-news.firebaseio.com/v0/。下面这些接口最重要:

This paragraph contains content that cannot be parsed and has been skipped.

一个故事条目通常长这样:

1{
2  "by": "twapi",
3  "descendants": 65,
4  "id": 47788542,
5  "kids": [47789171, 47788769, 47788762],
6  "score": 118,
7  "time": 1776312399,
8  "title": "Darkbloom – Private inference on idle Macs",
9  "type": "story",
10  "url": "https://darkbloom.dev"
11}

kids 字段里放的是直接子评论的 ID。每条评论本身也是一个 item,而且它也可能有自己的 kids——评论树就是这样一层层嵌套起来的。

第 1 步:获取热门故事 ID

创建一个名为 scrape_hn_api.py 的文件:

1import requests
2import time
3from pprint import pprint
4API_BASE = "https://hacker-news.firebaseio.com/v0"
5# 获取热门故事 ID
6response = requests.get(f"{API_BASE}/topstories.json")
7story_ids = response.json()
8print(f"Got {len(story_ids)} top story IDs")
9# 输出:Got 500 top story IDs

一次请求就能拿到 500 个故事 ID——没有 HTML 解析,没有选择器,只有一个 JSON 数组。

第 2 步:按 ID 获取故事详情

接下来我们要拿到故事本身的数据。这里会遇到“扇出”问题:500 条故事就意味着要发 500 次独立 API 请求。按我的测试,单条内容顺序请求大约要 1.2 秒。500 条的话,差不多要 10 分钟。

多数场景其实没必要抓满 500 条。下面的代码可以先抓前 30 条:

1def fetch_story(story_id):
2    """从 HN API 获取单条故事详情。"""
3    resp = requests.get(f"{API_BASE}/item/{story_id}.json")
4    return resp.json()
5> This paragraph contains content that cannot be parsed and has been skipped.
6# 按分数排序,显示前 10 条
7top = sorted(stories, key=lambda x: x["score"], reverse=True)[:10]
8pprint(top)

time.sleep(0.1) 是个小小的礼貌性延迟。Firebase API 没有明确公开限流规则,但任何 API 都不建议毫无停顿地猛刷。

第 3 步:抓取评论(递归树遍历)

这就是 API 方案比 HTML 抓取更舒服的地方。HN 的评论是深度嵌套的——回复里还有回复,层层套娃。如果用 HTML 方式处理,就得解析非常复杂的嵌套表格结构。而用 API 的话,每条评论的 kids 字段会告诉你它的子评论 ID,你只要递归遍历这棵树就行。

1def fetch_comments(item_id, depth=0, max_depth=3):
2    """递归抓取评论,直到 max_depth。"""
3    item = requests.get(f"{API_BASE}/item/{item_id}.json").json()
4    if not item or item.get("type") != "comment":
5        return []
6> This paragraph contains content that cannot be parsed and has been skipped.
7    if depth < max_depth and item.get("kids"):
8        for kid_id in item["kids"]:
9            comments.extend(fetch_comments(kid_id, depth + 1, max_depth))
10            time.sleep(0.05)
11    return comments
12# 示例:获取第一条热门故事的评论
13if stories:
14    top_story = stories[0]
15    top_story_full = requests.get(f"{API_BASE}/item/{top_story['id']}.json").json()
16    if top_story_full.get("kids"):
17        print(f"\nComments for: {top_story['title']}")
18        all_comments = []
19        for kid_id in top_story_full["kids"][:5]:  # 前 5 条一级评论
20            all_comments.extend(fetch_comments(kid_id, depth=0, max_depth=2))
21            time.sleep(0.1)
22        for c in all_comments[:15]:
23            indent = "  " * c["depth"]
24            preview = c["text"][:80].replace("\n", " ") if c["text"] else "[no text]"
25            print(f"{indent}[{c['author']}] {preview}...")

这种递归方式,比起解析嵌套 HTML 评论线程要省心多了。如果你需要完整评论树,API 绝对是更合适的选择。

第 4 步:运行并查看结果

1python scrape_hn_api.py

你会看到结构化的故事数据,然后是一段嵌套评论预览。数据更整洁,评论也更容易拿到,而且不会因为 HN 改了 CSS 类名就把你的爬虫搞崩。

不止第 1 页:分页和历史数据

大多数 HN 抓取教程只讲第 1 页——也就是 30 条故事。这个做演示够了,但真实场景通常需要更深一点的数据。

使用 BeautifulSoup 抓取多页

HN 的分页 URL 模式很简单:?p=2?p=3,往后依次类推。每页返回 30 条故事,网站大约最多提供到第 20 页(总计约 600 条故事)。再往后就会变成空页。

1import time
2def scrape_hn_pages(num_pages=5):
3    """抓取 HN 首页的多页内容。"""
4    all_stories = []
5    for page in range(1, num_pages + 1):
6        url = f"https://news.ycombinator.com/news?p={page}"
7        response = requests.get(url, headers=headers)
8        soup = BeautifulSoup(response.text, "html.parser")
9        story_rows = soup.select("tr.athing")
10        if not story_rows:
11            print(f"Page {page}: no stories found, stopping.")
12            break
13        for row in story_rows:
14            title_tag = row.select_one("span.titleline > a")
15            if not title_tag:
16                continue
17            meta_row = row.find_next_sibling("tr")
18            score = 0
19            if meta_row and (score_tag := meta_row.select_one("span.score")):
20                score = int(score_tag.get_text().replace(" points", ""))
21> This paragraph contains content that cannot be parsed and has been skipped.
22        print(f"Page {page}: scraped {len(story_rows)} stories")
23        # 遵守 robots.txt 中要求的 30 秒抓取间隔
24        if page < num_pages:
25            time.sleep(30)
26    return all_stories
27stories = scrape_hn_pages(5)
28print(f"\nTotal stories scraped: {len(stories)}")

这个 time.sleep(30) 很重要。HN 的 明确要求 30 秒的抓取间隔。忽略它,你可能会遇到限流(HTTP 429)或者临时封禁。5 页按 30 秒间隔抓取,大约需要 2.5 分钟——不算快,但很尊重对方。

如果你不想自己折腾分页, 可以自动处理点击分页和无限滚动分页。它会自动点 HN 页面底部的 “More” 按钮,不需要任何配置。

使用 Algolia API 获取 Hacker News 历史数据

Firebase API 提供的是当前数据。如果你要做历史分析——比如“2023 年最热门的 Python 话题有哪些?”或者“过去 5 年 AI 相关内容的变化趋势如何?”——那就需要

1import requests
2ALGOLIA_BASE = "https://hn.algolia.com/api/v1"
3> This paragraph contains content that cannot be parsed and has been skipped.
4# 示例:查找自 2024 年 1 月以来分数 10+ 的 Python 抓取相关故事
5results = search_hn(
6    query="python scraping",
7    tags="story",
8)
9print(f"Found {results['nbHits']} total results")
10for hit in results["hits"][:5]:
11    print(f"  [{hit.get('points', 0)} pts] {hit['title']}")

如果要按日期筛选,可以使用 numericFilters

1import calendar, datetime
2# 2024 年 1 月 1 日之后的故事
3start_date = datetime.datetime(2024, 1, 1)
4start_ts = int(calendar.timegm(start_date.timetuple()))
5> This paragraph contains content that cannot be parsed and has been skipped.
6Algolia API 的速度很快(服务端处理时间大约 59 毫秒),不需要 API Key,还支持最多 500 页分页。做大规模历史分析的话,它是最合适的选择。
7## 将抓取到的 Hacker News 数据导出到 CSV、Excel 和 Google Sheets
8我见过的每一篇 HN 抓取教程,最后几乎都会停在终端里的 `pprint()` 输出。这拿来调试很方便,但如果你要做每日摘要或者趋势分析,就得把数据真正落到文件里。下面就教你怎么做。
9### 用 Python 导出为 CSV
10```python
11import csv
12def export_to_csv(stories, filename="hn_stories.csv"):
13    """把抓取到的故事保存为 CSV 文件。"""
14    fieldnames = ["title", "url", "score", "author", "comments"]
15    with open(filename, "w", newline="", encoding="utf-8") as f:
16        writer = csv.DictWriter(f, fieldnames=fieldnames)
17        writer.writeheader()
18        writer.writerows(stories)
19    print(f"Saved {len(stories)} stories to {filename}")
20export_to_csv(stories)

用 Python 导出为 Excel

1import pandas as pd
2def export_to_excel(stories, filename="hn_stories.xlsx"):
3    """把抓取到的故事保存为 Excel 文件。"""
4    df = pd.DataFrame(stories)
5    df.to_excel(filename, index=False, engine="openpyxl")
6    print(f"Saved {len(stories)} stories to {filename}")
7export_to_excel(stories)

记得安装 openpyxl——pandas 会拿它做 Excel 引擎。如果缺了它,就会报 ImportError

推送到 Google Sheets(可选)

如果你想把数据自动推到 Google Sheets,可以用 gspread 库。不过这需要先配置 Google Cloud 服务账号(一次性设置):

1import gspread
2gc = gspread.service_account(filename="service_account.json")
3sh = gc.open("HN Daily Digest")
4worksheet = sh.sheet1
5# 将 stories 转换成行
6header = list(stories[0].keys())
7rows = [list(s.values()) for s in stories]
8worksheet.clear()
9worksheet.update([header] + rows)
10print("Pushed to Google Sheets")

无代码导出替代方案

如果为了导出数据还要配置服务账号、再写一堆导出代码,感觉比抓取本身还麻烦,我完全懂。在 Thunderbit,我们提供免费的数据导出功能,可以把抓取结果直接发到 Excel、Google Sheets、Airtable 或 Notion——不写代码、不用凭据,也不用维护管道。对于一次性数据提取,这真的快很多。下面我还会再讲。

让爬虫更适合生产环境:错误处理、缓存和定时任务

如果你只是跑一次玩玩,上面的代码已经够用了。如果你要把它每天放进工作流里跑,那还需要再加几个组件。

错误处理和重试逻辑

网络会出问题,服务器会限流,单次请求失败不应该让整个抓取任务崩掉。下面是一个带指数退避的重试函数:

1from tenacity import retry, stop_after_attempt, wait_exponential_jitter
2import requests
3@retry(stop=stop_after_attempt(5), wait=wait_exponential_jitter(initial=1, max=60))
4def fetch_with_retry(url):
5    """自动重试并使用指数退避抓取 URL。"""
6    response = requests.get(url, timeout=10)
7    response.raise_for_status()
8    return response
9# 用法:
10try:
11    resp = fetch_with_retry("https://hacker-news.firebaseio.com/v0/topstories.json")
12    story_ids = resp.json()
13except Exception as e:
14    print(f"Failed after retries: {e}")

tenacity 库可以很优雅地处理重试逻辑。它最多会重试 5 次,并带抖动的指数退避——从 1 秒开始,最大到 60 秒。这对 HTTP 429(限流)、503(服务不可用)以及短暂网络错误都很有用。

缓存响应,避免重复抓取

开发阶段你会反复运行爬虫、调整解析逻辑。如果没有缓存,每跑一次都会重新打到 HN 服务器。requests-cache 只要两行就能解决这个问题:

1import requests_cache
2requests_cache.install_cache("hn_cache", expire_after=3600)  # 缓存 1 小时

把这两行加到脚本顶部后,所有 requests.get() 调用都会自动缓存到本地 SQLite 数据库。一个小时内重复跑 10 次,真正发起网络请求的只有第一次。这也是论坛里经常推荐的工具,确实有道理。

将抓取和解析分离

有经验的爬虫开发者都很喜欢一个模式:先下载原始数据,再解析。这样如果解析逻辑有 bug,你修好后可以直接重新解析,不用再重新抓一遍。

1import os, json
2def crawl_and_save(story_ids, output_dir="raw_data"):
3    """抓取故事数据并将原始 JSON 保存到磁盘。"""
4    os.makedirs(output_dir, exist_ok=True)
5    for sid in story_ids:
6        filepath = os.path.join(output_dir, f"{sid}.json")
7        if os.path.exists(filepath):
8            continue  # 跳过已抓取的条目
9        resp = fetch_with_retry(f"{API_BASE}/item/{sid}.json")
10        with open(filepath, "w") as f:
11            json.dump(resp.json(), f)
12> This paragraph contains content that cannot be parsed and has been skipped.
13当你要抓几百条数据、又想快速迭代处理逻辑时,这种两阶段方式特别有价值。
14### 按计划自动运行爬虫
15如果你要做每日 HN 简报,就需要让爬虫自动跑起来。常见的方式有两种:
16**方式 1:cron(Linux/Mac)**
17```bash
18# 每天 UTC 时间 8:30 运行
1930 8 * * * /usr/bin/python3 /home/user/scrape_hn.py >> /home/user/scrape.log 2>&1

方式 2:GitHub Actions(免费,无需服务器)

1name: Scrape Hacker News
2on:
3  schedule:
4    - cron: '30 8 * * *'  # 每天 UTC 8:30 运行
5  workflow_dispatch:        # 手动触发按钮
6jobs:
7  scrape:
8    runs-on: ubuntu-latest
9    steps:
10      - uses: actions/checkout@v4
11      - uses: actions/setup-python@v6
12        with:
13          python-version: '3.12'
14      - run: pip install requests beautifulsoup4 pandas openpyxl
15      - run: python scrape_hn.py
16      - run: |
17          git config user.name "GitHub Actions Bot"
18          git config user.email "actions@github.com"
19          git add -A
20          git diff --staged --quiet || git commit -m "Update HN data $(date -u +%Y-%m-%dT%H:%M:%SZ)"
21          git push

GitHub Actions 的定时任务有几个坑要注意:所有 cron 时间都是 UTC,延迟 15–60 分钟很常见(最好用 :30 这种非整点时间,不要死卡 :00),而且如果仓库 60 天没有活动,GitHub 可能会把定时工作流关掉。记得加上 workflow_dispatch,这样测试时你可以手动触发。

如果你想要更省事的方案,Thunderbit 的 Scheduled Scraper 功能允许你直接用自然语言描述计划——比如“每天早上 8 点抓取”——不需要服务器,也不需要 cron。

当 Python 太重时:抓取 Hacker News 的无代码方式

虽然我是 Python 爱好者,而且我们团队也在做开发者工具,但我还是得实话实说:如果你现在只需要把今天的 HN 前 100 条故事拉到表格里,而且只做一次,那写、调试、运行一个 Python 脚本其实有点过度。光是准备环境(虚拟环境、装包、找选择器)花的时间,可能都比真正抓数据还长。

这就是 发挥作用的地方。流程大概是这样:

  1. 在 Chrome 中打开 news.ycombinator.com
  2. 点击 Thunderbit 扩展图标,然后选择 “AI Suggest Fields”
  3. AI 会自动读取页面,并建议字段:Title、URL、Score、Author、Comment Count、Time Posted
  4. 如果需要,你可以调整字段(改名、删除、增加自定义字段,甚至加一个 AI 提示词,比如“把它分类为 AI/DevTools/Web/Other”)
  5. 点击 “Scrape”——数据就会以结构化表格形式出现
  6. 导出到 Excel、Google Sheets、Airtable 或 Notion

两次点击,就能拿到结构化数据。不用选择器,不写代码,也不用维护。

这里还有一个很大的优势:Thunderbit 的 AI 可以自动适应页面布局变化。传统基于 CSS 选择器的爬虫,一旦网站改了标记就可能失效——而 HN 的 HTML 虽然整体比较稳定,但也改过(比如 class="athing submission" 这个类更新过,span.titleline 也替代了更早版本的 a.storylink)。AI 驱动的爬虫每次都会重新读取页面,所以不受 class 名变化影响。

python-vs-thunderbit-comparison.webp

Thunderbit 还能自动处理分页(自动点击 HN 的 “More” 按钮)和子页面抓取(进入每个故事的评论页,提取讨论数据)。对于 这个场景,它相当于方法 2 中的递归 API 代码,但你一行代码都不用写。

取舍其实很简单:当你需要自定义逻辑、复杂数据转换、定时自动化管道,或者你本来就在学编程时,Python 更合适。当你想快速拿到数据、不想维护代码,或者你不是开发者时,Thunderbit 更合适。选最适合你当前需求的工具就行。

Python、API 还是无代码:该选哪种方法?

下面是完整的决策框架:

对比项BeautifulSoup(HTML)Firebase APIAlgolia APIThunderbit(无代码)
需要的技术水平中级 Python初级 Python初级 Python不需要
搭建时间10–15 分钟5–10 分钟5–10 分钟2 分钟
维护成本中等(选择器可能失效)低(稳定 JSON)低(稳定 JSON)
数据深度仅首页任意条目、用户搜索 + 历史数据首页 + 子页面
评论较难容易(递归)容易(嵌套树)子页面抓取
历史数据是(完整归档)
导出方式需自行编写需自行编写需自行编写内置(Excel、Sheets 等)
定时任务cron / GitHub Actionscron / GitHub Actionscron / GitHub Actions内置调度器
最适合学习抓取稳定数据管道研究与分析快速提取数据

如果你正在学 Python,或者要做一些定制化项目,可以选方法 1 或 2。如果你需要历史分析,再加上 Algolia API。如果你只是想要数据,不想写代码,直接

结论与要点总结

现在,你手里已经有了这些工具:

  • 两种完整的 Python 方法来抓取 Hacker News——BeautifulSoup 适合 HTML 解析,Firebase API 适合干净的 JSON 数据
  • 分页技巧,可以抓第 1 页以外的内容;还包括回溯到 2007 年的 Algolia API 历史数据
  • 导出代码,可输出 CSV、Excel 和 Google Sheets——因为终端里的数据,对团队其他人来说并不实用
  • 生产级实践——重试逻辑、缓存、抓取/解析分离,以及通过 cron 或 GitHub Actions 实现定时自动化
  • 无代码替代方案,适合 Python 对你来说有点“杀鸡用牛刀”的场景

我的建议是:大多数场景先从 Firebase API(方法 2)开始。它更干净、更稳定,而且能轻松拿到评论,不用费劲解析嵌套 HTML。需要历史数据时,再加上 Algolia API。至于 ,建议你先收藏起来——当你只想快速拿一份表格、不想搭整个 Python 项目时,它会很好用。

如果你想继续往下挖,可以试着抓取 HN 评论做 ,用 GitHub Actions 搭一个每日简报管道,或者研究 Algolia API,看看过去十年技术趋势是怎么变化的。

试用 Thunderbit,快速抓取 Hacker News

常见问题

抓取 Hacker News 合法吗?

HN 的数据是公开可访问的,Y Combinator 也提供了官方 API 专门支持程序化访问。网站的 允许抓取只读内容(首页、条目页、用户页),但要求 30 秒抓取间隔。只要你尊重这个间隔,不去抓交互接口(投票、登录等),通常就没问题。关于抓取伦理的更多内容,可以参考我们的 指南。

Hacker News 有官方 API 吗?

有。 位于 hacker-news.firebaseio.com/v0/,免费、无需认证,可以访问故事、评论、用户资料以及所有信息流类型(top、new、best、ask、show、jobs)。它返回干净的 JSON,虽然没有明确公开限流上限,但还是建议控制请求频率,保持礼貌。

如何用 Python 抓取 Hacker News 评论?

使用 Firebase API 先拿故事条目,从中读取 kids 字段(一级评论 ID 数组)。每条评论本身也是一个 item,它也有自己的 kids 字段表示回复。你可以用递归函数逐层抓取每条评论及其子评论。完整代码请看上面的“抓取评论(递归树遍历)”部分。或者,使用 也能一次性返回完整的嵌套评论树,对评论很多的故事会快得多。

不写代码也能抓取 Hacker News 吗?

可以。 是一个 Chrome 扩展——打开 HN,点 “AI Suggest Fields”,它会自动识别标题、URL、分数、作者等字段。点击 “Scrape” 后,就能直接导出到 Excel、Google Sheets、Airtable 或 Notion。它还能处理分页,甚至能进入子页面抓评论数据。无需 Python,无需选择器,也无需维护。

如何获取 Hacker News 的历史数据?

是最好的工具。使用 search_by_date 端点,并通过 numericFilters=created_at_i>TIMESTAMP 按日期范围筛选。你可以按关键词搜索、按故事类型过滤,并在最多 500 页结果中分页查看。对于大规模历史分析,也可以在 (完整归档)、(2800 万条记录)以及 (400 万条故事)上找到公开数据集。

了解更多

Shuai Guan
Shuai Guan
Co-founder/CEO @ Thunderbit. Passionate about cross section of AI and Automation. He's a big advocate of automation and loves making it more accessible to everyone. Beyond tech, he channels his creativity through a passion for photography, capturing stories one picture at a time.
目录

试试 Thunderbit

只需 2 次点击即可抓取线索和其他数据。AI 驱动。

获取 Thunderbit 免费使用
使用 AI 提取数据
轻松将数据传输到 Google Sheets、Airtable 或 Notion
Chrome Store Rating
PRODUCT HUNT#1 Product of the Week