一个抓取网页的 Zap 通常第一次就能跑通,然后开始失败。这个套路很熟悉:一个 Webhooks by Zapier 动作调用抓取接口,等待 HTML,再把它交给一行 Google Sheets。它在编辑器里对着一个响应很快的测试页面顺利通过。接着它遇到真实目标:页面在 JavaScript 之后才渲染,或者挡着一道验证,或者干脆响应很慢,于是动作耗尽了时间。

第一反应是怪请求。请求没有问题。问题在于一个 Zapier 动作同时撑着两件生命周期截然不同的事:一个以秒计的工作流步骤,以及一次抓取,而只要目标愿意,它可以拖得久得多。

办法是不再让其中一个等另一个。Zapier 派发抓取任务后即结束。Crawlbase 执行抓取,有结果时再回报。本文就用两个 Zap 搭出这条流水线:不写代码,并为出错的运行准备一条恢复路径。

要点速览
  • 是两个 Zap,不是一个。 调度器 提交抓取任务后结束。 接收器 稍后接住完成的页面。
  • async=true 返回一个请求 ID(rid)而不是页面,因此派发动作在一次 API 确认的时间内就完成了。
  • 这个 webhook 参数叫 callback,不是 callback_url。它的值是 Zapier Catch Hook生成的 Custom Webhook URL。
  • 先搭接收器。它会生成调度器必须发送的那个 URL。
  • 加上 store=true ,这样回调丢失时还能凭 rid 取回结果,而不是让你为同一次抓取付两次费。
  • 先按 cb_status 过滤,再往下游写入任何东西。抓取是否成功,和页面是否存在,是两个不同的问题。

同步抓取为什么会失效

Zapier 动作有一个有限的执行窗口。对一个自动化平台来说这是合理设计:步骤本就应该很短,而一个每天运行数百万步骤的平台,不能让任何单个步骤无限期占着位置。

网页抓取并不迁就这个预期。加载一个页面要多久,是由目标决定的,不是由你决定的。一个商品页凌晨两点可能 400 毫秒就返回,大促期间却要二十秒。一个重度依赖 JavaScript 的列表页,必须先渲染才谈得上有内容可返回。一个负载吃紧的站点,或者决定发出验证的站点,能把一次抓取拉长到远超工作流步骤所预留的时间。

结果是一个与其说坏掉、不如说不可预测的工作流。它在快页面上成功,在慢页面上超时,也就是说,它恰恰在那些值得自动化的目标上失败。

一个动作撑着两段生命周期,对比两个动作各撑一段。 同步模式下,Zap 步骤在整个抓取期间保持打开,于是目标最慢的那次响应决定了工作流能否活下来。异步模式下,步骤在收到确认时就结束,结果什么时候到,第二个 Zap 就什么时候接住。

重试没有用,因为重试只是把同样的耦合对着同样的目标再来一遍。把 URL 列表拆成更小的批次也没有用,因为失败发生在单个请求上,而不是总量上。必须改变的是工作流的形状:从 请求、等待、处理 变成 派发、抓取、回调、处理

异步架构

Crawlbase Crawling API 直接支持这种拆分。在默认的同步模式下,HTTP 请求会一直保持打开,直到页面就绪,正文随同一个响应返回。加上 async=true 之后,API 改为接下任务,立即返回一个 请求 ID (rid),并在后台执行抓取。再加上 callback=<webhook URL> ,就是告诉它把完成的页面 POST 到哪里。

模式 API 返回什么 这对 Zap 意味着什么
同步 (默认) 抓取完成后返回抓到的页面 动作在整个抓取期间保持打开
异步 (async=true) 一个 rid,立即返回 动作结束,不等待页面
回调 (callback=<url>) 当下不额外返回什么;结果稍后被 POST 过来 页面就绪时由第二个 Zap 接收

这里真正要紧的区别,是 请求已受理抓取已完成。调度器永远只知道前者。页面本身会到达另一个 Zap,时间由不得任何人安排,而这正是流水线不再受目标快慢影响的原因。

有两个细节容易弄错。参数是 callback,不是 callback_url。另外,凡是打算投入生产的用法,都要搭配 store=true,它会把响应保存到 Crawlbase Cloud Storage ,并挂在同一个 rid下。就这一个开关,决定了一次丢失的回调是让你多查一次,还是让你多抓一次。

两个 Zap 的流水线。 Zap A 从触发器取到一个 URL,带上 async, callbackstore提交,收到一个 rid后结束。Crawlbase 按自己的节奏抓取,并把完成的页面 POST 到 Zap B 的 Catch Hook,由它校验并分发到 Sheets、Slack 或 CRM。两个 Zap 从不互相等待。

如果你的目标又快又直接,这些其实都用不上。 Crawlbase 原生 Zapier 集成 提供 Crawl URL, Scrape Structured DataTake Screenshot 这几个普通的 Zap 动作,完全不需要接 webhook。当真正拖垮你工作流的是抓取耗时,再考虑回调这套做法。

先搭接收器,再搭调度器

这两个 Zap 有一个搭建顺序上的依赖,很多人在这里栽跟头。接收器的 Catch Hook 会生成调度器必须作为 callback传出去的那个 URL,所以接收器必须先存在。先搭调度器,你手上就只有一个抓取请求,却没有地方接收结果。

开始之前,你需要:

  1. 一个 Crawlbase 账号。 注册 后从控制台复制你的 Crawling API token。Normal token 覆盖大多数页面;需要渲染的目标用 JavaScript token。
  2. 一个 Zapier 账号 ,且套餐支持多步骤 Zap,因为这里两个 Zap 都是多步骤的。
  3. 一个数据去向 :一份 Google 表格、一个 Slack 频道、一个 Airtable 库或一个 CRM。
  4. 一个用于测试的目标 URL。 用你要自动化的那个工作流里的真实页面,而不是占位地址,这样你看到的耗时才是你实际会遇到的耗时。

Zap B:接收器

接收器是回调的接收端。它从不发起抓取。它等着 Crawlbase 把完成的页面 POST 过来,然后校验并分发。

第 1 步:创建 Catch Hook

在 Zapier 中新建一个 Zap,触发器选 Webhooks by Zapier ,事件选 Catch Hook。Zapier 会生成一个 Custom Webhook URL ,形如:

text
https://hooks.zapier.com/hooks/catch/1234567/abcdef/

把它复制下来。这就是调度器里 Crawlbase callback 参数的值,也是连接流水线两半的唯一一根线。

第 2 步:给它一份样例载荷

Zapier 必须先见过一次请求,才能把回调字段拿出来供你映射。你可以用它自带的测试工具,也可以自己 POST 一份样例。真实的 Crawlbase 回调会带上抓到的内容,以及包括 ridurloriginal_statuscb_status

对着真实回调做映射,而不是对着自己编的样例,值得多花那一分钟。手写测试载荷里的字段名,往往和实际到达的对不上。

第 3 步:加上处理动作

字段可用之后,把需要校验、转换和保存结果的动作都加上。 Formatter by Zapier 不用写代码就能做修剪、取子串和拆分。一个 Google Sheets: Create Spreadsheet Row 动作可以这样映射:

表格列 回调字段
来源 URL 抓取的 url
状态 cb_status
请求 ID rid
内容 响应正文,或从中提取的某个字段

Google Sheets 最便于核对,但去向可以随意替换:Slack、Salesforce、Airtable、Notion,或任何 Zapier 能连的服务。这些动作都不会在调度器还开着的时候运行,因为回调到达时,调度器早就结束了。

第 4 步:打开接收器

在发出第一个真实的异步请求之前,先把接收器发布上线。属于一个已关闭 Zap 的 Catch Hook,不会处理送到它那里的内容;而抵达一个停用端点的回调,就是一次你付了钱又扔掉的抓取。先打开接收器,是整个搭建过程中最便宜的一个习惯。

Zap A:调度器

调度器从既有工作流中取一个 URL,提交去做异步抓取,记录 rid,然后结束。它的工作就这些。

第 1 步:选择触发器

触发器就是你业务里产生 URL 的那个东西:

  • Schedule by Zapier ,用于周期性的价格或库存检查
  • Google Sheets: New Spreadsheet Row ,URL 一加进表格就去抓
  • Google Forms: New Response ,有人提交 URL 就去抓

映射那个存放完整 URL 的字段,包含协议头。

第 2 步:加上 Crawlbase 请求

添加 Webhooks by Zapier 作为动作,事件选 GET,并把请求 URL 设为 https://api.crawlbase.com/。GET 与 Crawling API 文档中参数的用法一致。POST 也可以,只要参数能完整送到接口。

第 3 步:配置参数

Query String Params下添加五项:

作用
token 你的 Crawlbase token 对请求做鉴权
url 来自触发器的 URL 要抓取的页面
async true 在后台执行抓取,并返回一个 rid
callback 来自 Zap B 的 Catch Hook URL Crawlbase 把完成的页面 POST 到哪里
store true 把响应保存到 Cloud Storage,并挂在这个 rid

有两点要留意。 url 的值必须做 URL 编码,尤其是在目标 URL 自带查询串的时候。另外,token 只属于 Zap 配置,不属于别处:别让它出现在截图、共享的 Zap 模板和支持工单里;万一外泄,就轮换掉。

对于需要渲染的目标,改用 JavaScript token,并加上渲染参数,例如 page_wait,具体见 Crawling API。也不妨加上 format=json :它会把状态、URL 和正文放进一个 JSON 外壳返回,通常能让接收器里的字段映射更简单。

如果你想先在 Zapier 之外确认这套配置,等价的请求是:

bash
curl -G 'https://api.crawlbase.com/' \
  --data-urlencode 'token=YOUR_CRAWLBASE_TOKEN' \
  --data-urlencode 'url=https://example.com/product/123' \
  --data-urlencode 'async=true' \
  --data-urlencode 'callback=https://hooks.zapier.com/hooks/catch/1234567/abcdef/' \
  --data-urlencode 'store=true'

手动跑一次,是在把两边接起来之前区分 Crawlbase 问题和 Zapier 问题的好办法。

第 4 步:记录请求 ID

再加一个动作,把 rid 连同输入 URL 和派发时间戳一起写到某个持久的地方。一行表格就够了。

这不是为记账而记账。 rid 是抓取存在的三个地方之间的关联键:派发、随后的回调,以及保存下来的副本。正是靠它,你才能回答某个 URL 是否已经抓过、某个回调是否已经处理过,以及缺失的结果去了哪里。

第 5 步:打开调度器

发布它,然后忍住别再加一步。任何试图从调度器响应里读取页面正文的动作,都会把你刚刚去掉的等待重新引回来,超时也跟着回来。调度器的响应里装的是一个 rid。页面属于接收器。

读懂回调:cb_status 与 original_status

抓取完成后,Crawlbase 会把结果 POST 到 Catch Hook。在一次真实抓取之后使用 Test trigger ,或者打开一次近期的 Zap 运行记录,按实际到达的内容来做映射。要紧的字段有:

  • 抓到的内容,为 HTML;若你请求了 scraper,则为已解析的 JSON
  • cb_status,Crawlbase 对这次抓取的判定。 200 表示成功。它旧称 pc_status
  • rid,把这次回调与当初的派发对应起来
  • original_status,目标站点返回的 HTTP 状态
  • url,被抓取的页面

这两个状态字段回答的是不同的问题,把它们混为一谈,是这类流水线里最常见的脏数据来源。 original_status 描述站点说了什么。 cb_status 描述 Crawlbase 是否成功拿到了回答。

两个互相独立的判定,四种结果。 目标返回 404,是对一个并不存在的页面的一次成功抓取。验证页可能返回 200,而抓取本身却失败了。按 cb_status 分支,能把被拦截的响应挡在你的表格之外;只按 original_status 分支则做不到。

所以在去向动作之前放一个 Filter by Zapier 步骤,只有当 cb_status 等于 200时才继续。这样下游看到的,就都是真正抓到的页面。

尽量把解析和传输分开。 Formatter by Zapier 能应付普通的文本处理,而 Code by Zapier 留给确实需要定制提取的场景。对于 Crawlbase 已经能解析的站点, scraper 参数会直接返回结构化 JSON,把解析这一步整个省掉。

先把整条链路跑通一次

在放量之前,让一个 URL 完整走一遍:

  1. 确认接收器处于 On ,且它的 Catch Hook URL 正是调度器作为 callback发出的那个。
  2. 把调度器切到 On
  3. 触发一次:加一行测试数据、执行一次计划任务,或提交一次表单。
  4. 查看调度器的运行历史。Webhooks 动作应当很快结束,返回的是一个 rid,而不是一个页面。
  5. 给抓取留出完成的时间。
  6. 在接收器的运行历史里找那次回调触发的运行。
  7. 到去向那边核对那一行、那条消息或那条记录。

如果调度器成功了而接收器从未运行,常见原因有三个: callback 的 URL 写错或有拼写错误;Crawlbase 尝试投递时接收器是关着的;或者某个 Filter 步骤在运行到达去向动作之前就把它丢掉了。按这个顺序去查。

用 Cloud Storage 作为恢复路径

回调是正常的投递路径。生产系统还需要为“正常没有发生”的时候准备好答案:接收器正在改动、去向应用宕机、某个过滤条件写错而丢掉了一个好结果。

这正是 store=true 带来的价值。响应会保存在 Crawlbase Cloud Storage ,挂在 rid下,于是一次失败的回调变成了一次取回,而不是一次重抓。偶尔需要恢复时,用 rid存储控制台里把这次抓取找出来。如果是反复要做的事,就再搭一个小 Zap:

步骤 应用与动作 配置
1 手动触发,或 Google Sheets: New Row 提供 rid 以便恢复
2 Webhooks by Zapier: GET https://api.crawlbase.com/storage
3 查询串参数 tokenrid
4 你的去向动作 写入返回的正文,或继续后续处理

存储也可以按 url 查询,而不按 rid,那会返回该页面最近一次保存的版本。有一条限制要在设计时就考虑进去:保存的页面默认保留 14 天 。Cloud Storage 是恢复缓冲,不是你的归档,所以凡是需要长期保留的内容,都应由接收器复制进你自己的系统。

Crawlbase Crawling API

轮换住宅 IP、真实浏览器渲染,以及在一次抓取内完成的验证处理;当你不愿意让连接一直开着时,还有异步投递和 Cloud Storage。失败的请求不计费。免费开始,含最多 5,000 次请求,无需绑卡。

投入生产前要考虑的事

上面这条流水线是一个能跑的概念验证。再补上几点,它就成了可以放着长期运行的东西。

让接收器具备幂等性。rid 当作唯一键,写入前先检查是否已经处理过。投递重试、手工重放的运行,或者运行中途被改动的 Zap,都可能让同一个回调走两遍,而重复的一行,预防起来远比事后清理容易。

先校验,再写入。cb_status过滤,其余一律当作异常,并给它一个去处:单独的表格、Slack 告警,或一个重试清单。自动化里静默的失败比响亮的失败更糟,因为在数据已经错掉之前没人会去看。

让调度器保持不阻塞。 它只负责提交和记录。每当有人加上“就一小步”去读正文,超时就回来了。

保护好 token。 它待在 Zap 配置里,不在截图或共享模板里。一旦外泄就轮换。

用够用的最便宜的 token。 Normal token 比 JavaScript token 更快也更便宜。只有当 Normal 的响应回来是空的或被验证拦截时,才把某个目标升级到 JS token,而不是先备着。

尊重目标站点。 查看使用条款、robots 指令和速率限制,并守住它们。自动化容易搭建,并不改变你被允许采集的范围。

这套模式适用在哪里

“派发加回调”这个形状并不是抓取独有的。凡是工作流必须启动一件慢事、又不能守着等它的场合,你都会用到它。

场景 调度器触发 接收器动作
价格监控 计划任务,或新增一行 SKU 把价格和时间戳追加到表格
线索信息补全 CRM 中新增带公司 URL 的线索 把补全后的摘要发到 Slack
房源提醒 带房源 URL 的表单或表格 更新 Airtable 或 Salesforce
竞品简报 计划中的竞品 URL 列表 生成一封邮件简报

业务逻辑会变,执行模型不会。一个业务事件启动一次异步抓取,一次回调送回结果,一个下游动作拿它做点什么。如果比起 Zapier,你更想用代码掌握接收端,同样的架构在 Python 中的写法见 搭建 Flask 回调服务器

结语

同步的无代码抓取,问题从来不在 HTTP 请求本身,而在于把一个工作流步骤的生命周期,绑在了一次网页抓取的生命周期上,而这两件事没有任何理由在“该花多久”上达成一致。

把它们分开,依赖就消失了。调度器发出 async=true 和一个 callback URL,拿到一个 rid,在一次确认的时间内结束。Crawlbase 按自己的节奏抓取,并把页面 POST 给接收器,由它校验并继续分发。再加上 store=true ,那些仍然出错的运行也能凭 rid 取回,而不是丢失。

最终得到的,是一条可靠性不再取决于最慢的目标今天想跑多快的流水线。

常见问题(FAQ)

不写代码也能把 Crawlbase 和 Zapier 用起来吗?

可以。本文这条流水线用 Webhooks by Zapier 发送请求,用 Catch Hook 接收结果,再用 Formatter 和 Filter 步骤做处理。不需要 Python,也不需要后端。如果你的目标够快、根本用不上异步,Crawlbase 原生的 Zapier 应用直接提供 Crawl URL、Scrape Structured Data 和 Take Screenshot 这些现成动作。

为什么在 Zapier 里要用异步抓取,而不是普通请求?

因为 Zapier 动作有一个有限的执行窗口,而网页抓取没有。加上 async=true 之后,只要 Crawlbase 接下任务并返回一个 rid,派发动作就结束了,于是一个缓慢或渲染沉重的页面,不再决定这个 Zap 能否活下来。完成的页面会单独送到接收器。

cb_status 和 original_status 有什么区别?

cb_status 是 Crawlbase 对这次抓取的判定,其中 200 表示成功。它旧称 pc_statusoriginal_status 则是目标站点返回的 HTTP 状态。两者相互独立:一个站点可以回 404 ,同时这次抓取仍然是成功的;一个验证页可以回 200 ,而抓取本身失败了。请按 cb_status给工作流分支。

如果 Zapier 回调失败了会怎样?

如果请求里带了 store=true,响应已经按它的 rid保存在 Crawlbase Cloud Storage 里,因此你可以从存储控制台,或者通过一个小的恢复 Zap 把它取回,而不必重新抓取页面。这正是每次派发时都要记下 rid 的原因。保存的页面默认保留 14 天。

这条流水线可以用在任何网站上吗?

可以。异步模式在任何域名上都能用,所以这条流水线并不局限于某一类目标。不同的是每个目标各自需要什么:在客户端渲染的页面需要 JavaScript token,有时还需要 page_wait;另外有些站点就是比别的慢,而这正是这套架构要吸收的问题。在把一个计划任务 Zap 指向某个站点之前,请先查看该站点的使用条款、robots 指令和速率限制。

开始构建

大规模爬取任何站点,无需与基础设施对抗。

Crawlbase 负责处理代理、指纹和 CAPTCHA,让你的团队专注于交付数据流水线,而非维护爬取管道。1,000 次请求免费,无需信用卡。

自助开通 · 无需销售通话 · 提供企业级爬取量