C# / .NET
Официальный .NET-клиент для платформы Crawlbase. У каждого метода есть синхронная версия (например, Get) и асинхронная версия (GetAsync) - один артефакт, все API, разумные значения по умолчанию.
Как устроен SDK
SDK для .NET - это тонкая обёртка над тем же HTTP API, который описан в API Reference. Каждый параметр Crawling API, который вы добавили бы как query string в чистом HTTP-запросе, доступен как опция Dictionary<string, object> - имена, значения по умолчанию и поведение совпадают один к одному.
Одна особенность, о которой стоит знать заранее: .NET SDK предоставляет состояние ответа на самом экземпляре API, а не в возвращаемом объекте. Вызовы вроде api.Get(url) возвращают void; результат вы читаете через api.StatusCode, api.Body и так далее. Это отличается от SDK для Python / Node / Ruby / PHP (которые возвращают объект ответа). Исключение - Storage API: его методы возвращают объект ответа, который вы читаете напрямую.
Что вы получаете, используя его вместо прямой работы с HttpClient:
- URL-кодирование, валидация параметров и парсинг ответов работают из коробки.
- Пара sync + async на каждый метод - выбирайте тот, что подходит для вашего места вызова.
- Один клиентский класс на каждый Crawlbase API, все с одинаковой формой конструктора и вызова.
- Разумные значения по умолчанию (таймаут 90 секунд, автоматический парсинг JSON для ответов
format=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 APIs аутентифицируются по одной и той же модели токенов. На одном аккаунте существуют два типа токенов:
- 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);Полная модель токенов и их расположение в дашборде — на странице Аутентификация.
Быстрый старт
Три строки от namespace до полученного ответа. Обратите внимание, что состояние ответа живёт на экземпляре 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 (HTTP-статус запроса SDK к Crawlbase) и api.CrawlbaseStatus (вердикт Crawlbase - см. Ошибки ниже) при принятии решения о повторе. Передайте new Dictionary<string,object> { ["format"] = "json" }, чтобы получить JSON-обёртку вместо сырого содержимого страницы.
Все APIs в одном пакете
У каждого продукта Crawlbase есть соответствующий клиентский класс. Один и тот же конструктор (одна строка с токеном), одинаковая форма 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. Порядок мышления: фиксированное ожидание, затем network-idle, затем скролл для lazy-load, затем клик для любого закрывающего 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"] = "NAME" — и body становится JSON-строкой со структурированными полями, описанными на странице соответствующего скрейпера.
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", чтобы маршрутизировать crawl через выходные узлы этой страны. Используйте всякий раз, когда целевой сайт отдаёт локализованный контент в зависимости от 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" }
);Повторы с backoff
Рекомендуемая форма повторов: экспоненциальный backoff с лимитом 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;
}Асинхронные crawls и вебхуки
Режим fire-and-forget. Передайте ["async"] = true вместе с URL в ["callback"]; вызов сразу возвращает управление, а Crawlbase отправит результат POST-ом на ваш вебхук, когда страница будет готова. Полезно для пакетных задач и медленных целей.
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, который стоит перед тем же асинхронным пайплайном.
Sticky-сессии
Некоторым сценариям нужен один и тот же резидентный 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);CRUD для Cloud Storage
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();Ошибки и повторы
Платформа выдаёт два статус-кода в каждом ответе: собственный api.StatusCode SDK (HTTP-статус запроса к самой Crawlbase) и api.CrawlbaseStatus (вердикт Crawlbase по целевому ресурсу - полный список см. в таблице ошибок Crawling API). Всегда ветвитесь по api.CrawlbaseStatus при принятии решения о повторе - целевой ресурс может вернуть 200 с пустым body, и тогда 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) учитываются в вашей квоте.
Производительность и лучшие практики
- Переиспользуйте один клиент на токен.
Зарегистрируйте его как singleton в вашем DI-контейнере - каждый экземпляр открывает свой собственный
HttpClient. Не создавайте по экземпляру на запрос. - Используйте самый дешёвый токен, который работает. Не используйте JavaScript token по умолчанию «на всякий случай» - запросы с Normal token быстрее и расходуют меньше параллелизма.
- Предпочитайте
ajax_waitвместоpage_wait. Фиксированные задержки сжигают конкурентность на каждом запросе, даже на быстрых. - Учитывайте общее состояние на экземпляре API.
Поскольку Crawling/Scraper/Leads/Screenshots API записывают состояние ответа в объект api (а не возвращают значение), не разделяйте один экземпляр между параллельными
Task- второйGetAsync()с await перезапишет состояние ответа первой задачи прямо во время чтения. Создайте пул из одного экземпляра на воркер или используйте методы StorageAPI с возвращаемым объектом, которые безопасно чередовать. - Для пакетных задач: async + вебхук или передача в Enterprise Crawler. Awaitable Tasks, блокирующиеся на синхронных вызовах, быстро упираются в лимиты конкурентности; async + вебхук освобождает слот в момент постановки запроса в очередь.
Справочник методов
Все клиентские классы, кроме Storage, имеют одинаковый интерфейс. Конструкторы принимают строку токена; глаголы идут парами sync + async и записывают состояние ответа в экземпляр api.
Crawlbase.ScraperAPI, Crawlbase.LeadsAPI, Crawlbase.ScreenshotsAPI, Crawlbase.StorageAPI.Task. Та же модель ответа.data - это body: передайте Dictionary для form-encoded, строку для raw.Состояние ответа - свойства экземпляра api после вызова:
format=json / scraper=). Для ScreenshotsAPI оно закодировано в base64 - декодируйте через Convert.FromBase64String(api.Body).store=true. Используйте эти значения, чтобы получить сохранённый ответ обратно через StorageAPI.