C# 调用谷歌搜索 API:HttpClient 与 System.Text.Json 实战

C#/.NET 项目接搜索数据的场景不少:内部工具、WinForm 小程序、ASP.NET 后台服务。这篇用最少的依赖把"调谷歌搜索 API"做成一个可复用的客户端------只用 HttpClient 和 System.Text.Json,不引第三方包,老项目也能直接抄。

客户端代码

csharp 复制代码
// SerpClient.cs
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;

public sealed class SerpClient
{
    private static readonly HttpClient _http = new() { Timeout = TimeSpan.FromSeconds(30) };

    private readonly string _apiKey;
    public SerpClient(string apiKey) => _apiKey = apiKey;

    public async Task<JsonElement> SearchAsync(
        string q, string hl = "en", string gl = "us", int page = 1,
        CancellationToken ct = default)
    {
        using var req = new HttpRequestMessage(HttpMethod.Post,
            "https://api.serpbase.dev/google/search");
        req.Headers.TryAddWithoutValidation("X-API-Key", _apiKey);
        req.Content = new StringContent(
            JsonSerializer.Serialize(new { q, hl, gl, page }),
            Encoding.UTF8, "application/json");

        using var resp = await _http.SendAsync(req, ct);
        resp.EnsureSuccessStatusCode();

        using var doc = JsonDocument.Parse(await resp.Content.ReadAsStringAsync(ct));
        var root = doc.RootElement;

        // 信封:status=0 成功;失败时 error 带原因、credits_charged 为 0(不扣费)
        if (root.TryGetProperty("status", out var st) && st.GetInt32() != 0)
            throw new SerpApiException(st.GetInt32(),
                root.TryGetProperty("error", out var e) ? e.GetString() : "unknown");

        return root.Clone();   // Clone 后 doc 释放仍可用
    }
}

public sealed class SerpApiException(int code, string message)
    : Exception($"SERP API error {code}: {message}");

调用侧------遍历自然结果只需要几行:

csharp 复制代码
var client = new SerpClient(Environment.GetEnvironmentVariable("SERPBASE_API_KEY")!);
var data = await client.SearchAsync("mechanical keyboard review");

foreach (var r in data.GetProperty("organic").EnumerateArray())
    Console.WriteLine($"{r.GetProperty("rank").GetInt32(),3}  {r.GetProperty("title").GetString()}");
//     取其他字段前先 TryGetProperty:snippet、display_url 都是可选字段

信封结构、参数表(q/hl/gl/page/device)与 organic 字段定义以 SerpBase 官方文档 为准;rank 是本页内的 1 起算位次,link 是每条必有字段。

错误分诊表

信号 含义 动作
SerpApiException 1001 key 缺失/无效 不重试,查配置
SerpApiException 1020 余额不足 告警,人工充值
SerpApiException 1029 触发限流 指数退避重试,最多 3 次
TaskCanceledException 30 秒超时 重试一次,再失败记日志
HttpRequestException 网络/连接层 退避重试

计费口径:search 端点每次成功请求 1 credit;100 次免费试用够把这个类在开发环境调稳,标准包 $0.50/1k 起。

三个 .NET 特有的坑

  1. HttpClient 要复用 :每个请求 new HttpClient() 会耗尽socket,上面代码用静态单例;要按请求换 key,改用 HttpRequestMessage 加头(本例正是这么写的),而不是改 DefaultRequestHeaders。
  2. JsonDocument 的生命周期 :RootElement 在 JsonDocument 释放后失效,返回前 Clone();遍历时用 TryGetProperty 处理可选字段,别直接 GetProperty 抛 KeyNotFoundException。
  3. SSL/代理环境 :公司内网常配自签证书或代理,HttpClientHandler 的 ServerCertificateCustomValidationCallback 和 Proxy 属性在这里配,别散在业务代码里。

FAQ

为什么不用 Refit/RestSharp? 一个 POST 接口,手写 30 行换来零依赖;Refit 适合接口面大的场景,这里收益为负。

翻页怎么做? SearchAsync(q, page: 2),page 从 1 起;翻到 organic 为空或与上页重复就停。

能同时抓 PAA、相关搜索吗? 接口返回里这些是可选模块,同样用 TryGetProperty 判存后遍历;字段结构以实际返回和文档为准,先原样存 JSON 再建模。

把这个类放进项目,配一个 DI 注册(services.AddSingleton(new SerpClient(key))),后台服务里就能随时拿结构化搜索结果了。

相关推荐
波力海苔夹心脆67514 小时前
C# 视觉检测实战:PLC 按钮触发 VisionPro 检测,OK 亮灯 / NG 灭灯(西门子 PLC,含界面显示与图片存档)
开发语言·经验分享·c#·自动化·视觉检测·.net
hez201015 小时前
一行代码在 .NET 项目里启用 Satori GC
c#·.net·.net core·gc·clr
QUDONG_biubiubiu19 小时前
.NET Runtime 6.0/7.0/8.0/9.0下载修复教程,软件打不开这样解决
.net·net runtime下载·net运行库修复·net runtime安装
宝桥南山19 小时前
.NET 11 - 尝试创建一下AGUIServer和AGUIClient(基于新版.NET SDK for AG-UI)
microsoft·ai·微软·c#·aigc·.net
波力海苔夹心脆67520 小时前
C# LINQ 入门到上手:一篇讲透查询语法、常用操作符与延迟执行(含示例与速查表
经验分享·c#·.net·solr·linq
骑士雄师2 天前
rag的具体案例:通过es在意图识别的时候进行个命中。
大数据·elasticsearch·搜索引擎
小羊没烦恼!2 天前
Windows Azure Platform体验(1):Windows Azure
java·大数据·后端·python·flask·word·.net
ly76892 天前
Elasticsearch 分片分配与再平衡的底层逻辑:从 allocation decider 到集群扩容抖动
大数据·elasticsearch·搜索引擎·集群扩容·分片分配·磁盘水位线
Cx330❀3 天前
Qt 多线程深度解析:从底层原理到 UI 线程与同步实战
开发语言·qt·ui·搜索引擎·性能优化·图形渲染
ly76893 天前
Elasticsearch 写入链路的 refresh、flush 与 translog 边界:为什么 bulk 吞吐会突然塌陷
大数据·elasticsearch·搜索引擎·translog·写入链路·bulk调优