C# / .NET
Crawlbase 平台的官方 .NET 客户端。每个方法都有同步版本(例如 Get)和异步版本(GetAsync):同一个构件,覆盖所有 API,并具备合理的默认值。
SDK 的结构
.NET SDK 是对 API Reference 中所述 HTTP API 的轻量封装。在原始 HTTP 调用中作为查询字符串附加的每个 Crawling API 参数,都可作为 Dictionary<string, object> 选项访问:名称、默认值和行为均一一对应。
有一点需要提前了解:.NET SDK 将响应状态暴露在 API 实例本身上,而不是返回值对象上。诸如 api.Get(url) 的调用返回 void;您通过 api.StatusCode、api.Body 等读取结果。这与 Python / Node / Ruby / PHP SDK 不同(后者返回一个响应对象)。Storage API 是个例外:它的方法返回一个可直接读取的响应对象。
相比直接使用 HttpClient,使用它能获得:
- URL 编码、参数验证和响应解析开箱即用。
- 每个动词都有同步 + 异步配对,按调用场景选择即可。
- 每个 Crawlbase API 对应一个客户端类,全部共享相同的构造函数 / 调用形式。
- 合理的默认值(90 秒超时、对
format=json响应自动进行 JSON 解析)。
源代码位于 github.com/crawlbase/crawlbase-net。
安装
最新版本发布在 NuGet 上。目标框架为 .NET 6+;已测试至 .NET 9。
# .NET CLI
dotnet add package CrawlbaseAPI
# Package Manager Console
Install-Package CrawlbaseAPI
# Or in csproj:
# <PackageReference Include="CrawlbaseAPI" Version="1.1.0" />身份验证
每个 Crawlbase API 都使用相同的 token 模型进行身份验证。一个账号下有两种 token 类型:
- Normal Token (TCP) - 用于静态 HTML、JSON 端点等不需要浏览器的场景。更快 + 更便宜。
- JavaScript Token
- 用于 SPA、懒加载信息流,以及任何将内容隐藏在客户端渲染背后的场景。使用
page_wait、ajax_wait、scroll和css_click_selector时必需。
在生产环境中请使用环境变量或 DI 容器的配置。模式:
// Pick the right token at instantiation; the SDK doesn't switch
// tokens per-call, so keep two clients if you alternate.
var api = new Crawlbase.API(Environment.GetEnvironmentVariable("CRAWLBASE_TOKEN"));
var js = new Crawlbase.API(Environment.GetEnvironmentVariable("CRAWLBASE_JS_TOKEN"));
await api.GetAsync("https://github.com/anthropic");
var opts = new Dictionary<string, object> { ["page_wait"] = 2000 };
await js.GetAsync("https://feed.example.com", opts);完整的 token 模型 + 仪表盘位置请参见 Authentication 页面。
快速开始
从命名空间到爬取响应只需三行代码。注意响应状态保存在 api 实例上:
var api = new Crawlbase.API("YOUR_TOKEN");
await api.GetAsync("https://github.com/anthropic");
if (api.StatusCode == 200) {
Console.WriteLine(api.Body);
}在决定是否重试时,根据 api.StatusCode(SDK 到 Crawlbase 的 HTTP 状态)和 api.CrawlbaseStatus(Crawlbase 的判定结果:参见下方错误)进行分支判断。传入 new Dictionary<string,object> { ["format"] = "json" } 可接收 JSON 封装结果,而不是原始页面内容。
一个包,所有 API
每个 Crawlbase 产品都有对应的客户端类。相同的构造函数(单个 token 字符串),相同的 Get / GetAsync / Post / PostAsync 形式。
string token = "YOUR_TOKEN";
var crawl = new Crawlbase.API(token); // Crawling API
var scraper = new Crawlbase.ScraperAPI(token); // parsed JSON for supported sites
var leads = new Crawlbase.LeadsAPI(token); // domain-scoped email extraction (legacy)
var shots = new Crawlbase.ScreenshotsAPI(token); // body is base64-encoded image
var storage = new Crawlbase.StorageAPI(token); // Cloud Storage CRUD
// Push high-volume async jobs to the Enterprise Crawler via the Crawling API:
// api.Get(url, options) where options carries `callback=true` + `crawler=YourCrawler`.
// See /docs/crawler for the queue-management workflow.常见模式
JavaScript 渲染
对于 SPA、懒加载信息流,以及初始 HTML 为空的页面,使用 JavaScript token 进行实例化,并传入 page_wait、ajax_wait、scroll 和 css_click_selector 的任意组合。建议的顺序:先固定等待,再等待网络空闲,然后滚动以触发懒加载,最后点击任何拦截 UI 元素。
var api = new Crawlbase.API("YOUR_JS_TOKEN");
await api.GetAsync("https://spa.example.com", new Dictionary<string, object> {
["page_wait"] = 2000,
["ajax_wait"] = true,
["scroll"] = true,
});使用内置 Scraper
在受支持的网站上完全跳过解析器。传入 ["scraper"] = "NAME",body 就会变成一个 JSON 字符串,包含每个 scraper 页面所记录的结构化字段。
using System.Text.Json;
var api = new Crawlbase.ScraperAPI("YOUR_TOKEN");
await api.GetAsync(
"https://www.amazon.com/dp/1098145356",
new Dictionary<string, object> { ["scraper"] = "amazon-product-details" }
);
var data = JsonSerializer.Deserialize<JsonElement>(api.Body);
Console.WriteLine($"{data.GetProperty("name")} - {data.GetProperty("price")}");地理路由
传入 ["country"] = "ISO" 即可通过该国家的出口节点路由爬取请求。当目标网站根据 IP 提供本地化内容时使用此功能。
var api = new Crawlbase.API("YOUR_TOKEN");
// Hit the German Amazon catalog from a German residential IP
await api.GetAsync(
"https://www.amazon.com/dp/1098145356",
new Dictionary<string, object> { ["country"] = "DE" }
);带退避的重试
推荐的重试形式:指数退避,最多 3-5 次尝试,仅对临时错误(5xx 或空 body)进行重试,不要对 4xx 进行重试。
public async Task<bool> CrawlAsync(Crawlbase.API api, string url, int attempts = 5) {
var rand = new Random();
for (int i = 0; i < attempts; i++) {
try {
await api.GetAsync(url);
} catch (Exception) {
// SDK throws on transport failures - fall through to retry
}
if (api.StatusCode == 200 && api.CrawlbaseStatus == 200) {
return true;
}
if (api.StatusCode is >= 400 and < 500) {
throw new InvalidOperationException($"client error {api.StatusCode}: {url}");
}
// Exponential backoff with jitter
var ms = (int) (rand.NextDouble() * Math.Pow(2, i) * 1000);
await Task.Delay(ms);
}
return false;
}异步爬取 + Webhook
发后即忘模式。传入 ["async"] = true 和 ["callback"] URL;调用会立即返回,当页面就绪时 Crawlbase 会将结果 POST 到您的 webhook。适用于批处理任务和较慢的目标。
var api = new Crawlbase.API("YOUR_TOKEN");
await api.GetAsync("https://example.com", new Dictionary<string, object> {
["async"] = true,
["callback"] = "https://your-app.com/webhook",
});
// api.Body is a JSON envelope { rid: ... } - use that to correlate
// the eventual webhook delivery.
//
// Your ASP.NET / Minimal API endpoint receives a POST with:
// { rid, url, original_status, pc_status, body }对于超大批量(数百万个 URL),请使用 Enterprise Crawler,它位于这一相同异步管道的前端。
粘性会话
某些流程需要在多次调用之间使用同一个住宅 IP。传入 cookies_session 和一个稳定的标识符,Crawlbase 会在约 30 分钟内复用同一个出口节点。
var api = new Crawlbase.API("YOUR_JS_TOKEN");
var session = $"checkout-{userId}";
var opts = new Dictionary<string, object> { ["cookies_session"] = session };
await api.GetAsync("https://shop.example.com/cart", opts);
await api.GetAsync("https://shop.example.com/checkout", opts);
await api.GetAsync("https://shop.example.com/confirm", opts);Cloud Storage CRUD
Storage API 是「响应挂在 api 实例上」这一模式的例外:它的方法返回一个可直接读取的响应对象。读取先前 Crawling API 调用(store=true)保存的结果时很有用。
var storage = new Crawlbase.StorageAPI("YOUR_TOKEN");
// Fetch by URL
var response = storage.GetByUrl("https://www.apple.com");
Console.WriteLine(response.OriginalStatus);
Console.WriteLine(response.CrawlbaseStatus);
Console.WriteLine(response.URL);
Console.WriteLine(response.RID);
Console.WriteLine(response.StoredAt);
// Or fetch by RID, delete, bulk-fetch, list RIDs, total count
var item = storage.GetByRID(rid);
bool deleted = storage.Delete(rid);
var items = storage.Bulk(new List<string> { rid1, rid2 });
var rids = storage.RIDs(100); // optional limit
var total = storage.TotalCount();错误 & 重试
平台在每个响应上都返回两个状态码:SDK 自身的 api.StatusCode(请求 Crawlbase 本身的 HTTP 状态)和 api.CrawlbaseStatus(Crawlbase 对目标的判定结果:完整列表参见 Crawling API 错误表)。决定是否重试时,请始终根据 api.CrawlbaseStatus 进行分支:目标可能返回 200 但响应正文为空,这种情况下 StatusCode 为 200,而 CrawlbaseStatus 为 520。
try {
await api.GetAsync(url);
} catch (Exception ex) {
log.LogError(ex, "transport error");
return;
}
int pc = api.CrawlbaseStatus;
switch (pc) {
case 200:
UseBody(api.Body);
break;
case 520 or 525:
// 520 = empty body, 525 = anti-bot couldn't be solved.
// Switch to JS token and retry.
await RetryWithJsTokenAsync(url);
break;
case 521 or 522 or 523:
// Target unreachable or timed out. Retry with backoff.
ScheduleRetry(url);
break;
default:
log.LogError("crawl failed url={Url} crawlbase_status={CrawlbaseStatus}", url, pc);
break;
}针对平台的所有重试都是免费的:只有成功响应(CrawlbaseStatus: 200)才计入您的配额。
性能 & 最佳实践
- 每个 token 复用单一客户端。
在您的 DI 容器中将其注册为单例:每个实例都会打开自己的底层
HttpClient。不要为每个请求都构造一个新实例。 - 使用能用的最便宜的 token。 不要「以防万一」就默认使用 JavaScript token:Normal token 请求更快,并且占用更少的并发量。
- 优先使用
ajax_wait而非page_wait。 固定延迟会在每个请求上消耗并发量,即使是快速请求也是如此。 - 注意 API 实例上的共享状态。
由于 Crawling/Scraper/Leads/Screenshots API 会将响应状态写入 api 对象(而不是返回值),不要在多个并发
Task之间共享同一个实例:第二个 await 的GetAsync()会在第一个任务读取过程中覆盖其响应状态。请为每个 worker 池化一个实例,或使用 StorageAPI 的返回对象式方法(可安全交错调用)。 - 对于批处理任务:async + webhook,或推送到 Enterprise Crawler。 在同步调用上阻塞的 Awaitable Task 会很快用满并发上限;async + webhook 则会在请求入队的那一刻就释放槽位。
方法参考
所有非 Storage 客户端类共享同一套接口。构造函数接受一个 token 字符串;动作方法以同步 + 异步成对提供,并将响应状态写入 api 实例。
Crawlbase.ScraperAPI、Crawlbase.LeadsAPI、Crawlbase.ScreenshotsAPI、Crawlbase.StorageAPI 形式相同。Task。响应模型相同。data 是请求体:传入 Dictionary 进行表单编码,或传入字符串作为原始内容。响应状态:调用之后 api 实例上的属性:
format=json / scraper= 时为 JSON 字符串)。对于 ScreenshotsAPI,此内容为 base64 编码:可使用 Convert.FromBase64String(api.Body) 进行转换。store=true 时设置。可用于通过 StorageAPI 取回已存储的响应。