Ruby
Официальный Ruby gem для платформы Crawlbase. Идиоматичный Ruby для Ruby 2.7+ и JRuby - один и тот же gem, все API, разумные значения по умолчанию, соответствующие тому, что большинство Rails-приложений настраивают вручную.
Как устроен 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-методы.
timeout в секундах (по умолчанию 90).options сопоставляет любой параметр Crawling API с его значением. Возвращает объект response.data - это body: передайте hash для form-encoded, строку для сырых данных.Форма ответа - методы объекта ответа:
format=json / scraper=).async: true или store: true).