Войти

Как устроен SDK

Ruby gem - это тонкая обёртка над тем же HTTP API, который описан в API Reference. Каждый параметр Crawling API, который вы добавили бы в виде query-строки при прямом HTTP-вызове, доступен в gem как ключевой аргумент вызова - имена, значения по умолчанию и поведение соответствуют один к одному. Gem не добавляет ни одного параметра и не скрывает ни одного.

Что вы получаете, используя его вместо Net::HTTP / Faraday напрямую:

  • URL-кодирование, валидация параметров и парсинг ответов - всё из коробки, чтобы код приложения был сосредоточен на бизнес-логике.
  • Идиоматичный Ruby-интерфейс - именованные аргументы, имена параметров в snake_case, исключения при сбоях транспорта, обычные Ruby-объекты ответа.
  • Один клиентский класс на каждый Crawlbase API, все с одинаковой формой конструктора и вызова.
  • Разумные значения по умолчанию (таймаут 90 секунд, автоматический парсинг JSON для ответов format=json, тела в UTF-8) — соответствуют тому, что большинство команд настраивают вручную при первой интеграции.

Исходный код на github.com/crawlbase/crawlbase-ruby. Issues и PRs приветствуются.

Установка

Последняя версия на RubyGems. Протестировано на Ruby 2.7, 3.0, 3.1, 3.2, 3.3 и JRuby.

gem install crawlbase

# Or in your Gemfile
gem 'crawlbase'

Аутентификация

Все Crawlbase API используют одну и ту же модель токена для аутентификации. На одном аккаунте существуют два типа токенов:

  • Normal Token (TCP) - для статического HTML, JSON-эндпоинтов, всего, что не требует браузера. Быстрее и дешевле.
  • JavaScript Token - для SPA, лениво загружаемых лент, всего, где контент скрыт за рендерингом на стороне клиента. Обязателен для использования page_wait, ajax_wait, scroll и css_click_selector.

Используйте Rails credentials (Rails.application.credentials.crawlbase_token) или переменные окружения в продакшене. Gem сам по себе их не читает - это сделано намеренно, чтобы вы контролировали источник учётных данных. Шаблон:

require 'crawlbase'

# Pick the right token at instantiation; the gem doesn't switch
# tokens per-call, so keep two clients if you alternate.
api = Crawlbase::API.new(token: ENV.fetch('CRAWLBASE_TOKEN'))
js = Crawlbase::API.new(token: ENV.fetch('CRAWLBASE_JS_TOKEN'))

api.get('https://github.com/anthropic')
js.get('https://feed.example.com', page_wait: 2000)

Полная модель токенов и расположение в дашборде — на странице Authentication.

Быстрый старт

Три строки от require до полученного HTML:

require 'crawlbase'

api = Crawlbase::API.new(token: 'YOUR_TOKEN')
res = api.get('https://github.com/anthropic')

puts res.body if res.status_code == 200

Ветвитесь по .status_code (HTTP-статус gem к Crawlbase) и .pc_status (вердикт Crawlbase - см. Ошибки ниже) при решении о повторе запроса. Передайте format: 'json', чтобы получить JSON-обёртку вместо сырого содержимого страницы.

Все API в одном gem

У каждого Crawlbase API есть соответствующий класс. Один и тот же конструктор, одни и те же глаголы get / post.

require 'crawlbase'

token = { token: 'YOUR_TOKEN' }

crawl = Crawlbase::API.new(**token) # general-purpose page fetch
scraper = Crawlbase::ScraperAPI.new(**token) # parsed JSON for supported sites
leads = Crawlbase::LeadsAPI.new(**token) # domain-scoped email extraction (legacy)
shots = Crawlbase::ScreenshotsAPI.new(**token) # screenshots of any URL
storage = Crawlbase::StorageAPI.new(**token) # Cloud Storage CRUD

# Push high-volume async jobs to the Enterprise Crawler via the Crawling API:
# api.get(url, async: true, callback: '...', crawler: 'YourCrawler').
# See /docs/crawler for the queue workflow.

Распространённые паттерны

JavaScript-рендеринг

Для SPA, лениво-подгружаемых лент и страниц, где исходный HTML пуст, создавайте экземпляр с JavaScript token и передавайте любую комбинацию page_wait, ajax_wait, scroll и css_click_selector. Логика порядка: фиксированное ожидание, затем network-idle, затем scroll для ленивой загрузки, затем click для любого UI-элемента, открывающего доступ.

api = Crawlbase::API.new(token: 'YOUR_JS_TOKEN')
res = api.get('https://spa.example.com',
 page_wait: 2000,
 ajax_wait: true,
 scroll: true)

Использование встроенного скрапера

Полностью пропустите парсер на поддерживаемых сайтах. Передайте scraper: 'NAME', и тело ответа станет JSON-строкой со структурированными полями, описанными на странице соответствующего скрапера.

require 'crawlbase'
require 'json'

api = Crawlbase::ScraperAPI.new(token: 'YOUR_TOKEN')
res = api.get('https://www.amazon.com/dp/1098145356',
 scraper: 'amazon-product-details')
data = JSON.parse(res.body)
puts data['name'], data['price']

Гео-маршрутизация

Передайте country: 'ISO', чтобы маршрутизировать crawl через выходные узлы указанной страны. Используйте, когда целевой сайт отдаёт локализованный контент на основе IP.

api = Crawlbase::API.new(token: 'YOUR_TOKEN')

# Hit the German Amazon catalog from a German residential IP
res = api.get('https://www.amazon.com/dp/1098145356', country: 'DE')

Повторные попытки с backoff

Рекомендуемая форма повторов: экспоненциальный backoff с ограничением в 3-5 попыток, повтор только при временных ошибках (5xx или пустое тело), без повторов на 4xx.

require 'crawlbase'

api = Crawlbase::API.new(token: 'YOUR_TOKEN')

def crawl(api, url, attempts: 5)
 attempts.times do |i|
 res = api.get(url)
 return res if res.status_code == 200 && res.pc_status.to_i == 200
 raise "client error: %d" % res.status_code if (400..499).include?(res.status_code)
 sleep(rand * (2**i)) # exponential backoff with jitter
 end
 raise "Failed: %s" % url
end

Асинхронные crawl-запросы и вебхуки

Режим fire-and-forget. Вызов gem немедленно возвращается с rid; Crawlbase отправляет результат POST-запросом на ваш callback URL, когда страница готова. Полезно для пакетных задач и медленных целей.

api = Crawlbase::API.new(token: 'YOUR_TOKEN')
res = api.get('https://example.com',
 async: true,
 callback: 'https://your-app.com/webhook')
rid = res.rid # correlate the eventual webhook delivery

# Your Rails / Sinatra webhook receives a POST with:
# { rid, url, original_status, pc_status, body }

Для очень больших объёмов (миллионы URL) используйте Enterprise Crawler, который стоит перед тем же асинхронным пайплайном.

Sticky-сессии

Некоторые сценарии требуют одного и того же резидентного IP в нескольких вызовах. Передайте cookies_session со стабильным идентификатором, и Crawlbase будет переиспользовать тот же выходной узел в течение ~30 минут.

api = Crawlbase::API.new(token: 'YOUR_JS_TOKEN')

session = "checkout-#{user_id}"
api.get('https://shop.example.com/cart', cookies_session: session)
api.get('https://shop.example.com/checkout', cookies_session: session)
api.get('https://shop.example.com/confirm', cookies_session: session)

Ошибки и повторы

Платформа возвращает два кода статуса в каждом ответе: собственный .status_code gem (HTTP-статус запроса к самому Crawlbase) и .pc_status (вердикт Crawlbase по целевому ресурсу - полный список см. в таблице ошибок Crawling API). При решении о повторе всегда ветвитесь по .pc_status - целевой ресурс может вернуть 200 с пустым телом, и тогда .status_code будет 200, а .pc_status - 520.

res = api.get(url)
pc = res.pc_status.to_i

case pc
when 200
 use(res.body)
when 520, 525
 # 520 = empty body, 525 = anti-bot couldn't be solved.
 # Switch to JS token and retry.
 retry_with_js_token(url)
when 521, 522, 523
 # Target unreachable or timed out. Retry with backoff.
 schedule_retry(url)
else
 Rails.logger.error('crawl failed', url: url, pc_status: pc)
end

Все повторы к платформе бесплатны - в квоту засчитываются только успешные ответы (pc_status: 200).

Производительность и лучшие практики

  • Переиспользуйте один клиент на токен. Конструктор дешёвый, но каждый экземпляр открывает собственное соединение. Создайте его один раз при старте приложения (Rails initializer — естественное место) и используйте между запросами.
  • Используйте самый дешёвый рабочий токен. Не используйте JavaScript token по умолчанию «на всякий случай» - запросы с Normal token быстрее и используют меньше параллельных слотов. Переключайтесь на JS только тогда, когда ответ от Normal пустой или заблокирован анти-бот-защитой.
  • Предпочитайте ajax_wait вместо page_wait. Фиксированные задержки расходуют concurrency на каждом запросе, даже на быстрых.
  • Для пакетных задач: async + webhook или передача в Enterprise Crawler. Sidekiq-воркеры, синхронно вызывающие gem, насытят ваш предел concurrency; async + webhook освобождает слот в момент постановки запроса в очередь.
  • Следите за заголовком ответа remaining. Он содержит число оставшихся слотов параллельных запросов - снижайте темп заранее, не дожидаясь упора в лимит и ответов 429.

Справочник методов

Все клиентские классы имеют одинаковый интерфейс. Конструктор принимает keyword arguments; глаголы зеркалят соответствующие HTTP-методы.

Crawlbase::API.new(token:, timeout:)
конструктор
Инициализирует клиента с вашим token. Опционально: timeout в секундах (по умолчанию 90).
#get(url, **options)
метод
Отправляет GET. options сопоставляет любой параметр Crawling API с его значением. Возвращает объект response.
#post(url, data, **options)
метод
Отправить POST. data - это body: передайте hash для form-encoded, строку для сырых данных.

Форма ответа - методы объекта ответа:

.status_code
Integer
HTTP-статус запроса gem к Crawlbase.
.pc_status
Integer
Вердикт Crawlbase по целевому сайту. Используйте его для решений о повторных попытках.
.original_status
Integer
HTTP-статус, который целевой сайт вернул Crawlbase.
.url
String
Итоговый URL после редиректов на стороне целевого сайта.
.body
String
Содержимое страницы (или JSON-строка, если использовались format=json / scraper=).
.headers
Hash
Заголовки ответа от целевого сайта.
.rid
String
Request ID (когда указано async: true или store: true).