采集任务从「一个脚本」到「编排的工作流」
单关键词采集是一个脚本,但真实业务往往是多步骤的:搜索 → 详情 → 清洗 → 落库 → 报表,步骤之间有依赖,中间会失败,还得重试。这种「多步骤 + 依赖 + 失败」的组合,就需要编排(orchestration)。这篇记录采集任务从单脚本走向编排的实践,以及什么时候才需要编排。
什么时候需要编排
不是所有采集都需要编排。判断信号:
- 有依赖链:B 步骤依赖 A 步骤的结果(比如地图详情依赖搜索返回的 feature_id)
- 步骤会失败:中途失败要重试,且失败要在正确的步骤重试
- 多步骤跨时间:部分步骤是异步/定时,要等上游
- 要可视化进度:想知道「跑到第几步了、哪步失败」
单脚本顺序跑 + try/except 能撑到某个复杂度,越过就乱。编排的价值是把「步骤、依赖、失败、进度」显式化。
编排的三种形态(从简到繁)
形态一:脚本内步骤函数(起步)
def pipeline(keyword, feature_id=None):
# 步骤 1:搜索
search_data = fetch_search(keyword)
# 步骤 2:详情(依赖搜索)
if feature_id:
detail_data = fetch_detail(feature_id)
# 步骤 3:清洗
records = clean(search_data, detail_data)
# 步骤 4:落库
store(records)
return len(records)
简单,但失败处理、重试、进度都要自己写。适合步骤少、够用的阶段。
形态二:步骤表 + 状态机(团队/生产)
把「步骤」显式化,每步有状态,失败可定位、可重试:
STEPS = ["search", "detail", "clean", "store"]
class PipelineTask:
def __init__(self, keyword):
self.steps = {s: "pending" for s in STEPS}
self.keyword = keyword
def run_step(self, step, context):
if step == "search":
context["search"] = fetch_search(self.keyword)
elif step == "detail":
context["detail"] = fetch_detail(context["search"][0]["feature_id"])
# ...
self.steps[step] = "done"
def run(self):
context = {}
for step in STEPS:
try:
self.run_step(step, context)
except Exception as e:
self.steps[step] = "failed"
raise RetryFromStep(step, context) # 从失败步骤重试
优点:失败定位到具体步骤 + 从失败步骤续跑,不用整条重跑(幂等篇的状态机思路,用在步骤级)。
形态三:现成编排工具(DAG)
步骤多、依赖复杂时,用现成工具(Airflow、Prefect、Dagster)或轻量的任务队列。适合「步骤非常多 + 要调度 + 要可视化的团队」。
建议:独立开发/小团队,形态一或二就够;形态三是「步骤真正复杂到脚本撑不住」时才上。
编排要解决的核心问题
1. 失败从正确步骤重试
单脚本失败重跑 = 从头跑(浪费前面步骤)。编排 = 从失败步骤续跑(省时省费)。地图采集尤其明显:搜索已经成功了,详情失败,重跑不该重新搜索。
# 伪代码:续跑
def resume(task):
for step in task.steps:
if task.steps[step] != "done":
task.run_step(step, task.context)
2. 步骤产物持久化
步骤结果存在 context(内存)会丢;跨时间编排要把中间产物存下来(搜索的 feature_id 落库,详情失败时不用重搜)。
3. 依赖与数据流
显式声明「详情依赖搜索的 feature_id」,编排层按依赖顺序跑。别在代码里靠「碰巧的顺序」。
踩坑记录
坑 1:失败重跑整条。 详情失败重跑整条 pipeline,搜索步骤白白重发(浪费 credits)。改成「从失败步骤续跑」。
坑 2:中间产物不持久。 搜索的 feature_id 只在内存,进程重启就丢,得重搜。中间产物落库。
坑 3:步骤顺序靠代码顺序。 加步骤时改错顺序,依赖乱。显式声明依赖。
坑 4:没记录步骤状态。 出问题不知道「跑到哪步挂的」。步骤状态表 + 日志(带 request_id)。
工程清单沉淀
- 步骤显式化:每步有状态(pending/done/failed)
- 失败从失败步骤续跑,别整条重跑
- 中间产物持久化(如 feature_id 落库)
- 依赖显式声明
- 步骤状态 + 日志可定位进度
- 复杂度没到,别上重编排工具
采集编排的本质,是把「多步骤 + 依赖 + 失败」从「纠缠在代码里」变成「显式可管理」。步骤状态化 + 失败续跑 + 产物持久,这三件做好了,多步骤采集就是「可控的工作流」而不是「一个长脚本」。
接口的分步特性(地图搜索→详情的两段式依赖)在 SerpBase 官方文档 里就是典型例子,做编排时对照。你的采集任务用到编排了吗?评论区聊聊从脚本到工作流的经历。

浙公网安备 33010602011771号