Golang 中的 YouTube 转录文本:net/http 和三个结构体

这是整个程序。解释在后面。
package main
import (
"encoding/json"
"fmt"
"net/http"
"net/url"
"os"
"time"
)
type Transcript struct {
VideoID string `json:"videoId"`
Title string `json:"title"`
PubDate string `json:"pubDate"`
Content string `json:"content"`
ContentSize int `json:"contentSize"`
Truncated bool `json:"truncated"`
}
type APIError struct {
Code string `json:"code"`
Message string `json:"message"`
Status int `json:"status"`
RetryAfterSeconds int `json:"retryAfterSeconds"`
}
type envelope struct {
Result *Transcript `json:"result"`
Error *APIError `json:"error"`
}
func main() {
q := url.Values{}
q.Set("url", "https://www.youtube.com/watch?v=dQw4w9WgXcQ")
req, err := http.NewRequest(http.MethodGet,
"https://youtube2text.org/api/transcribe?"+q.Encode(), nil)
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
req.Header.Set("x-api-key", os.Getenv("YT2TEXT_KEY"))
client := &http.Client{Timeout: 60 * time.Second}
resp, err := client.Do(req)
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
defer resp.Body.Close()
var env envelope
if err := json.NewDecoder(resp.Body).Decode(&env); err != nil {
fmt.Fprintln(os.Stderr, "decode:", err)
os.Exit(1)
}
if env.Error != nil {
fmt.Fprintf(os.Stderr, "%s: %s\n", env.Error.Code, env.Error.Message)
os.Exit(1)
}
fmt.Println(env.Result.Title)
fmt.Printf("%d chars, truncated=%v\n",
env.Result.ContentSize, env.Result.Truncated)
}
go run main.go,完成。没有第三方模块,这正是在 Golang 中获取 YouTube 转录文本应该看起来的样子——API 是返回 JSON 的纯 HTTPS,所以 net/http 和 encoding/json 覆盖所有内容。没有官方的 Go 库,我会论证这是一个特性。
信封技巧
成功响应将数据放在 result 下;失败将结构化对象放在 error 下。解码两者到一个具有两个指针字段的结构体中意味着单个 Decode 调用处理每个响应——哪个指针非零就是你的答案。不需要重新读取主体、不需要嗅探 Content-Type,如果你想双重检查,env.Error.Status 匹配 HTTP 状态。
值得 switch 语句的代码
APIError.Code 是稳定的,所以在它上面分支而不是在消息字符串上:
switch env.Error.Code {
case "RATE_LIMIT_EXCEEDED":
wait := time.Duration(env.Error.RetryAfterSeconds) * time.Second
time.Sleep(wait) // then retry the request
case "TRANSCRIPT_UNAVAILABLE":
// video exists but has no caption track — skip permanently
case "VIDEO_NOT_FOUND", "INVALID_URL":
// bad input; log and move on
default:
// UNAUTHORIZED, VALIDATION_ERROR, YOUTUBE_ERROR
}
TRANSCRIPT_UNAVAILABLE 是人们处理不当的那个。转录文本来自 YouTube 的字幕轨道——手动或自动生成、优先英文、否则第一个可用语言。如果视频关闭了字幕,就没有转录文本要获取,用指数退避重试不会让字幕增长。在批处理作业中将其视为永久跳过,否则你的工人队列填满了僵尸。
参数和密钥
总共两个查询参数。url 接受任何 YouTube URL 形式或裸露的 11 字符 id。maxChars 限制输出长度(150000 是默认和最大值)——当转录文本馈送到 LLM 时有用,你按令牌付费,这是我听说的大多数 Go 用例,通常对整个频道的 RAG。
对于无需任何注册的密钥,curl https://youtube2text.org/api/demo-key 返回一个工作的——在所有匿名用户中每个 IP 每月 5 个视频共享,所以它严格用于尝试事物。如果你在权衡是否调用 yt-dlp 获取字幕文件,我写了yt-dlp 字幕对 API 的比较;如果你的栈的一部分是 Node,JavaScript 中的同一调用更短。
对于真实用途,在 youtube2text.org/app/keys 用 Google 登录——无卡、无电话、每月 5 个视频的免费层、从 50 个 5.99 美元起到无限 19.99 美元的付费层。上面的 Go 代码不改变;只有环境变量改变。