爬取几百个页面时,同步循环完全够用:发送请求,等待 HTML,解析,重复。但一旦你需要数千甚至数百万个页面,这种模式就会崩溃,因为每个请求都会在远程浏览器渲染页面、代理完成重试的过程中阻塞你的代码。Crawlbase Crawler 的存在就是为了消除这种等待。你向它推送 URL,它在后台将其排队并爬取,并在完成后将每个结果交付到你控制的 webhook。
本教程展示如何在 Python 中端到端地使用 Crawler:搭建接收结果的 webhook 端点,创建指向该端点的 Crawler,通过 Crawling API 推送一批 URL,并在结果到达时读取爬取的 HTML。过程中你将了解使 Crawler 适用于规模化场景的特性:异步处理、普通请求与 JavaScript 请求的区别、仅为成功请求付费,以及自动重试机制。示例使用中性的公开页面,结尾附有关于负责任抓取的简短说明。
你将构建什么
一个小而完整的异步管道。一端是接受 Crawler POST 回调并存储每个结果的 webhook 服务器。另一端是通过 Crawling API 向你命名的 Crawler 提交 URL 的推送脚本。你最终将得到以下几个部分:
- Webhook 接收器。一个接受 Crawler POST 回调、解压 gzip 响应体并将爬取的 HTML 保存到磁盘的 Flask 端点。
- Crawler 定义。在你的控制台中创建的命名 Crawler,可以是用于静态页面的普通(TCP)Crawler,也可以是用于客户端渲染页面的 JavaScript Crawler,并指向你的 webhook URL。
-
推送脚本。一个将 URL 列表通过
callback=true和crawler=YourCrawlerName发送到 Crawler 的 Python 脚本,并打印每条请求返回的请求 id(RID)。 - 结果处理。每次回调到达时携带的字段,包括 RID、原始 URL、状态码和页面体。
异步 Crawler 与直接请求的区别
直接的 Crawling API 调用是同步的:你调用 api.get(url),代码阻塞直到页面被获取和渲染,然后在同一个响应中拿到 HTML。这是处理少量页面或需要立即拿到结果的交互式工作的正确工具。
Crawler 颠覆了这一流程。你推送一个 URL,立即获得一个简短的请求 id,继续推送批次中的其余 URL。Crawler 在后台爬取每个页面,按自己的节奏处理渲染、IP 轮换和重试,并在完成后将结果 POST 到你的 webhook。你的代码从不需要为一次爬取的整个持续时间保持连接打开。这种解耦使单个批次能够跨越数千个 URL,而不需要你的进程在等待中闲置,这正是异步产品页面推荐它用于大型任务的原因。权衡之处在于:你需要一个公开可访问的端点来接收回调,而这正是你首先需要构建的。
创建 Crawler 时需要选择类型。普通(TCP)Crawler 获取静态 HTML,对服务端渲染的页面来说是更经济的选项。JavaScript Crawler 先在真实浏览器中渲染页面,当内容是客户端构建的(React、Angular,或任何加载后才填入内容的页面)时需要使用。选择与你目标匹配的类型;JavaScript 请求消耗的积分多于普通请求。
前置条件
需要准备好以下几项,每项都不需要花太长时间。
基础 Python 知识。你应该能够运行脚本并用 pip 安装依赖包。如果你刚开始处理收到的 HTML,BeautifulSoup 指南与本教程配合得很好。
Python 3.8 或更高版本。用 python --version 确认。如果没有,请从 python.org 安装并确保其在 PATH 中。
Crawlbase 账号和 token。注册后,打开控制台,从账号文档页面复制你的 token。你会获得一个普通 token 和一个 JavaScript token;使用与你创建的 Crawler 类型匹配的那个。Crawlbase 注册即赠 1,000 次免费请求,足够完成本指南。请将 token 视为密码,不要提交到版本控制中。
暴露本地 localhost 的方式。Crawler 通过公共互联网交付结果,因此你的 webhook 必须能从你的机器外部访问。在本地开发中,ngrok 等隧道工具可以将公共 URL 转发到你本地的端口。在生产环境中,你需要将端点托管在真实服务器上。
项目设置
创建虚拟环境使依赖保持隔离,然后安装你需要的两个库:用于 webhook 服务器的 Flask,以及用于推送脚本的官方 Crawlbase 客户端。
python --version python -m venv crawler_env source crawler_env/bin/activate pip install crawlbase flask
在 Windows 上,使用 crawler_env\Scripts\activate 替代 source 命令来激活环境。crawlbase 包是通过 Crawling API 推送 URL 的官方客户端,flask 为回调端点提供一个最简 Web 服务器。用于解压回调响应体的 gzip 模块随标准库附带,无需额外安装。
第 1 步:构建 webhook 接收器
Crawler 将每个结果以 POST 请求的形式发送到你的回调 URL。作为有效端点,你的 webhook 需要做三件事:能从公共互联网访问,接受 POST 请求,并快速以 200、201 或 204 状态且无响应体作出响应。Crawler 以 gzip 压缩的形式发送页面体,因此你需要在保存前先解压。以下是一个完整的 Flask 接收器。
# webhook.py import gzip from flask import Flask, request, Response app = Flask(__name__) @app.route("/webhook/crawlbase", methods=["POST"]) def webhook(): rid = request.headers.get("rid") url = request.headers.get("url") pc_status = request.headers.get("pc_status") try: body = gzip.decompress(request.data).decode("latin1") except OSError: body = request.data.decode("latin1", errors="replace") with open(f"result_{rid}.html", "w", encoding="latin1") as f: f.write(body) print(f"Received {rid} for {url} (status {pc_status})") return Response(status=204) if __name__ == "__main__": app.run(port=8000)
端点从请求头中读取 RID、爬取的 URL 和 Crawlbase 状态,将 gzip 响应体解压为 HTML,并将其写入以 RID 命名的文件,使每个结果单独落地。它返回 204 No Content,正是 Crawler 期望的响应:快速、空的确认。用 python webhook.py 运行,服务器在 8000 端口监听。
现在将其暴露到公网。服务器运行后,在同一端口启动隧道:
ngrok http 8000
ngrok 会打印一个公共转发 URL,例如 https://abc123.ngrok-free.app。你的完整回调 URL 是该主机加上路由,例如 https://abc123.ngrok-free.app/webhook/crawlbase。记下这个地址,下一步会用到。使用 ngrok 免费套餐时,每次重启 URL 都会变化,所以重启后需要重新读取。
你刚构建的 webhook 只需要接收完整的 HTML,因为难的部分发生在上游:当 Crawler 爬取每个推送的 URL 时,Crawling API 在需要时渲染页面,并在服务端轮换住宅 IP,因此你无需自行运维无头浏览器集群或代理池。将 Crawler 指向这个端点,在免费套餐上推送你的第一批 URL。
第 2 步:创建 Crawler
有了公开回调 URL 后,从控制台创建 Crawler。打开 Crawler 部分并选择创建新 Crawler。你需要提供三项信息:
-
名称。推送 URL 时用于引用的唯一标识符,例如
test-crawler。 - 类型。静态页面用普通(TCP)Crawler,客户端渲染页面用 JavaScript Crawler,如上所述。
-
回调 URL。第 1 步中的公开 webhook URL,包含路由:
https://abc123.ngrok-free.app/webhook/crawlbase。
如果你不想运行自己的端点,Crawlbase Cloud Storage 也可以作为回调目标,将结果保存供你稍后获取。本教程使用你构建的 webhook,因为它展示了完整的回调流程。Crawler 保存后,即可接受推送的 URL。
第 3 步:向 Crawler 推送 URL
推送是 Crawling API 发挥作用的地方。你以与同步爬取相同的方式调用它,但添加两个选项:callback=true 告诉它这是异步请求,crawler=test-crawler 指定应处理该请求的 Crawler。每次推送立即返回一个请求 id,而非页面本身。以下是推送脚本。
# push.py from crawlbase import CrawlingAPI api = CrawlingAPI({"token": "YOUR_CRAWLBASE_TOKEN"}) urls = [ "https://httpbin.org/html", "https://example.com/", "https://books.toscrape.com/", ] OPTIONS = {"callback": "true", "crawler": "test-crawler"} for url in urls: response = api.get(url, OPTIONS) print(response["body"])
每次调用立即返回一个包含 RID 的小型 JSON 响应体,Crawler 将该 URL 加入后台爬取队列。默认情况下每秒最多可推送 30 个 URL;如果需要更多,可联系 Crawlbase 支持提高限制。注意普通 token 与普通 Crawler 配对,JavaScript token 与 JavaScript Crawler 配对,因此请使用与你创建的类型匹配的 token。
推送返回的内容
运行 python push.py,按推送顺序获得每个 URL 对应的一个 RID:
{"rid": "d756c32b0999b1c0507e364f"} {"rid": "455ee207f6907fbd6168ac1e"} {"rid": "e9eb6ce579dec207e8973615"}
RID 是每个请求的句柄。你可以在之后通过 Crawler 的管理端点查找请求,并且它会在回调中返回,让你能将每个结果与推送的 URL 对应起来。由于推送是异步的,整个批次在不到一秒内返回;实际爬取在之后于后台进行。
第 4 步:在 webhook 接收结果
一旦页面被爬取,Crawler 就会将结果 POST 到你的回调 URL。响应体是 gzip 压缩的页面,元数据通过请求头传输。默认响应是 HTML,请求头格式如下:
Content-Type: text/plain Content-Encoding: gzip Original-Status: 200 PC-Status: 200 rid: the RID you received in the push call url: the URL which was crawled Body: the gzip-compressed HTML of the page
Original-Status 是目标网站返回的状态码,PC-Status 是 Crawlbase 对本次爬取的状态码,你可以通过它区分成功获取和失败,并做出相应处理。如果你更希望获得结构化输出而非原始 HTML,在推送时传入 format=json,响应体则以 JSON 对象的形式到达:
{ "pc_status": 200, "original_status": 200, "rid": "the RID you received in the push call", "url": "the URL which was crawled", "body": "the HTML of the page" }
由于我们推送了三个 URL,webhook 会收到三次 POST,每次各自写入一个 result_<rid>.html 文件。HTML 落盘后,你可以用任何方式解析它;这正是你将其加载到 BeautifulSoup 并提取所需字段的时候,与同步爬取后的操作完全相同。
如果你需要将自己的标识符传递到回调中,推送时传入 callback_headers 参数,格式为 NAME:VALUE|NAME2:VALUE2(需进行 URL 编码)。Crawler 会在结果中将这些请求头原样返回,让你无需额外查询就能从回调中读取附加的任务 id 或记录键。
规模化时重要的关键特性
以上四步是完整的模式。使其能够在数百万个 URL 上保持可靠的,是 Crawler 几个值得特别说明的行为特性。
- 异步处理。推送立即返回 RID,爬取在后台运行,因此你的代码永远不会因等待慢速页面而阻塞。单个进程可以推送非常大的批次,然后只需等待结果陆续到达。
- 普通请求与 JavaScript 请求。普通 Crawler 以低成本获取静态 HTML;JavaScript Crawler 在浏览器中渲染页面以处理客户端内容。你按 Crawler 选择类型,JavaScript 请求消耗的积分多于普通请求,因此只在需要时为渲染付费。
- 按成功请求付费。仅对成功的请求计费,而非每次尝试,这使大规模爬取的成本与结果挂钩,而非与付出的努力挂钩。
- 自动重试。如果 Crawler 向你的 webhook 交付结果但服务器未返回成功状态,它会重试爬取并重新交付。这些重试一旦成功落地就会计为成功请求,因此保持端点快速响应并返回 204 可以避免为不必要的重试付费。
一个运维细节:如果你的 webhook 下线,Crawlbase 的监控会检测到并暂停 Crawler,待端点可访问后恢复。你所有 Crawler 的合并等待队列有上限;如果达到上限,推送会暂停并发送邮件通知,待队列消化后恢复。你很少需要直接处理这些,但这正是长时间运行的异步任务不会悄无声息地丢失结果的原因。
扩展管道
生产运行中整体形态保持不变,你改变的是输入和接收端。以下几个习惯能让大型任务保持健康:
- 批量推送。从文件或队列中读取 URL 并在循环中推送,在默认每秒 30 个的限制内进行,除非你申请了更高的限额。存储每个返回的 RID,方便在结果到达时进行核对。
- 让 webhook 保持稳健。用 204 快速确认,然后在带外处理响应体(写入存储或交给工作队列),而非在请求内部解析。慢速 webhook 会触发你需要付费的重试。
-
监控状态码。追踪每次回调上的
PC-Status和Original-Status,以便区分真正的页面失败和暂时性失败,并将需要处理的请求重新送回队列。
如果你完全不想管理回调存储,可以将 Crawler 指向 Crawlbase Cloud Storage,按自己的节奏获取结果。关于搭建回调服务和持久化结果的完整介绍,请参阅使用 Crawlbase Crawler 提取数据以及构建可扩展网络数据管道指南。如果你的目标大量使用客户端渲染,爬取 JavaScript 网站演练对渲染部分有更深入的介绍。
负责任地抓取
Crawler 让大规模采集变得简单,这使负责任使用成为一种自律而非能力问题。只抓取公开数据,即无需账号即可访问的页面,远离任何需要登录或付费才能访问的内容。查阅每个目标网站的服务条款和 robots.txt,并将两者视为你可采集内容的边界。
将请求频率控制在合理范围内。异步模式可以推送得很猛,因此请设置不会给所爬网站带来压力的数量。当你采集的数据包含任何与可识别个人相关的内容时,将其视为个人数据,并按 GDPR 和 CCPA 等法规处理:最小化保留内容,尽可能聚合,不要建立个人档案。本指南中的中性示例 URL(httpbin、example.com 和一个沙箱书店)存在的目的正是让你在不将爬虫指向任何人的生产网站的情况下测试流程。
核心要点
- Crawler 从设计上就是异步的。你推送 URL,立即获得 RID,并在 webhook 接收完整结果,代码永远不会因慢速爬取而阻塞。
-
你需要构建两端。一个接受 POST 回调并返回 204 的公开 webhook 端点,以及一个通过 Crawling API 使用
callback=true和crawler=YourCrawlerName提交 URL 的推送脚本。 - 按目标类型选择 Crawler。静态页面用普通(TCP)Crawler,客户端渲染页面用 JavaScript Crawler;JavaScript 请求消耗更多积分,因此只在需要时为渲染付费。
- 交付失败会自动重试。如果你的 webhook 未以成功状态确认,Crawler 会重新交付,这些重试一旦成功落地就会计为成功请求,因此保持端点快速响应。
- 坚守公开数据。遵守每个网站的服务条款和 robots.txt,控制频率,并按 GDPR 和 CCPA 处理任何个人数据。
常见问题
Crawler 与普通 Crawling API 调用有何不同?
普通 Crawling API 调用是同步的:你等待响应,并在同一个请求中拿到 HTML。Crawler 是异步的:你推送 URL,立即获得请求 id,完整结果稍后被 POST 到你的 webhook。对少量页面或交互式工作使用直接调用,当你需要在不阻塞代码的情况下处理数千或数百万个 URL 时使用 Crawler。
我的 webhook 需要做什么才算是有效端点?
它必须能从公共互联网访问,接受 POST 请求,并快速以 200、201 或 204 状态且无响应体作出响应。Crawler 以 gzip 压缩的形式发送页面,因此在使用前需要解压响应体。快速确认,之后再进行较重的处理,因为慢速 webhook 可能触发重试。
我必须使用 Python 吗?
不必。Python 在这里很方便,因为有官方 Crawlbase 客户端和 Flask,但 Crawler 与语言无关。推送端是带有 callback=true 和 crawler=YourCrawlerName 的 Crawling API 调用,webhook 是任何接受 POST 请求的 HTTP 端点。两端都可以用 JavaScript、Ruby、Go 或任何能使用 HTTP 的语言构建。
RID 是什么,如何使用它?
RID(请求 id)在你推送 URL 时返回,并在回调中原样返回。它让你能将每个收到的结果与提交的 URL 对应起来,并且你可以用它通过 Crawler 的管理端点查找请求。为每次推送存储 RID 是在结果陆续到达时核对大型批次的最简方法。
何时应该使用 JavaScript Crawler 而非普通 Crawler?
当你需要的内容是客户端渲染的,例如 React 或 Angular 应用,或加载后才填入内容的页面时,使用 JavaScript Crawler。普通(TCP)Crawler 对服务端渲染的静态 HTML 就已足够,且每次请求消耗更少积分。将 Crawler 类型与目标匹配,并使用对应的 token(普通或 JavaScript)。
重试如何计费?
你仅为成功请求付费而非每次尝试,JavaScript 请求消耗的积分多于普通请求。如果 Crawler 尝试交付结果但你的 webhook 未返回成功状态,它会重试并重新交付;这些重试一旦成功落地就会计为成功请求。保持端点快速响应并返回 204 可以避免为可预防的重试付费。
大规模爬取任何站点,无需与基础设施对抗。
Crawlbase 负责处理代理、指纹和 CAPTCHA,让你的团队专注于交付数据流水线,而非维护爬取管道。1,000 次请求免费,无需信用卡。
