Прочитать одну веб-страницу и написать краткое резюме несложно. Делать то же самое для нескольких сотен страниц каждое утро, совсем другая задача, и именно для такой работы хорошо подходит языковая модель. Сложность никогда не была в самом суммировании. Трудность, получить чистый читаемый текст со страниц, которые этому противятся, а затем подавать модели больше текста, чем вмещает её контекстное окно, не теряя нить.
Это руководство рассматривает обе части от начала до конца. Вы будете собирать страницы с помощью Crawlbase Crawling API в виде чистого markdown, затем суммировать их с помощью LLM, а когда страница слишком длинна для одного вызова, нарезать её на части и применять map-reduce суммирование, чтобы ничего не выпало. Всё здесь, это исполняемый Python, ориентированный на публичный веб-контент. В итоге у вас будет небольшой пайплайн, превращающий список URL в короткие, последовательные резюме, которые можно хранить, искать или включать в отчёт. Это и есть суть того, как суммировать веб-данные с помощью Crawlbase и AI.
Зачем суммировать веб-данные с помощью Crawlbase и AI
Просмотр одной страницы показывает, что говорит один документ прямо сейчас. Ценность появляется при масштабировании: отслеживать, что говорит набор страниц конкурентов с течением времени, сжимать поток статей в ежедневный дайджест или превращать страницы товаров и отзывов в несколько строк, которые человек действительно прочитает. LLM работает быстро и последовательно, применяя одни и те же критерии к каждому документу без усталости к двухсотой странице.
Однако модель хороша ровно настолько, насколько хорош передаваемый ей текст, и именно здесь большинство проектов «AI-суммаризатора» незаметно ломаются. Современные страницы насыщены JavaScript, завёрнуты в навигацию, рекламу, баннеры cookies и шаблонные блоки, и многие из них прямо блокируют автоматизированный трафик. Передайте сырой HTML в модель, и вы потратите токены на суммирование разметки и меню вместо контента. Решение, разделить сбор и суммирование: пусть Crawlbase занимается рендерингом, разблокировкой и чистым извлечением, а модель делает то, что умеет. Подробнее об этом шаге извлечения, в статье как работает AI-извлечение данных.
Как пайплайн складывается воедино
Есть два этапа, и их разделение делает всё поддерживаемым.
- Сбор. Crawling API загружает каждый URL за надёжным IP, при необходимости рендерит JavaScript и возвращает чистый markdown вместо сырого HTML. Это означает, что суммируемый текст уже очищен от навигации, скриптов и стилей.
- Суммирование. LLM читает markdown и возвращает краткое резюме. Для страниц, умещающихся в контекстное окно модели, это один вызов. Для длинных страниц вы разбиваете текст на части, суммируете каждую, затем суммируете резюме. Последний паттерн, map-reduce.
Запрос к Crawlbase на markdown, а не на HTML, важнее, чем кажется. Markdown сохраняет заголовки, списки и структуру, убирая шум, поэтому модель тратит своё контекстное окно на смысл. Подробнее об этом выборе, в статье LLM-готовый markdown-скрапинг.
Crawling API может вернуть страницу как markdown, если передать format=markdown (или соответствующий параметр scraper). Всегда предпочитайте это сырому HTML для суммирования. Сырой HTML сжигает токены на тегах и инлайн-стилях, которые модели не нужны, а лишний шум ощутимо ухудшает качество резюме. Markdown сохраняет структуру, полезную модели, и убирает всё остальное.
Настройка проекта
Вам нужен Python 3 и два аккаунта: бесплатный аккаунт Crawlbase для токена и аккаунт OpenAI для модели. Сначала создайте аккаунт Crawlbase; вы получаете до 20 000 бесплатных API-запросов: 1 000 при регистрации и больше по мере прохождения шагов онбординга, этого достаточно, чтобы следовать руководству. Скопируйте Normal request token со страницы документации аккаунта и получите API-ключ OpenAI.
Затем создайте папку проекта и установите библиотеки.
python --version mkdir web-summarizer && cd web-summarizer python -m venv .venv && source .venv/bin/activate pip install requests openai tiktoken
Три зависимости выполняют работу: requests вызывает Crawling API, openai, клиент для модели, а tiktoken считает токены, чтобы знать, когда страница слишком велика для одного вызова. Задайте два секрета как переменные окружения, чтобы они не попали в код.
export CRAWLBASE_TOKEN="your_crawlbase_normal_token" export OPENAI_API_KEY="your_openai_api_key"
Шаг 1: получение страницы как чистого markdown
Начните со сбора. Вы отправляете Crawling API целевой URL и параметр format=markdown, и он возвращает страницу, уже преобразованную в markdown. Функция ниже оборачивает этот вызов, проверяет статус вышестоящего сервера и возвращает только тело markdown, чтобы остальная часть пайплайна никогда не видела сырой HTML.
import os import requests CRAWLBASE_TOKEN = os.environ["CRAWLBASE_TOKEN"] API_ENDPOINT = "https://api.crawlbase.com/" def fetch_markdown(url: str) -> str: params = { "token": CRAWLBASE_TOKEN, "url": url, "format": "markdown", } response = requests.get(API_ENDPOINT, params=params, timeout=90) response.raise_for_status() return response.text if __name__ == "__main__": markdown = fetch_markdown("https://www.crawlbase.com/blog/") print(markdown[:800])
Запустите, и вы получите текст статьи в виде markdown с сохранёнными заголовками и списками, при этом обёртка страницы уже убрана. Если цель рендерит контент с помощью JavaScript, тот же вызов работает с JavaScript-токеном вместо Normal, так что страница рендерится в реальном браузере перед конвертацией. Смените токен, и вы суммируете контент одностраничных приложений без каких-либо других изменений в коде.
Чистый текст на входе, хорошие резюме на выходе. Crawling API рендерит JavaScript, ротирует резидентные IP для обхода блокировок и возвращает каждую страницу в виде готового к суммированию markdown в одном вызове, так что модель никогда не видит навигационные панели или cookie-баннеры. Начните с бесплатного уровня и направьте его на любой публичный URL.
Шаг 2: суммирование короткой страницы в одном вызове
Когда страница комфортно умещается в контекстное окно модели, суммирование, это один запрос. Функция ниже принимает текст в markdown и краткую инструкцию, отправляет их в модель с низкой температурой для последовательности и возвращает строку с резюме. Низкая температура важна: вы хотите, чтобы один и тот же ввод давал стабильный вывод в разных запусках, а не творческое разнообразие.
from openai import OpenAI client = OpenAI() # reads OPENAI_API_KEY from the environment MODEL = "gpt-4o-mini" def summarize(text: str, instruction: str) -> str: prompt = f"{instruction}\n\n---\n\n{text}" response = client.chat.completions.create( model=MODEL, messages=[{"role": "user", "content": prompt}], temperature=0.2, ) return response.choices[0].message.content.strip() SUMMARY_PROMPT = ( "Summarize the following web page in 4-6 sentences. " "Lead with the main point, then the key supporting facts. " "Ignore navigation, ads, and boilerplate." ) if __name__ == "__main__": page = fetch_markdown("https://www.crawlbase.com/blog/") print(summarize(page, SUMMARY_PROMPT))
Это весь счастливый путь для обычной статьи. Получить markdown, отправить с инструкцией, вывести результат. Модель занимается языковой работой; Crawlbase занимался работой с данными. Единственное, что стоит между этим и страницей, которая не влезает, длина, что и является следующим шагом.
Шаг 3: обработка длинных страниц через нарезку на части
У каждой модели есть контекстное окно, жёсткое ограничение на объём текста, который она может прочитать за один вызов. Объёмные статьи, страницы документации и ветки форумов могут его превысить, и тогда API отклоняет запрос. Решение, разбить текст на части, каждая из которых умещается в окно, с небольшим перекрытием, чтобы предложение, разрезанное на границе, всё равно целиком присутствовало в одной из частей.
Для подсчёта токенов, а не символов, используйте tiktoken, поскольку ограничение измеряется в токенах. Функция ниже перебирает список токенов и нарезает его на окна фиксированного размера.
import tiktoken encoder = tiktoken.encoding_for_model("gpt-4o-mini") def chunk_text(text: str, max_tokens: int = 2000, overlap: int = 150): tokens = encoder.encode(text) chunks = [] start = 0 while start < len(tokens): end = start + max_tokens window = tokens[start:end] chunks.append(encoder.decode(window)) start = end - overlap return chunks
Каждая часть теперь является самодостаточным фрагментом текста, достаточно малым для самостоятельного суммирования. Значение max_tokens в 2 000 оставляет комфортный запас для промта и ответа внутри современного контекстного окна; уменьшите его для меньших моделей. Перекрытие не даёт потерять граничное предложение между двумя частями. При чистом markdown от Crawlbase на входе эти части содержат только контент, что снижает их количество и удерживает резюме по теме.
Шаг 4: объединение резюме частей через map-reduce
Нарезка даёт несколько частей; map-reduce превращает их обратно в один ответ. Паттерн состоит из двух фаз. В фазе map вы суммируете каждую часть независимо, получая список частичных резюме. В фазе reduce вы конкатенируете эти частичные резюме и суммируете их вместе в одно финальное. Если объединённые частичные резюме сами слишком длинны, вы снова редуцируете, повторяя до получения одного резюме.
MAP_PROMPT = ( "Summarize this section of a longer document in 3-4 sentences. " "Keep concrete facts, names, and numbers." ) REDUCE_PROMPT = ( "The following are summaries of consecutive sections of one document. " "Combine them into a single coherent summary of 5-7 sentences, " "removing repetition and keeping the overall narrative." ) def summarize_long(text: str) -> str: chunks = chunk_text(text) if len(chunks) == 1: return summarize(chunks[0], SUMMARY_PROMPT) partials = [summarize(c, MAP_PROMPT) for c in chunks] combined = "\n\n".join(partials) while len(encoder.encode(combined)) > 2000: partials = [summarize(c, MAP_PROMPT) for c in chunk_text(combined)] combined = "\n\n".join(partials) return summarize(combined, REDUCE_PROMPT)
Теперь эта единственная функция обрабатывает любую длину. Короткая страница делает один вызов и возвращает результат немедленно. Длинная страница проходит map, reduce и, если нужно, reduce снова, при этом цикл гарантирует, что финальный ввод всегда умещается. Разные промты для map и reduce важны: промт map запрашивает насыщенные фактами частичные резюме, чтобы детали выжили в первом проходе, а промт reduce запрашивает чистый нарратив, чтобы финальное резюме читалось как единое целое, а не сшитый список.
Шаг 5: запуск пайплайна для многих URL
Два этапа теперь объединяются в небольшой пайплайн. Передайте ему список URL, получите каждый как markdown, суммируйте каждый с помощью функции с учётом длины и соберите результаты. Оберните каждый URL в try/except, чтобы одна плохая страница не потопила весь пакет, и у вас есть что-то, что можно направить на поток страниц.
import json URLS = [ "https://www.crawlbase.com/blog/", "https://www.crawlbase.com/blog/ai-data-extraction-how-it-works/", ] def run_pipeline(urls): results = [] for url in urls: try: markdown = fetch_markdown(url) summary = summarize_long(markdown) results.append({"url": url, "summary": summary}) except Exception as error: print(f"Skipped {url}: {error}") return results if __name__ == "__main__": output = run_pipeline(URLS) print(json.dumps(output, indent=2))
Вывод, JSON-массив пар url и summary, готовый для записи в файл, отправки в базу данных или рендеринга в дайджест. Сокращённый пример того, что возвращается:
[ { "url": "https://www.crawlbase.com/blog/", "summary": "The Crawlbase blog covers web scraping, proxies, and data extraction, with hands-on tutorials for engineers. Recent posts focus on rendering JavaScript sites, avoiding blocks, and turning pages into clean structured data." }, { "url": "https://www.crawlbase.com/blog/ai-data-extraction-how-it-works/", "summary": "The article explains how AI models extract structured fields from messy web pages, contrasting rule-based scrapers with model-driven extraction that adapts to layout changes." } ]
Практические советы для продакшена
Кешируйте полученный markdown
Сбор и суммирование дают сбои по разным причинам, поэтому не связывайте их. Сохраняйте markdown каждой страницы на диск с ключом по URL в момент получения. Когда вы захотите перезапустить с другим промтом или моделью, вы суммируете из кеша вместо повторного сбора, это быстрее и не тратит API-кредиты на уже имеющиеся страницы.
Регулируйте темп и повторяйте запросы
Оба API могут ограничивать тесный цикл. Добавьте небольшую паузу между URL и оберните вызов модели в повторную попытку с откатом, чтобы временная ошибка не потеряла страницу. Crawling API управляет ротацией IP и разблокировкой за вас, поэтому необходимый здесь темп невысок, но всё равно стоит быть вежливым клиентом.
Фиксируйте промты и модель
Переиспользуемые промты под контролем версий, это то, что делает резюме последовательными в разных запусках. Держите map и reduce промты в одном месте, фиксируйте имя модели и держите температуру низкой. Когда что-либо из этого меняется, воспринимайте это как изменение вашего вывода, потому что это так и есть.
Подбирайте инструмент под страницу
Используйте Normal token для статических страниц и JavaScript token для одностраничных приложений, рендерящих контент в браузере. Если вы хотите получать структурированные данные, цену, название, рейтинг, а не прозу, используйте Crawling API для получения структурированного JSON, а затем суммируйте его. Если вы встраиваете это в агента или рабочий процесс на основе MCP, Web MCP открывает те же возможности сбора и извлечения вашей модели как инструменты. Для полноценной агентной сборки статья создание AI-пайплайна данных с LangChain и Crawlbase развивает эту тему дальше.
Ключевые выводы
- Разделяйте сбор и суммирование. Crawlbase получает чистый текст; модель занимается языковой работой. Разделение двух этапов делает пайплайн поддерживаемым.
-
Получайте markdown, а не HTML. Передавайте
format=markdown, чтобы модель тратила контекст на контент, а не на навигационные панели и скрипты. -
Считайте токены, затем нарезайте. Используйте
tiktokenдля разбивки длинных страниц на перекрывающиеся части, каждая из которых умещается в контекстное окно. - Map-reduce масштабируется до любой длины. Суммируйте каждую часть, затем суммируйте резюме, снова редуцируя до получения одного.
- Кешируйте и фиксируйте для продакшена. Сохраняйте полученный markdown, ведите версионирование промтов, фиксируйте модель и держите температуру низкой для стабильного вывода.
Часто задаваемые вопросы
Зачем получать markdown вместо сырого HTML для суммирования?
Сырой HTML полон тегов, скриптов, инлайн-стилей, навигации и рекламы, которые не несут смысла для резюме, но всё равно стоят токены. Crawling API может вернуть страницу как markdown, который сохраняет заголовки, списки и основной текст, убирая шум. Это означает, что модель тратит своё ограниченное контекстное окно на реальный контент, резюме получаются чище, и вы платите за меньше токенов на страницу.
Что такое map-reduce суммирование и когда оно нужно?
Map-reduce, это двухфазный паттерн для текста, слишком длинного для одного вызова модели. В фазе map вы суммируете каждую часть документа самостоятельно; в фазе reduce вы суммируете эти частичные резюме вместе в один финальный ответ. Он нужен всякий раз, когда страница превышает контекстное окно модели. Короткие страницы переходят прямо к единственному вызову, что и объясняет, почему в примере сначала проверяется количество частей.
Как выбрать размер части?
Задавайте размер части в токенах, а не в символах, и оставляйте запас для промта и ответа. Часть в 2 000 токенов хорошо работает на современных моделях с большими контекстными окнами; уменьшите для меньших моделей. Добавьте небольшое перекрытие, около 100–200 токенов, чтобы предложение, разрезанное на границе части, всё равно целиком присутствовало в одной из них. Считайте токены через tiktoken, используя ту же кодировку, что и ваша модель.
Можно ли суммировать JavaScript-рендеримые страницы?
Да. Замените Normal token на JavaScript token в вызове Crawling API. Он рендерит страницу в реальном браузере перед конвертацией в markdown, так что контент одностраничных приложений присутствует, когда модель его читает. Остальная часть пайплайна, нарезка и map-reduce, вообще не меняется.
Нужен ли платный аккаунт Crawlbase или OpenAI для следования этому руководству?
Нет. Crawlbase даёт вам до 20 000 бесплатных API-запросов: 1 000 при регистрации и больше по мере прохождения шагов онбординга, этого достаточно для тестирования пайплайна от начала до конца. Использование OpenAI тарифицируется по токенам, и небольшая модель вроде gpt-4o-mini делает суммирование дешёвым. Оба подходят для начала на бесплатных или недорогих уровнях перед масштабированием.
Можно ли использовать другую модель или провайдера?
Да. Пайплайн зависит от модели только в двух аспектах: вызов в стиле чата, принимающий промт и возвращающий текст, и счётчик токенов для нарезки. Замените клиент в функции summarize на любого предпочтительного провайдера и обновите кодировку tiktoken в соответствии с моделью. Этап сбора и логика map-reduce остаются ровно такими же.
Обходите любой сайт в масштабе, без борьбы с инфраструктурой.
Crawlbase берёт на себя прокси, отпечатки и CAPTCHA, чтобы ваша команда выпускала конвейеры данных вместо поддержки обвязки краулинга. 1 000 запросов бесплатно, без карты.

