Go 项目里要接搜索数据接口,标准库就够了——net/http + encoding/json,不用引入任何第三方依赖。这篇给一份完整可运行的代码,以及几个 Go 场景下容易踩的点。
完整代码
package main
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"os"
"time"
)
// 信封字段:六个端点共用
type Envelope struct {
Status int `json:"status"`
RequestID string `json:"request_id"`
ElapsedMs int `json:"elapsed_ms"`
CreditsCharged *int `json:"credits_charged"` // 可空,用指针
SearchType string `json:"search_type"`
}
type SearchResponse struct {
Envelope
Organic []struct {
Rank int `json:"rank"`
Title string `json:"title"`
Link string `json:"link"`
} `json:"organic"`
}
func main() {
apiKey := os.Getenv("SERPBASE_API_KEY")
body, _ := json.Marshal(map[string]string{
"q": "serp api",
"hl": "en",
"gl": "us",
})
req, _ := http.NewRequest("POST",
"https://api.serpbase.dev/google/search",
bytes.NewReader(body))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-API-Key", apiKey)
client := &http.Client{Timeout: 30 * time.Second}
resp, err := client.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
var out SearchResponse
if err := json.NewDecoder(resp.Body).Decode(&out); err != nil {
panic(err)
}
// 信封先判 status,再读 organic
if out.Status != 0 {
panic(fmt.Sprintf("SERP failed: status=%d request_id=%s", out.Status, out.RequestID))
}
fmt.Printf("elapsed=%dms\n", out.ElapsedMs)
for _, r := range out.Organic {
fmt.Printf("#%d %s %s\n", r.Rank, r.Title, r.Link)
}
}
运行:
export SERPBASE_API_KEY=你的key
go run main.go
三个 Go 场景的要点
一、credits_charged 可空,必须用指针。 计费字段可能是数字、0,也可能是 null(计费未确认)。Go 里如果用 int 接收,null 会被解析成 0——「未确认」和「确认不扣费」混为一谈,对账就错了。用 *int,nil 就是 null。
二、信封嵌套用嵌入结构体。 六个端点共用顶层字段,用 Go 的 struct embedding(Envelope 嵌入 SearchResponse)复用定义,换端点只改业务字段。
三、http.Client 复用。 别每次请求 &http.Client{} 新建——连接池没法复用。整个进程共用一个 client(默认自带连接池),批量采集时差距明显。
封装成客户端(简化版)
type Client struct {
apiKey string
http *http.Client
}
func New(apiKey string) *Client {
return &Client{apiKey: apiKey, http: &http.Client{Timeout: 30 * time.Second}}
}
func (c *Client) Search(q, hl, gl string) (*SearchResponse, error) {
body, _ := json.Marshal(map[string]string{"q": q, "hl": hl, "gl": gl})
req, _ := http.NewRequest("POST", "https://api.serpbase.dev/google/search", bytes.NewReader(body))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-API-Key", c.apiKey)
resp, err := c.http.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
var out SearchResponse
if err := json.NewDecoder(resp.Body).Decode(&out); err != nil {
return nil, err
}
return &out, nil
}
要注意的点
失败也可能计费。 官网口径:已执行的失败可能消耗 credits。重试要设上限和退避,别无限循环。
字段都是可选的。 snippet、模块字段以下游解析为准,Go 里用指针类型接收,nil 即「没有」。
Key 从环境变量读。 别硬编码进代码——Go 项目同样有把 Key 提交进仓库的风险。
成本
搜索端点 1 credit 一次。1000 个关键词一次采集 1,000 credits,$10 Starter 包(2 万 credits,无日历过期)够跑 20 轮。
小结
Go 调搜索数据接口,标准库全搞定:http.Client 复用 + 嵌入结构体复用信封 + 指针处理可空字段。三件事做对,代码既干净又不会在对账时踩坑。
接口的字段结构和计费规则见 SerpBase 官方文档。你的 Go 项目怎么调第三方接口?评论区聊聊。
转载自 CSDN-专业IT技术社区
原文链接:https://blog.csdn.net/yxdkjhfl/article/details/168242882



