登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  Golang >  Go教程

Go pkg.go.dev API 怎么读取包文档索引

来源:17golang原创

时间:2026-10-05 19:26:20 381浏览 收藏

用 pkg.go.dev API 读取“包文档索引”时,不要只请求一个端点。当前稳定接口是 /v1:/v1/package/{path} 负责包元数据和文档正文,/v1/symbols/{path} 负责函数、类型、方法等结构化符号。把两份结果合并,才是一份适合本地搜索或文档导航的索引。

最小结论
  • 包文档正文:请求 /v1/package/{path}?doc=md&examples=true。
  • 符号索引:请求 /v1/symbols/{path},并沿用 nextPageToken 翻页。
  • 结果要保存 modulePath、version、goos 和 goarch,避免索引语义漂移。

官方 API 文档:https://pkg.go.dev/v1/api

问题现场:为什么只拿到包元数据

直接调用 /v1/package/{path} 时,返回值通常包含包名、摘要、模块路径和版本,却没有完整文档。这不是接口失效,而是因为文档正文需要显式传入 doc 参数。可选值包括 text、html、md 或 markdown;做本地检索时,Markdown 通常更容易保存和二次处理。

另一方面,docs 是一段完整文档,不适合直接回答“这个包有哪些函数和类型”。结构化目录来自 /v1/symbols/{path}。它的 symbols.items 会分别给出符号名、种类、摘要和父级关系。

pkg.go.dev v1 package 文档端点与 symbols 符号端点合并为本地索引的结构图
图1:包文档正文与结构化符号索引来自不同端点,客户端可合并成自己的检索数据。

初步判断:两个端点各取什么

目标请求关键字段
包级信息与正文/v1/package/{path}?doc=md&examples=truename、synopsis、docs、modulePath、version
函数、类型、方法目录/v1/symbols/{path}symbols.items、nextPageToken
消除路径歧义原请求增加 modulecandidates 中选择的模块路径

官网早期发布文章曾展示 /v1beta,但当前 API 文档已经使用 /v1。新代码应以当前官方 API 页面为准,不要继续复制旧的 beta 地址。

动手验证:先用两条请求看清返回结构

# 返回包元数据,并把 Markdown 文档放进 docs 字段。
curl -L 'https://pkg.go.dev/v1/package/golang.org/x/time/rate?doc=md&examples=true'

# 返回函数、类型、方法等结构化符号。
curl -L 'https://pkg.go.dev/v1/symbols/golang.org/x/time/rate?limit=100'

第一条请求适合建立包级文档记录,第二条请求适合建立符号级倒排索引。不要抓取 pkg.go.dev 的 HTML 页面来补目录:API 已经提供稳定字段,而且 HTML 展示结构可能随页面改版变化。

Go 客户端:读取正文并遍历完整符号页

下面的示例保留了 HTTP 状态检查、超时和分页。导入路径按斜杠分段转义,避免把整个路径中的斜杠编码掉。

package pkgindex

import (
    "context"
    "encoding/json"
    "fmt"
    "net/http"
    "net/url"
    "strings"
    "time"
)

type PackageDoc struct {
    Path       string `json:"path"`
    Name       string `json:"name"`
    Synopsis   string `json:"synopsis"`
    Docs       string `json:"docs"`
    ModulePath string `json:"modulePath"`
    Version    string `json:"version"`
    GOOS       string `json:"goos"`
    GOARCH     string `json:"goarch"`
}

type Symbol struct {
    Name     string `json:"name"`
    Kind     string `json:"kind"`
    Synopsis string `json:"synopsis"`
    Parent   string `json:"parent"`
}

type SymbolPage struct {
    Items         []Symbol `json:"items"`
    NextPageToken string   `json:"nextPageToken"`
}

type SymbolsResponse struct {
    ModulePath string     `json:"modulePath"`
    Version    string     `json:"version"`
    Symbols    SymbolPage `json:"symbols"`
}

type Client struct {
    HTTP *http.Client
}

func NewClient() *Client {
    return &Client{HTTP: &http.Client{Timeout: 15 * time.Second}}
}

func escapeImportPath(p string) string {
    // 只转义每个路径段,保留 API 路径需要的斜杠。
    parts := strings.Split(p, "/")
    for i := range parts {
        parts[i] = url.PathEscape(parts[i])
    }
    return strings.Join(parts, "/")
}

func (c *Client) decode(ctx context.Context, endpoint string, dst any) error {
    req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil)
    if err != nil {
        return err
    }
    resp, err := c.HTTP.Do(req)
    if err != nil {
        return err
    }
    defer resp.Body.Close()
    if resp.StatusCode = 300 {
        return fmt.Errorf("pkg.go.dev API: %s", resp.Status)
    }
    // JSON 解码失败时直接返回,避免把半份索引写入存储。
    return json.NewDecoder(resp.Body).Decode(dst)
}

func (c *Client) Package(ctx context.Context, path string) (PackageDoc, error) {
    endpoint := "https://pkg.go.dev/v1/package/" + escapeImportPath(path)
    q := url.Values{"doc": {"md"}, "examples": {"true"}}
    var out PackageDoc
    err := c.decode(ctx, endpoint+"?"+q.Encode(), &out)
    return out, err
}

func (c *Client) Symbols(ctx context.Context, path string) ([]Symbol, error) {
    endpoint := "https://pkg.go.dev/v1/symbols/" + escapeImportPath(path)
    q := url.Values{"limit": {"100"}}
    var all []Symbol

    for {
        var page SymbolsResponse
        if err := c.decode(ctx, endpoint+"?"+q.Encode(), &page); err != nil {
            return nil, err
        }
        all = append(all, page.Symbols.Items...)
        if page.Symbols.NextPageToken == "" {
            return all, nil
        }
        // 下一页重复原查询,只增加服务端返回的 token。
        q.Set("token", page.Symbols.NextPageToken)
    }
}

调用时先取得 PackageDoc,再取得全部 Symbol。本地记录可以使用 modulePath@version + package path + goos + goarch 作为稳定主键,符号条目再以 kind + parent + name 作为子键。

定位原因:分页不完整与包路径歧义

如果只保存第一次 /symbols 响应,符号较多的包会缺项。分页对象中的 nextPageToken 非空时,应把原请求参数原样保留,再增加 token 发起下一次请求,直到令牌为空。

另一个常见问题是包路径可能对应多个模块。API 的错误响应会给出 candidates;选定正确模块后,在原请求增加 module 参数重试。例如模块路径为 example.com/project 时,应使用查询参数传入,而不是自行改写包路径。

pkg.go.dev API 使用 module 消除包路径歧义并用 nextPageToken 读取后续符号页的说明图
图2:路径歧义用 module 参数消除,符号分页则沿用 nextPageToken 继续请求。

修复方案:固定版本和构建上下文

省略 version 时,接口默认解析最新版本。这适合临时查询,却不适合可重复构建的文档站。生产索引应显式传入语义化版本,或在首次响应后至少把返回的 modulePath 与 version 一起保存。涉及平台差异时,再传入并记录 goos、goarch。

  • 展示型文档:保存 docs,保留 Markdown 原文。
  • 搜索与跳转:逐页保存 symbols.items。
  • 可重复刷新:固定 module、version、goos、goarch。
  • 请求控制:复用 HTTP 客户端、设置超时,并控制并发;官方文档当前说明按 IP 有请求频率限制。

验证结果:什么才算读取完整

完成一次索引后,至少检查三项:docs 是否非空;符号分页是否已经读到空的 nextPageToken;包记录中的模块和版本是否符合预期。若只有包摘要而没有正文,通常是漏了 doc;若符号数量偏少,通常是没有翻页;若同一路径内容突然变化,通常是没有固定模块版本。

常见问题

只调用 package 端点能得到符号目录吗?

不能把它当作完整的结构化目录。package 端点提供包级信息和可选文档正文,函数、类型、方法等条目应从 symbols 端点读取。

doc=html、doc=text 和 doc=md 该选哪个?

本地搜索和再渲染通常选 md;纯文本分析可选 text;只有明确需要服务端 HTML 片段时才选 html。

可以用 main 或 master 作为 version 吗?

接口允许语义化版本,也支持 main 或 master。但分支内容会移动,长期索引最好保存解析后的具体版本并制定刷新策略。

总结

pkg.go.dev 的包文档索引不是单一响应:用 package 端点取得元数据和 docs,用 symbols 端点取得结构化条目,再处理分页、模块歧义与版本上下文。这样得到的数据比抓取网页稳定,也更适合搜索、跳转和增量刷新。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>