采集量一上来,你会发现 credit 消耗里有一部分是「白花的」:同一个词在调试时查了八遍、失败重跑把已成功的词又查了一遍、两个任务重叠查了同一批词。这些重复请求不会带来任何新信息,只会加速余额消耗。这篇用两级缓存 + 去重,把重复请求砍掉。
先定「什么能缓存、缓多久」
不是所有数据都适合缓存,先按端点分类(TTL 按你的业务容忍度定):
| 数据类型 | 建议 TTL | 理由 |
|---|---|---|
排名(/google/search) |
当天有效(按天为键) | 排名日内波动小,按天缓存最符合业务语义 |
新闻(/google/news) |
10-30 分钟 | 新闻时效高,长了会漏 |
地图商家(/google/maps/*) |
7 天 | 商家信息变化慢 |
| 图片/视频列表 | 1 天 | 变化慢,但没必要更久 |
缓存键:全部参数,一个不能少
这是缓存最容易翻车的地方:键必须包含所有影响结果的参数 ------endpoint + q + gl + hl + page。少一个,就会把「美国市场的结果」返给「德国市场」的请求。
python
import hashlib, json
def cache_key(endpoint: str, params: dict) -> str:
raw = json.dumps({"e": endpoint, "p": params}, sort_keys=True, ensure_ascii=False)
return hashlib.md5(raw.encode()).hexdigest()
两级缓存:内存 + 落盘
- 内存层:同一次任务内命中,零成本
- 落盘层(SQLite) :跨任务、跨天补跑命中------失败重跑只补没成功的词
python
import sqlite3, time
con = sqlite3.connect("cache.db")
con.execute("""CREATE TABLE IF NOT EXISTS http_cache (
key TEXT PRIMARY KEY, body TEXT NOT NULL, saved_at REAL NOT NULL
)""")
def get_cache(key: str, ttl: int):
row = con.execute("SELECT body, saved_at FROM http_cache WHERE key=?", (key,)).fetchone()
if row and (time.time() - row[1]) < ttl:
return json.loads(row[0])
return None
def set_cache(key: str, body: dict):
con.execute("INSERT OR REPLACE INTO http_cache VALUES (?,?,?)",
(key, json.dumps(body, ensure_ascii=False), time.time()))
con.commit()
def fetch_cached(endpoint: str, params: dict, ttl: int) -> dict:
key = cache_key(endpoint, params)
hit = get_cache(key, ttl)
if hit is not None:
return hit
resp = requests.post(f"https://api.serpbase.dev/google/{endpoint}",
headers=HEADERS, json=params, timeout=(5, 30))
data = resp.json()
if data.get("status") == 0: # 只缓存成功响应!
set_cache(key, data)
return data
去重:两层
- 词表去重 :任务开始前
set(keywords)去重,重复词直接删 - 在途合并:并发任务里同一个词只发一次请求(用一个「进行中」的字典登记,后来者等结果)------这就是 SingleFlight 的思路,能挡住并发场景的重复查询
效果(一次真实场景)
一次「修 bug 后重跑」的 500 词任务:480 个词命中缓存(前一天刚采过),只发了 20 次真实请求------credits 消耗从 500 降到 20。缓存把「重跑成本」几乎清零,这对调试和失败恢复意义很大。
踩坑记录
坑 1:缓存键缺参数。 最容易发生的事故:串市场、串页码。全参数入键。
坑 2:TTL 一刀切。 排名按天、新闻按分钟------统一 TTL 要么漏数据要么浪费。按端点分档。
坑 3:缓存了失败响应。 把 status != 0 的结果缓存下来,下次「命中」一个错误。只缓存成功响应。
坑 4:缓存表无限增长。 没清理机制的缓存迟早变成第二张日志表。按时间/条数定期清理。
坑 5:跨天误用。 排名缓存必须以「天」为键的一部分,不能拿昨天的排名当今天的。日期入键。
坑 6:把缓存当数据库。 缓存会被清理/过期,分析数据必须照常落库。缓存是加速层,不是数据层。
小结
省 credit 的组合拳:全参数缓存键 → 两级缓存(内存+落盘)→ 词表/在途去重 → 只缓存成功响应 。对采集任务来说,这套机制最大的价值不只是省钱------它让「重跑」变得几乎免费,而重跑自由,正是调试和容错能力的基础。
端点计费规则(1/2 credits)与信封字段说明见 SerpBase 官方文档。利益相关说明:文中提到的服务是我自己在用的,花自己的钱,独立体验。你们给采集做过缓存层吗?命中率多少?评论区聊聊。