Перейти к содержимому

Smart Scrape

Smart Scrape выбирает самый быстрый способ получить пригодный документ и запускает Chromium только при необходимости. Возвращаемое тело всегда сопровождается дискриминатором contentKind, который говорит, как читать content.

Окно терминала
curl -X POST "https://proxy.unoapi.ru/v1/browser/smart-scrape?timeout=30000" \
-H "Authorization: Bearer $UNOAPI_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com",
"formats": ["content", "markdown"]
}'
Поле Тип По умолчанию Описание
url string Обязательный HTTP(S) URL
formats string[] ["content"] content, markdown, links, screenshot, pdf, seo; html принимается как псевдоним content

Query-параметр timeout задаётся в миллисекундах и применяется отдельно к HTTP- и browser-попыткам. Попытка сохранённой копии из поискового кэша ограничена двумя секундами.

Форматы screenshot и pdf сразу запускают браузер. Если запрошены оба, они создаются из одной загрузки страницы.

Дискриминатор присваивается один раз — в момент получения ответа — и сохраняется вместе с телом. Клиент обязан ветвиться по contentKind и не должен переклассифицировать contentType или анализировать содержимое.

contentKind описывает сам ресурс, а не поле content, поэтому он возвращается всегда, когда документ получен, — в том числе если формат content не запрашивался. Иначе запрос только с markdown получил бы дословный неочищенный исходник и ничего, по чему ветвиться.

contentKind MIME-типы Что лежит в content
html text/html, application/xhtml+xml Очищенный результат извлечения: скрипты, стили, обработчики событий и javascript:/data: ссылки удалены
json application/json, text/json, application/*+json Точный декодированный исходник, без санитизации
xml application/xml, text/xml, image/svg+xml, application/*+xml (кроме XHTML) Точный декодированный исходник, без санитизации
text application/robots+txt и любой другой text/* — включая text/plain, text/markdown, text/csv, text/tab-separated-values, text/css, text/javascript Точный декодированный исходник, без санитизации
pdf application/pdf Извлечённый текстовый слой — не байты файла. Скан без текстового слоя — неудача с message: "PDF has no text layer"
spreadsheet application/vnd.ms-excel, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel.sheet.macroEnabled.12, application/vnd.ms-excel.sheet.binary.macroEnabled.12, application/vnd.oasis.opendocument.spreadsheet, application/vnd.openxmlformats-officedocument.spreadsheetml.template, application/vnd.ms-excel.template.macroEnabled.12, application/vnd.oasis.opendocument.spreadsheet-template CSV по листам — один лист как чистый CSV, несколько разделяются строками === Sheet: имя ===

text/event-stream не поддерживается: потоковый ответ не имеет конца и не может быть прочитан как документ. Не распознанные типы — image/png, application/msword, application/octet-stream без подсказки в URL — документом не считаются.

pdf и spreadsheet — третий класс наряду с извлечением HTML и точным исходником: content для них — текст, извлечённый из бинарного файла. Не путайте с форматом запроса pdf, который рендерит страницу в PDF, — это разные поля. Лимит чтения для бинарных документов — 10 МиБ (текстовые — 4 МиБ).

  1. Распознанный MIME-тип ответа имеет приоритет всегда, даже если противоречит расширению в URL.
  2. Если заголовок Content-Type отсутствует, используется расширение конечного URL, иначе html.
  3. Если тип указан, но не описывает формат (application/octet-stream), используется только расширение конечного URL.
  4. Любой другой распознанный, но неподдерживаемый тип документом не является.

Расширение берётся из конечного URL после редиректов. Подсказки: .html/.htm, .json, .xml/.rss/.atom/.svg, .txt/.md/.markdown/.csv/.tsv, .pdf, .xls/.xlsx/.xlsm/.xlsb/.ods/.xlt/.xltx/.xltm/.ots.

  1. Общий 12-часовой кэш документов.
  2. Сохранённая копия из кэша поисковой системы — только если URL не оканчивается явным «сырым» расширением и сама копия является HTML.
  3. Анонимный HTTP-запрос к оригиналу.
  4. Изолированный headless Chromium для JavaScript-страниц и некачественных HTTP-ответов.

Поле strategy содержит фактически сработавшую стратегию: cache, yandex-cache, http-fetch или browser; значение yandex-cache соответствует уровню сохранённой копии из поискового кэша. attempted перечисляет сделанные попытки по порядку.

Правила перехода к браузеру:

  • «сырой» или бинарный MIME-тип на 2xx возвращает результат и никогда не эскалирует: Chromium не прочитает исходник или бинарный файл лучше прямого запроса. Более того, headless-браузер вообще не открывает PDF и таблицы (навигация превращается в скачивание), поэтому извлечение из бинарных файлов работает только на дешёвом уровне;
  • «сырой» MIME-тип на не-2xx останавливается на дешёвом уровне;
  • URL с явным «сырым» расширением, который упал или ответил не-2xx, останавливается на дешёвом уровне — даже если тело ошибки является HTML;
  • HTML-тип на 2xx перекрывает «сырое» расширение и идёт обычным путём извлечения, включая переход в браузер;
  • 2xx с распознанным, но неподдерживаемым типом (image/png, application/msword) завершается на дешёвом уровне: Chromium прочитает тот же заголовок и ответит так же, поэтому переход в браузер оплачивался бы впустую. Исключение — application/octet-stream: формат не назван, и источник, меняющий ответ по User-Agent, может отдать браузеру что-то читаемое;
  • когда Chromium доходит до JSON, XML или текста, возвращается тело основного навигационного ответа, а не DOM встроенного просмотрщика.
{
"ok": true,
"statusCode": 200,
"content": "<html>...</html>",
"contentKind": "html",
"contentType": "text/html; charset=utf-8",
"headers": {"content-type": "text/html; charset=utf-8"},
"strategy": "http-fetch",
"attempted": ["cache", "http-fetch"],
"message": null,
"screenshot": null,
"pdf": null,
"markdown": "# Article\n...",
"links": ["https://example.com/next"],
"seo": {
"title": "Article",
"metadata": {},
"canonical": "https://example.com/article",
"language": "en",
"headings": [],
"links": [],
"images": [],
"jsonLd": [],
"diagnostics": []
}
}
Случай content contentKind
Успех, формат content запрошен строка html, json, xml, text, pdf или spreadsheet
Успех, формат content не запрошен null html, json, xml, text, pdf или spreadsheet
Только screenshot/pdf null null
Исчерпанные стратегии, не-2xx, неподдерживаемый тип null null

Для json, xml, text, pdf и spreadsheet поля links и seo всегда null. Производный markdown: для text — сам исходник; для xml — таблица sitemap или список записей RSS/Atom, иначе инертный блок ```xml; для json — инертный блок ```json с точным исходником, без переформатирования; для pdf — извлечённый текст как есть; для spreadsheet — Markdown-таблица по каждому листу, не более 100 строк на лист с пометкой [… ещё N строк] (CSV в content при этом не усечён).

Тело декодируется по объявленной кодировке: сначала BOM, затем charset из заголовка, затем <meta charset> или объявление XML, иначе UTF-8. Это касается и дешёвого HTTP-уровня, и браузерного, поэтому windows-1251 читается одинаково на обоих.

Внешние Set-Cookie, авторизационные и другие небезопасные заголовки не возвращаются.

Если все стратегии исчерпаны, HTTP-статус остаётся 200, а тело содержит ok: false и диагностическое message. При этом сохраняются статус, MIME-тип, безопасные заголовки, редиректы и X-Response-URL последней попытки. Смешанный запрос вида ["content", "screenshot"] к ресурсу без документа вернёт ok: false, но скриншот останется на месте. Ошибки самого запроса — невалидный URL, формат или таймаут — возвращаются до выполнения и не тарифицируются.

Все примеры проверены против production. Ключ передаётся через переменную окружения UNOAPI_KEY.

Cache-Control: no-cache пропускает кэш ответов, кэш документов и сохранённую копию из поискового кэша и идёт сразу к источнику. Ответ приходит с X-Cache: BYPASS, а свежий результат обновляет кэши. Свежесть имеет цену: страница, которая обычно закрывается сохранённой копией за 1 копейку, без неё может дойти до Chromium и browser-уровня.

Окно терминала
curl -s -D - -X POST "https://proxy.unoapi.ru/v1/browser/smart-scrape?timeout=30000" \
-H "Authorization: Bearer $UNOAPI_KEY" \
-H "Content-Type: application/json" \
-H "Cache-Control: no-cache" \
-d '{"url": "https://example.com", "formats": ["content"]}'
# в заголовках ответа: x-cache: BYPASS

Форматы seo и links работают только для HTML-документов; для json, xml и text оба поля равны null. Элементы seo.headings — объекты с полями level и text.

Окно терминала
curl -s -X POST "https://proxy.unoapi.ru/v1/browser/smart-scrape?timeout=30000" \
-H "Authorization: Bearer $UNOAPI_KEY" \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com", "formats": ["seo", "links"]}'

Визуальные форматы идут сразу в браузер и тарифицируются по browser-уровню. PNG приходит в поле screenshot в base64. content равен null, если формат content не запрошен; contentKind равен null, когда документ не запрашивался вовсе (как здесь) или получить его не удалось (ok: false) — см. таблицу в разделе «Ответ». Запрос ["screenshot", "markdown"] вернёт contentKind вместе со скриншотом.

Окно терминала
curl -s -X POST "https://proxy.unoapi.ru/v1/browser/smart-scrape?timeout=30000" \
-H "Authorization: Bearer $UNOAPI_KEY" \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com", "formats": ["screenshot"]}' \
| jq -r '.screenshot // empty' | base64 -d > page.png

content для бинарных документов — извлечённый текст: у PDF — текстовый слой, у таблиц — CSV по листам. Запрос ничем не отличается от обычного; вид придёт в contentKind.

Окно терминала
curl -s -X POST "https://proxy.unoapi.ru/v1/browser/smart-scrape?timeout=30000" \
-H "Authorization: Bearer $UNOAPI_KEY" \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com/annual-report.pdf", "formats": ["content"]}'
# contentKind: "pdf", content — текстовый слой документа
Окно терминала
curl -s -X POST "https://proxy.unoapi.ru/v1/browser/smart-scrape?timeout=30000" \
-H "Authorization: Bearer $UNOAPI_KEY" \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com/stats.xlsx", "formats": ["content", "markdown"]}'
# contentKind: "spreadsheet", content — CSV, markdown — таблицы по листам

Ограничения: файл больше 10 МиБ, извлечённый текст больше 1,5 МБ (такой результат не поместился бы в кэш, и каждый повторный запрос парсил бы файл заново), скан без текстового слоя или повреждённый файл — ok: false на дешёвом уровне с диагностическим message. Если прямой HTTP-запрос к файлу недоступен, headless-браузер не поможет: Chromium скачивает бинарные файлы, а не открывает их.

  • Кэш документов, сохранённая копия из поискового кэша и прямой HTTP: 1 копейка.
  • Попытка Chromium, включая неуспешную: 10 копеек.
  • Идентичный 12-часовой response-cache hit: 1 копейка, без обращения к browser-сервису.

В cache identity входят канонический URL и нормализованный набор форматов. timeout и порядок/дубликаты форматов на identity не влияют. Неизвестные поля тела игнорируются, поэтому снятый в этой версии proxy у старых клиентов не меняет ни ответ, ни ключ кэша. Ответы с no-store, ошибки, скриншоты, PDF и слишком большие envelopes не сохраняются.

Тело больше 4 МиБ не возвращается. На HTTP-уровне и в /v1/browser/cheerio чтение прерывается по достижении лимита, в браузере — отклоняется по заявленному Content-Length, а тело без объявленной длины буферизуется один раз и отбрасывается (Playwright не даёт потокового доступа). Слишком большой ответ origin завершает запрос на дешёвом уровне: Chromium не сделает документ меньше, поэтому переход в браузер только удвоил бы расход и стоимость.

Endpoint принимает запросы Browserless Smart Scrape без изменений и расширяет ответ:

https://production-sfo.browserless.io/smart-scrape
https://proxy.unoapi.ru/v1/browser/smart-scrape

Запросы работают как есть: formats: ["html"] принимается (как псевдоним content), поле proxy игнорируется. Что стоит знать при переходе:

  • ответ содержит обязательное поле contentKind — ветвитесь по нему, а не по Content-Type; для HTML-документов content приходит очищенным, а не сырым;
  • неудача — это HTTP 200 с ok: false и заполненными statusCode, contentType, безопасными заголовками и редиректами последней попытки, а не код ошибки;
  • добавлены форматы seo и links, каскад с общим кэшем документов и сохранённой копией из поискового кэша и двухуровневая тарификация — дешёвый уровень без запуска Chromium.

Остальные Browserless-эндпоинты (/screenshot, /content, /scrape, /pdf, /performance) сохраняют совместимость — см. обзор Browser API.