Blog/开发者工具/

用一个 API 监测 Reddit 社区:一套实用工作流 | SandBase

搭一套 Reddit 社区监测工作流:评估社区规模、发现话题讨论、给背后的作者建档——一把 SandBase key,不用 OAuth 应用。

深色电影质感画面:一个社区解析成话题搜索结果和作者资料信号,汇入 agent 核心

在 Reddit 上做社区监测,归根到底是三个问题:这个社区有多大、大家在就我关心的话题说什么、以及是谁在说?这篇教程用 SandBase Reddit API 把这三个问题串成一套工作流——你自己不用 Reddit OAuth 应用,也不用 PRAW,但仍然需要一把 SandBase API key。端点参考只保证响应信封(id、status、model、outputs[0].data);下面出现的业务字段是示例结构、并非保证的 schema,所以字段名和数值都当作示例看,以真实响应为准核对。

如果你想先看完整的端点全景,从 Reddit 公开数据 API hub 开始。这一篇是落地的工作流。

先说结论

  • 三步:subreddit-info(基线)→ dynamic-search(发现)→ user-profile(作者信号)。
  • 每次都是 POST /v1/api/reddit/<path>,一把 SANDBASE_API_KEY;响应共用 { id, status, model, outputs } 信封。
  • 在这些示例结构里,Reddit 的载荷嵌在命名键下(subredditInfoByName、redditorInfoByName)——读准确的路径,并以真实响应为准核对。
  • 只有信封(id、status、model、outputs[0].data)是保证的;业务字段是示例——以真实响应为准核对。
  • 只是公开、只读数据;你自己不用 Reddit OAuth 应用,也不能发帖或投票,但仍需要一把 SandBase API key。

工作流全貌

步骤端点输入你拿到
1. 评估社区reddit/app/subreddit-infosubreddit_name订阅数、类型、描述
2. 发现讨论reddit/app/dynamic-searchquery某话题的搜索结果
3. 给作者建档reddit/app/user-profileusernamekarma 明细、账号标记

SandBase Reddit 端点参考,展示本工作流用到的社区和用户端点 端点的 API 参考是每个参数名和响应路径的权威来源。

第 1 步 —— 评估社区

监测一个社区之前,先建立基线:它有多大、多活跃?按名称解析社区:

import os
import requests

BASE = "https://api.sandbase.ai/v1/api/reddit"
HEADERS = {
    "Authorization": f"Bearer {os.environ['SANDBASE_API_KEY']}",
    "Content-Type": "application/json",
}


def call(path: str, payload: dict) -> dict:
    resp = requests.post(f"{BASE}/{path}", headers=HEADERS, json=payload, timeout=90)
    resp.raise_for_status()
    body = resp.json()
    if body.get("status") != "completed":
        raise RuntimeError(body.get("error", {}).get("message", "request did not complete"))
    return body["outputs"][0]["data"]


info = call("app/subreddit-info", {"subreddit_name": "programming"}).get("subredditInfoByName", {})
print(info.get("name"), info.get("subscribersCount"), info.get("type"))
# 示例输出:programming 6922588 PUBLIC

注意这个示例结构里的嵌套:载荷在 subredditInfoByName 下,带 name、subscribersCount、type 这样的字段。只有信封是保证的,所以代码用 .get() 防御式读取,而不是假设这些键一定存在。上面的数值是示例——一次社区读取可以浮现订阅数、一个 type 和一个社区 id,但请以真实响应为准核对具体字段名和数值,因为业务字段不是保证的 schema。把这个基线存下来,后续运行就能追踪订阅数随时间的增长。

第 2 步 —— 发现话题讨论

有了基线,就搜索关于你话题的讨论。dynamic-search 接一个 query(你的搜索关键词),按参考它还有相互独立的可选参数:用于排序结果的 sort_type,以及从上一次响应带过来、用来取下一页的 after 游标:

results = call("app/dynamic-search", {"query": "machine learning"})
# results 是一个结构化的搜索布局——读 schema,
# 并看一份真实响应,对准你实际要用的字段路径再迭代;
# 要翻页,就传上一次响应里的 `after` 游标

dynamic-search 的响应是一个结构化布局,不是扁平列表,而且只有信封是保证的——它的确切业务结构在公开参考里没有完整记录。带上 query 和你需要的可选 sort_type 跑一次真实请求,看响应,对准你实际用到的字段路径——不要假设一次调用就同时返回帖子和社区。

SandBase Reddit dynamic-search API 参考,展示 query 参数和响应 schema dynamic-search 在 search 键下返回一个结构化布局——迭代前先读 schema。

第 3 步 —— 给作者建档

浮现出讨论之后,给背后的账号建档,以此给它们的信号加权。user-profile 接一个 username:

user = call("app/user-profile", {"username": "spez"}).get("redditorInfoByName", {})
karma = user.get("karma", {})
print(user.get("name"), karma.get("total"), "total karma")
# 示例输出:spez 940980 total karma
print("employee:", user.get("isEmployee"))
# 示例输出:employee: True

同样注意这个示例结构里嵌在 redditorInfoByName 下。一次用户读取可以浮现一个 karma 对象,带 fromPosts、fromComments 和 total 这样的字段,还有像 isEmployee 这样的账号标记。只有信封是保证的,所以代码用 .get(),而不是假设这些键一定存在。把 karma 和账号年龄当作可选的排序或过滤信号——它们是上下文,不是对可信度或专业性的确证。下面是一个示例响应结构:业务字段不是保证的 schema,请把字段名和数值都当作示例,以一份真实响应为准核对具体字段(载荷会变、数值也会随时间变)。

{
  "id": "a98dfb8b-afa5-4471-9f91-33cadaca5d99",
  "status": "completed",
  "model": "reddit/app/user-profile",
  "outputs": [
    {
      "data": {
        "redditorInfoByName": {
          "name": "spez",
          "id": "t2_1w72",
          "isEmployee": true,
          "karma": { "fromPosts": 184487, "fromComments": 756493, "total": 940980 }
        }
      }
    }
  ]
}

SandBase Reddit user-profile API 参考,展示 username 参数和响应 schema user-profile 在 redditorInfoByName 下返回 karma 和账号标记。

把它串起来

一次最小的监测过程长这样。它对每个业务字段都用 .get() 读取,因为只有信封是保证的,示例字段在真实响应里可能缺失或嵌套方式不同:

# 1. 给你监测的社区建基线
baseline = call("app/subreddit-info", {"subreddit_name": "programming"}).get("subredditInfoByName", {})
record = {"subreddit": baseline.get("name"), "subscribers": baseline.get("subscribersCount")}

# 2. 找关于你话题的讨论
hits = call("app/dynamic-search", {"query": "machine learning"})
# 看一份真实响应,按 schema 提取作者用户名

# 3. 给每个作者附上 karma/账号上下文
for username in extract_usernames(hits):
    user = call("app/user-profile", {"username": username}).get("redditorInfoByName", {})
    record.setdefault("authors", []).append({
        "name": user.get("name"),
        "karma": user.get("karma", {}).get("total"),
        "employee": user.get("isEmployee", False),
    })

因为每次调用共用同一个信封和同一个 call 帮助函数,加重试或速率退避是一处改动的事。当你需要的不止这三次读取时,查线上 Reddit 列表找到合适的端点,接入前先确认它的参数。

处理粗糙的边角

  • 注意嵌套。 在这些示例结构里,Reddit 的载荷在命名键下(subredditInfoByName、redditorInfoByName)。只有信封是保证的,所以读准确的路径要防御式,别假设是顶层字段。
  • 迭代前先看 dynamic-search。 它的布局是结构化的,不是扁平列表,公开参考也没完整记录业务字段——从真实响应里对准字段路径,用 after 游标翻页,需要特定顺序时再设 sort_type。
  • 对 status 分支。 failed 或 timeout 的请求带 error 而没有 outputs。call 帮助函数已经强制这一点。
  • 尊重速率限制。 遇到 HTTP 429 用退避,并控制轮询节奏。
  • 只是公开数据。 你自己不用 Reddit OAuth 应用,不能投票、发帖,也访问不了私密社区——但每次调用都用一把 SandBase API key 鉴权。

为什么在 API 层做监测

你当然可以开个浏览器标签页手动翻一个社区,但那不 scale,也给不了你可以做趋势的结构化数据。把这三次调用排成定时任务,就把定性的浏览变成了可度量的信号:能逐周画图的订阅数、能去重和聚类的话题命中、以及能当可选排序信号的作者 karma。这套工作流采集的是公开对话数据;做情感分析是你在其上另跑的一个分析步骤。因为调用返回的是命名的 JSON 字段,你可以把每一轮存进一张表,再和上一轮做 diff——增长、新出现的声音、一个社区谈论内容的变化。

同样的统一信封也让这套工作流可组合。把 programming 换成任意社区,把 query 换成任意话题,代码路径完全一样。再加第四次调用——比如对某个具体帖子的 post-comments——它就用同一个 call 帮助函数、同一套错误处理接进来。这就是一把 key、一种结构的 API 在监测工作里的实际回报:你把时间花在信号意味着什么上,而不是花在维持一个爬虫的存活上。

常见问题

需要 Reddit 的 OAuth 应用或账号吗? 不需要。你用 SANDBASE_API_KEY 向 SandBase 鉴权。这些读取端点不用 Reddit OAuth 应用、不用 PRAW,你这边也不用账号。

结果和评论怎么翻页? dynamic-search 接一个可选的 after 游标请求参数,你从上一次请求里把它带过来;post-comments 接一个 after 游标,外加一个 sort_type 给帖子排序。看一份真实响应,弄清你从哪个字段读下一个 after 值,因为它的布局是结构化的,不是扁平列表。

能读私密或仅限账号的数据吗? 不能。这套工作流只是公开、只读数据——社区统计、搜索结果和公开的资料字段。不能投票、发帖,也访问不了私密社区。

下一步

你现在有了一套可复用的社区监测工作流,建立在三次公开、只读的调用上。从这里出发,你可以把它排成定时任务持续追踪订阅增长,再把采集到的对话数据喂给一个单独的情感分析或排序步骤。