搜索数据要不要全量重采?增量采集与数据版本化的工程实践
采集搜索数据,一个朴素但容易被忽略的问题:数据是「按天全量重采」还是「只采变化的部分」? 全量简单,但同一个关键词天天采,大部分结果没变——白花的 credits 和存储越来越多。这篇记录我怎么从全量切到增量采集,以及「版本化」是怎么解决「变化检测」问题的。
先算一笔账:全量为什么贵
假设 100 个关键词,每天全量采一次:
- 每天 100 次请求,一个月 3000 次
- 就算每次 1 credits,成本是线性增长的
- 更糟的是存储:每天都在存「基本没变的」结果,表越来越肥,分析越来越慢
真实情况是:大多数关键词的 SERP 一两天内变化很小。大部分请求和存储,采的都是重复数据。
增量采集的核心:先回答「变没变」
增量不是「少采一点」,而是「先判断要不要采」。核心是变化检测:用一个轻量的「指纹」判断 SERP 是否变化,变了才重采。
import hashlib
def serp_fingerprint(data):
"""用结果的 URL 序列做指纹,判断 SERP 是否变化"""
urls = [r.get("link", "") for r in data.get("organic", [])]
return hashlib.sha256("\n".join(urls).encode()).hexdigest()
# 昨天的指纹 vs 今天的指纹
prev_fp = load_fingerprint(keyword)
cur_fp = serp_fingerprint(current_data)
if cur_fp != prev_fp:
store_snapshot(keyword, current_data) # 变了才存新快照
save_fingerprint(keyword, cur_fp)
else:
touch(keyword) # 没变,只更新「上次检查时间」
用「URL 序列」做指纹,是搜索数据变化检测里性价比最高的方案——比字段全比对省,又能抓住「谁上榜了、谁掉榜了」这类核心变化。
那每天还要不要全量查?
矛盾来了:变化检测本身也需要一次请求(不查怎么知道变没变)。
所以增量采集的正确打开方式不是「隔天不查」,而是分两层:
第一层:每日轻检查(一定查)
每个关键词每天至少查一次,算指纹、做变化检测。这一步保证「变化不会漏」。
第二层:快照只存变化
只有「指纹变化」的关键词才落新快照,没变的只更新「上次检查时间」和「连续未变天数」。
这样请求量不省(每天还是要查一遍),但存储和后续分析省一大截——表里只有「有变化的版本」,而不是每天的重复全量。
版本化:把数据存成「版本链」
增量采集的产物是「版本化快照」——每个关键词一个版本链:
import sqlite3
con = sqlite3.connect("serp_versions.db")
con.execute("""CREATE TABLE IF NOT EXISTS snapshots (
keyword TEXT,
version INTEGER, -- 第几个版本
fingerprint TEXT,
payload TEXT, -- JSON 快照
first_seen TEXT, -- 这个版本第一次出现
last_seen TEXT, -- 最后一次确认仍是这个版本
PRIMARY KEY (keyword, version)
)""")
def store_if_changed(keyword, data, today):
fp = serp_fingerprint(data)
cur = con.execute(
"SELECT version, fingerprint FROM snapshots WHERE keyword = ? ORDER BY version DESC LIMIT 1",
(keyword,),
).fetchone()
if cur is None or cur[1] != fp:
version = (cur[0] if cur else 0) + 1
con.execute(
"INSERT INTO snapshots VALUES (?, ?, ?, ?, ?, ?)",
(keyword, version, fp, json.dumps(data), today, today),
)
else:
con.execute(
"UPDATE snapshots SET last_seen = ? WHERE keyword = ? AND version = ?",
(today, keyword, cur[0]),
)
con.commit()
first_seen / last_seen 两个字段,让「这个版本在 SERP 上待了几天」成为可查询的数据——一个版本存在的时长,本身就是有价值的信号(排名长期稳定 vs 频繁变动)。
版本化的收益
- 存储省:只存变化的版本,表瘦一个数量级
- 趋势分析准:版本链天然是「SERP 演变历史」,回溯「什么时候开始变、变了多少次」直接查表
- 审计可溯:每个版本有指纹,能确认「这是那个时刻的真实快照」
配合之前的基础设施
增量采集不是孤立方案,和之前几篇是配合关系:
- 缓存层(缓存篇):短期命中缓存减少重复请求,增量层管「版本存储」,各管一段
- 调度(定时批量篇):每日轻检查 + 变化落版本,调度节奏照旧
- 备份(51CTO 归档思路):版本链的
last_seen长期不更新的关键词,就是要清理的历史数据
踩坑记录
坑 1:指纹太敏感。 第一版用完整字段做哈希,snippet 里一个标点变化就「误报变化」,天天存新版本。改成「URL 序列」指纹后,只抓真正的「谁上榜了」变化。
坑 2:只存变化,没记录「什么时候确认没变」。 版本表只有 first_seen,没有 last_seen,回溯时不知道「这个版本持续了多久」。补上 last_seen 后,信息才完整。
坑 3:没清理长期不动的版本。 有些词几个月不变,版本链就一两条,但也占着行。定期把「连续 N 天未变化」的关键词标记归档。
工程清单沉淀
- 变化检测用「URL 序列指纹」,别用全字段哈希
- 每日轻检查不能省(变化检测本身要一次请求),但快照只存变化
- 版本化存储用
first_seen/last_seen,让「版本持续时长」可查询 - 和缓存、调度、备份配合,各管一段
- 定期清理长期不动的版本
增量采集的意义不在「少发请求」,而在让存储和分析只面对有意义的差异。版本化之后,你的搜索数据从「每天的重复快照」变成「一条干净的演变链」。
接口返回结构和字段(有机结果数组、URL 字段)在 SerpBase 官方文档 里都能对上,做指纹和版本化时先看它。你的采集是全量还是增量?评论区聊聊各自的存储方案。

浙公网安备 33010602011771号