Ruby 做脚本和小工具依然顺手,而且这篇的客户端只用标准库——net/http 加 json,连 gem 都不用装,ruby serp_client.rb 直接跑。适合给运维脚本、Sidekiq 任务或任意老项目里加一个搜索数据源。
客户端代码
# 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 特有的坑
Net::HTTP要use_ssl = true:https 地址忘了这行会直接报use_ssl错误;URI.parse不会自动开。把 open/read 超时显式设置,默认值在网络抖动时偏长。data['status']的类型:JSON 里数字就是 Integer,直接!= 0判断即可;但如果哪天信封升级成字符串,'0' != 0会误报——防御性写法是data['status'].to_i != 0。- 别忘了键是字符串:
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 脚本里就都能拿结构化搜索结果了。

394

被折叠的 条评论
为什么被折叠?



