Go
Официальный Go-клиент для платформы Crawlbase. Идиоматичный Go: возврат ошибок вместо исключений, поддержка context.Context у каждого метода, нулевые внешние зависимости (только net/http + stdlib).
Как устроен SDK
Go SDK намеренно минималистичен. Один клиент, CrawlingAPI, охватывает все продукты Crawlbase через единый endpoint Crawling API:
| Сценарий использования | Передать в options |
|---|---|
| Обычный обход | (ничего, значение по умолчанию) |
| Встроенный скрейпер | "scraper": "amazon-product-details" (и остальные из каталога) |
| Скриншот | "screenshot": "true" |
| Извлечение email | "scraper": "email-extractor" |
| Async + webhook | "async": "true" + "callback": "https://..." |
| Отправка в Enterprise Crawler | "async": "true" + "callback" + "crawler": "YourCrawler" |
Отдельные endpoint'ы /scraper, /leads и /screenshots (которые старые Crawlbase SDK оборачивают отдельными классами клиентов) закрыты для новых регистраций с 2024 года. Go SDK поставляется только с современным путём: один клиент, все продукты, никаких рудиментарных классов.
Что вы получаете, используя его вместо net/http напрямую:
- URL-кодирование, валидация параметров и парсинг ответов — из коробки.
- Идиоматичная поверхность Go: возврат
(result, error), именованные поля структур, никаких panic при сбоях транспорта. - Поддержка
context.Contextв каждом методе через варианты*WithContextдля отмены / дедлайнов / распространения трассировки. - Разумные значения по умолчанию (таймаут 90 секунд, прозрачная распаковка gzip, автоматический парсинг JSON для ответов
format=json/scraper=).
Исходники на github.com/crawlbase/crawlbase-go. Справочник на pkg.go.dev. Issues и PR приветствуются.
Установка
Последняя версия на pkg.go.dev. Требуется Go 1.21+.
go get github.com/crawlbase/crawlbase-go@latest
# Or pin a specific version
go get github.com/crawlbase/[email protected]Аутентификация
Все API Crawlbase используют одну и ту же модель токенов. На одном аккаунте существует два типа токенов:
- Normal Token (TCP) - для статического HTML, JSON-эндпоинтов, всего, что не требует браузера. Быстрее и дешевле.
- JavaScript Token
- для SPA, лениво загружаемых лент, всего, что скрывает контент за клиентским рендерингом. Требуется для использования
page_wait,ajax_wait,scrollиcss_click_selector.
В продакшене используйте переменные окружения. SDK сам не читает env-переменные, это сделано намеренно, чтобы вы сохраняли контроль над тем, откуда берутся учётные данные. Шаблон:
package main
import (
"log"
"os"
"github.com/crawlbase/crawlbase-go"
)
func main() {
// Pick the right token at instantiation; the SDK doesn't switch
// tokens per-call, so keep two clients if you alternate.
api, err := crawlbase.NewCrawlingAPI(os.Getenv("CRAWLBASE_TOKEN"))
if err != nil {
log.Fatal(err)
}
js, err := crawlbase.NewCrawlingAPI(os.Getenv("CRAWLBASE_JS_TOKEN"))
if err != nil {
log.Fatal(err)
}
api.Get("https://github.com/anthropic", nil)
js.Get("https://feed.example.com", map[string]string{"page_wait": "2000"})
}Конструктор возвращает crawlbase.ErrTokenRequired, если строка токена пуста. Полная модель токенов и расположение в дашборде — на странице Аутентификация.
Быстрый старт
Три строки от импорта до полученного HTML:
package main
import (
"fmt"
"log"
"github.com/crawlbase/crawlbase-go"
)
func main() {
api, err := crawlbase.NewCrawlingAPI("YOUR_TOKEN")
if err != nil {
log.Fatal(err)
}
res, err := api.Get("https://github.com/anthropic", nil)
if err != nil {
log.Fatal(err)
}
if res.StatusCode == 200 {
fmt.Println(res.Body)
}
}Ветвитесь по res.StatusCode (HTTP-статус SDK к Crawlbase) и res.PCStatus (вердикт Crawlbase, см. Ошибки ниже), когда решаете, повторять ли запрос. Передайте map[string]string{"format": "json"}, чтобы получить JSON-конверт вместо сырого содержимого страницы (автоматически разбирается в res.JSON).
Распространённые шаблоны
JavaScript-рендеринг
Для SPA, лениво подгружаемых лент и страниц с пустым начальным HTML создавайте экземпляр с JavaScript token и передавайте любую комбинацию из page_wait, ajax_wait, scroll и css_click_selector. Порядок, о котором стоит подумать: фиксированное ожидание, затем network-idle, затем scroll для ленивой загрузки, затем click для любого блокирующего UI-элемента.
api, _ := crawlbase.NewCrawlingAPI("YOUR_JS_TOKEN")
res, err := api.Get("https://spa.example.com", map[string]string{
"page_wait": "2000",
"ajax_wait": "true",
"scroll": "true",
})Использование встроенного скрейпера
Полностью пропустите парсер на поддерживаемых сайтах. Передайте "scraper": "NAME", и тело ответа станет JSON-строкой со структурированными полями, описанными на странице конкретного скрейпера. Тело также предварительно декодируется в res.JSON, чтобы вы могли читать поля напрямую.
api, _ := crawlbase.NewCrawlingAPI("YOUR_TOKEN")
res, err := api.Get(
"https://www.amazon.com/dp/1098145356",
map[string]string{"scraper": "amazon-product-details"},
)
if err != nil {
log.Fatal(err)
}
if name, ok := res.JSON["name"].(string); ok {
fmt.Println(name)
}Гео-маршрутизация
Передайте "country": "ISO", чтобы направить обход через выходные узлы соответствующей страны. Используйте всегда, когда целевой сайт отдаёт локализованный контент в зависимости от IP.
api, _ := crawlbase.NewCrawlingAPI("YOUR_TOKEN")
// Hit the German Amazon catalog from a German residential IP
res, _ := api.Get(
"https://www.amazon.com/dp/1098145356",
map[string]string{"country": "DE"},
)Повторные попытки с backoff
Рекомендуемая схема повторов: экспоненциальный backoff с ограничением 3–5 попыток, повтор только при временных ошибках (5xx или пустое тело), без повторов при 4xx.
import (
"fmt"
"math"
"math/rand"
"time"
"github.com/crawlbase/crawlbase-go"
)
func Crawl(api *crawlbase.CrawlingAPI, url string, attempts int) (*crawlbase.Response, error) {
for i := 0; i < attempts; i++ {
res, err := api.Get(url, nil)
if err != nil {
return nil, err
}
if res.StatusCode == 200 && res.PCStatus == 200 {
return res, nil
}
if res.StatusCode >= 400 && res.StatusCode < 500 {
return nil, fmt.Errorf("client error %d: %s", res.StatusCode, url)
}
// Exponential backoff with jitter
d := time.Duration(rand.Float64() * math.Pow(2, float64(i)) * float64(time.Second))
time.Sleep(d)
}
return nil, fmt.Errorf("failed: %s", url)
}Async-обходы + webhooks
Режим fire-and-forget. Вызов SDK сразу возвращается с RID; Crawlbase отправляет результат POST-запросом на ваш callback URL, когда страница готова. Полезно для пакетных задач и медленных целевых сайтов.
api, _ := crawlbase.NewCrawlingAPI("YOUR_TOKEN")
res, _ := api.Get("https://example.com", map[string]string{
"async": "true",
"callback": "https://your-app.com/webhook",
})
rid := res.RID // correlate the eventual webhook delivery
// Your net/http handler receives a POST with:
// { rid, url, original_status, pc_status, body }Для очень больших объёмов (миллионы URL) отправляйте задачи в Enterprise Crawler, добавив "crawler": "YourCrawlerName" вместе с опциями async + callback.
Sticky-сессии
Некоторым сценариям нужен один и тот же резидентский IP на протяжении нескольких вызовов. Передайте cookies_session со стабильным идентификатором, и Crawlbase будет переиспользовать тот же выходной узел в течение примерно 30 минут.
api, _ := crawlbase.NewCrawlingAPI("YOUR_JS_TOKEN")
session := fmt.Sprintf("checkout-%d", userID)
opts := map[string]string{"cookies_session": session}
api.Get("https://shop.example.com/cart", opts)
api.Get("https://shop.example.com/checkout", opts)
api.Get("https://shop.example.com/confirm", opts)Скриншоты
Передайте "screenshot": "true", чтобы сделать снимок всей страницы. Тело возвращается как изображение, закодированное в base64; используйте crawlbase.ImageBytes(res) для декодирования в сырые байты для os.WriteFile / image.Decode.
api, _ := crawlbase.NewCrawlingAPI("YOUR_JS_TOKEN")
res, _ := api.Get("https://www.apple.com", map[string]string{
"screenshot": "true",
})
img, err := crawlbase.ImageBytes(res)
if err != nil {
log.Fatal(err)
}
os.WriteFile("apple.png", img, 0o644)Context для отмены
У каждого метода есть вариант *WithContext для использования с context.Context: полезно всякий раз, когда вызов должен учитывать отмену сверху, дедлайны или распространение трассировок (HTTP-обработчики, gRPC-серверы, всё, что находится в цикле запросов).
import (
"context"
"time"
)
api, _ := crawlbase.NewCrawlingAPI("YOUR_TOKEN")
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
res, err := api.GetWithContext(ctx, "https://example.com", nil)Ошибки & повторы
Платформа возвращает два кода состояния в каждом ответе: собственный res.StatusCode SDK (HTTP-статус запроса к самой Crawlbase) и res.PCStatus (вердикт Crawlbase по целевой странице, извлечённый из заголовка ответа pc_status для типизированного доступа; полный список см. в таблице ошибок Crawling API). Всегда ветвитесь по PCStatus, когда решаете, повторять ли запрос: цель может вернуть 200 с пустым телом, в этом случае StatusCode равен 200, но PCStatus равен 520.
res, err := api.Get(url, nil)
if err != nil {
return err
}
switch res.PCStatus {
case 200:
use(res.Body)
case 520, 525:
// 520 = empty body, 525 = anti-bot couldn't be solved.
// Switch to JS token and retry.
retryWithJSToken(url)
case 521, 522, 523:
// Target unreachable or timed out. Retry with backoff.
scheduleRetry(url)
default:
log.Printf("crawl failed: url=%s pc_status=%d", url, res.PCStatus)
}Все повторные запросы к платформе бесплатны: только успешные ответы (PCStatus: 200) учитываются в вашей квоте.
Производительность & лучшие практики
- Переиспользуйте один клиент на token.
Конструктор дешёвый, но каждый экземпляр
*CrawlingAPIимеет собственныйhttp.Clientсо своим пулом соединений. Создавайте его один раз при инициализации сервиса и используйте совместно во всех горутинах (SDK потокобезопасен). - Используйте самый дешёвый token, который работает.
Не используйте JavaScript token по умолчанию «на всякий случай»: запросы с Normal token быстрее и используют меньше параллелизма. Повышайте уровень при
PCStatus == 520или525. - Предпочитайте
ajax_waitвместоpage_wait. Фиксированные задержки расходуют параллелизм на каждом запросе, даже на быстрых. - Для пакетных задач: async + webhook или отправка в Enterprise Crawler. Пулы горутин, блокирующиеся на синхронных вызовах, быстро упираются в лимиты параллелизма; async + webhook освобождает слот в момент постановки запроса в очередь.
- Используйте
GetWithContext/PostWithContextв серверном коде. Context уровня запроса распространяет отмену, когда вызывающая сторона уходит: без него зависший обход будет продолжаться после дедлайна вызывающей стороны.
Поля ответа
Полные сигнатуры методов, godoc и примеры по каждому методу живут на pkg.go.dev. Поля ниже, к которым пользователи Crawlbase обращаются чаще всего: типизированный вердикт по цели, возвращаемый в каждом *crawlbase.Response:
pc_status (или cb_status) для типизированного доступа. Принимайте решение о повторе на основе этого поля.format=json / scraper=; или изображение в base64, если использовался screenshot=true)."async": "true" или "store": "true".json.Unmarshal для вызовов scraper / format=json.