Ruby 调用谷歌搜索 API:net/http 与 json 标准库实战

Ruby 做脚本和小工具依然顺手,而且这篇的客户端只用标准库 ------net/http 加 json,连 gem 都不用装,ruby serp_client.rb 直接跑。适合给运维脚本、Sidekiq 任务或任意老项目里加一个搜索数据源。

客户端代码

ruby 复制代码
# serp_client.rb
require 'net/http'
require 'json'
require 'uri'

class SerpClient
  API_URL = URI('https://api.serpbase.dev/google/search')

  def initialize(api_key)
    @api_key = api_key
  end

  # 信封:status 为 0 表示成功;失败时 error 带原因、credits_charged 为 0(不扣费)
  def search(q, hl: 'en', gl: 'us', page: 1)
    body = { q: q, hl: hl, gl: gl, page: page }.to_json

    http = Net::HTTP.new(API_URL.host, API_URL.port)
    http.use_ssl = true
    http.open_timeout = 10
    http.read_timeout = 30

    resp = http.post(API_URL.path, body,
      'X-API-Key'    => @api_key,
      'Content-Type' => 'application/json')

    data = JSON.parse(resp.body)
    if data['status'] != 0
      raise SerpApiError.new(data['status'], data['error'])
    end
    data
  end

  class SerpApiError < StandardError
    attr_reader :code
    def initialize(code, message)
      @code = code
      super("SERP API error #{code}: #{message}")
    end
  end
end

# 用法
client = SerpClient.new(ENV['SERPBASE_API_KEY'])
data = client.search('mechanical keyboard review')

data['organic'].each do |r|
  puts format('%3d  %s', r['rank'], r['title'])
  puts "     #{r['link']}"
end

信封结构、参数表(q/hl/gl/page/device)与 organic 字段定义以 SerpBase 官方文档 为准;rank 是本页内的 1 起算位次,title 和 link 是每条必有字段,snippet、display_url 等为可选------取值前用 r['snippet'] 拿到的是 nil 也别当异常处理。

错误分诊表

信号 含义 动作
SerpApiError 1001 key 缺失/无效 不重试,查配置
SerpApiError 1020 余额不足 告警,人工充值
SerpApiError 1029 触发限流 指数退避重试,最多 3 次
Net::OpenTimeout / Net::ReadTimeout 连接/读取超时 重试一次,再失败记日志
JSON::ParserError 响应非 JSON 记录原始响应前 200 字符

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

三个 Ruby 特有的坑

  1. Net::HTTP 要 use_ssl = true :https 地址忘了这行会直接报 use_ssl 错误;URI.parse 不会自动开。把 open/read 超时显式设置,默认值在网络抖动时偏长。
  2. data['status'] 的类型 :JSON 里数字就是 Integer,直接 != 0 判断即可;但如果哪天信封升级成字符串,'0' != 0 会误报------防御性写法是 data['status'].to_i != 0。
  3. 别忘了键是字符串 :JSON.parse 默认返回字符串键的 Hash,data[:organic] 永远是 nil,要写 data['organic'];装 symbolize_names: true 参数也行,但团队统一一种风格更重要。

FAQ

为什么不装 httparty 或 faraday? 一个 POST 接口用不上它们;标准库版零依赖,扔进任何项目都不会引起 gem 冲突。等第二个接口出现再抽公共模块不迟。

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

PAA、相关搜索怎么拿? 返回里是可选模块:data['people_also_ask']、data['related_searches'],用 data.fetch('related_searches', []) 兜底遍历;字段结构以实际返回和文档为准,先原样存 JSON 再建模。

把 serp_client.rb 放进 lib/,配一行环境变量,Rake 任务和 cron 脚本里就都能拿结构化搜索结果了。

相关推荐
VX130715441222 小时前
测绘资质延续申请申报材料常见退件问题及解答
搜索引擎·arcgis·全文检索
LaughingZhu9 小时前
Product Hunt 热榜 2026-10-10:Busabase、Zernio、Playground by Google Labs
人工智能·深度学习·神经网络·搜索引擎·百度
zhangzeyuaaa2 天前
Ruby 多线程、GVL(GIL)与 Mutex 完全指南
开发语言·前端·ruby
zhangzeyuaaa2 天前
Ruby `require` 完全指南:从 `$LOAD_PATH` 到 `require_relative`
服务器·前端·ruby
zhangzeyuaaa2 天前
深入 Ruby:return 退出规则完全指南
开发语言·ruby
骑士雄师2 天前
rag的具体案例:通过es在意图识别的时候进行个命中。
大数据·elasticsearch·搜索引擎
程序猿乐锅3 天前
从0-1一文详解RabbitMQ
java·分布式·后端·中间件·rabbitmq·ruby
ly76893 天前
Elasticsearch 分片分配与再平衡的底层逻辑:从 allocation decider 到集群扩容抖动
大数据·elasticsearch·搜索引擎·集群扩容·分片分配·磁盘水位线
Cx330❀3 天前
Qt 多线程深度解析:从底层原理到 UI 线程与同步实战
开发语言·qt·ui·搜索引擎·性能优化·图形渲染
ly76893 天前
Elasticsearch 写入链路的 refresh、flush 与 translog 边界:为什么 bulk 吞吐会突然塌陷
大数据·elasticsearch·搜索引擎·translog·写入链路·bulk调优