mirror of
https://gitverse.ru/kpa39l/vesti.git
synced 2026-09-29 09:55:03 +00:00
6.6 KiB
6.6 KiB
Proposal: rss-crawler
Why
В VESTI сейчас есть только telegram_crawler (Telethon, MTProto). RSS-источники — самый массовый и дешёвый класс источников (по оценке PRD — ~80% объёма новостей): веб-СМИ, блоги, IT-порталы публикуют RSS/Atom-ленты. Без RSS-краулера они недоступны.
Проблемы, которые решает модуль:
- нет спроса на RSS вовсе: в источники (sources.yaml) нельзя добавить ни одного
RSS-канала — краулер просто не умеет их обрабатывать (
get_enabled_sources(crawler="rss")вернёт пусто); - негражданный режим опроса: без If-None-Match/If-Modified-Since каждый запуск тянет полные ленты — это лишний трафик для издателей и для нас;
- мусорные ссылки: URL из лент часто содержат UTM-метки, якоря, трекеры — дедупликация по сырому URL даёт дубли одной статьи;
- обрывки вместо текста: многие ленты отдают summary вместо полного текста — без дотягивания полного текста карточки кандидатов бедные;
- нет состояния/здоровья: нет etag/modified/last_error/status для RSS-источников.
Для пользователя это значит: можно добавить в VESTI новостные сайты/блоги и получать их посты в конвейере (классификация → кандидаты → публикация) наравне с Telegram-каналами.
What Changes
- Новый модуль краулера
crawler/rss_crawler.py(feedparser + httpx):- читает
sources.yaml(crawler: rss), группирует по priority; - HTTP GET c
If-None-Match/If-Modified-Sinceиз таблицыrss_state→ 304 = пропуск; - нормализует URL (UTM-параметры, якоря) →
canonical_url, дедуп по sha256; - извлекает полный текст через trafilatura, если запись — обрывок;
- пишет посты в
postsчерез существующийstore_posts-механизм (дедуп по sha256 текста); - пишет метаданные ленты в
rss_state(etag/modified/last_error/status, last_build_date); - ведёт
runs(source_id, task, trigger) — как telegram_crawler; - CLI:
python -m crawler.rss_crawler --all | --direction | --source <slug>.
- читает
- Новая таблица
rss_state(etag, modified, last_error, status, last_build_date) — аналогtg_stateдля RSS-лент. requirements.txt: + trafilatura (извлечение полного текста).sources.yaml: первый реальный RSS-источник для проверки — LWN (lwn.net) — и один закомментированный пример (opennet.ru)..env.example: +RSS_USER_AGENT(информативный User-Agent).- Cron (Hermes):
vesti-rss-crawler(каждые 15 мин, P0) — тихий, no_agent, как telegram.
Не меняется
- Дедуп и схема
posts— как у telegram_crawler (sha256 текста; url — дополнительный ключ). - Классификатор, веб, publisher — не затрагиваются (RSS-посты идут в тот же
posts). tg_state— остаётся для Telegram; RSS используетrss_state.- Никаких автопубликаций: посты попадают в кандидаты, подтверждение — человеком.
Capabilities
New Capabilities
rss-crawler: Синхронный краулер RSS/Atom-лент: реестр из sources.yaml, условные GET (etag/modified), нормализация URL, извлечение полного текста (trafilatura), состояние и здоровье ленты в БД, интеграция с runs/posts.
Modified Capabilities
news-store(таблицаposts): без изменений схемы; RSS-посты используют её как есть.sources.yaml: новые источники сcrawler: rss.vesti-web: без изменений (RSS-посты автоматически видны как кандидаты).
Impact
- Файлы:
- новый:
crawler/rss_crawler.py(модуль-краулер); - изменён:
db/schema.sql(+CREATE TABLE rss_state),requirements.txt(+ trafilatura),sources/sources.yaml(+lwn, пример opennet),.env.example(+RSS_USER_AGENT),config.py(+RSS_USER_AGENT, +RSS_*); - доки: STATUS.md, TODO.md, WALKTHROUGH.md, AGENT.MD (команда запуска).
- новый:
- Данные: миграция —
CREATE TABLE IF NOT EXISTS; существующие данные не трогаются. - Секреты: не требует (RSS публичные). User-Agent — из .env.
- Сеть: исходящие HTTP-запросы к сайтам издателей (только к лентам, учтиво: условные GET, рейт-лимит 1–2 с между источниками).
- Rollback: модуль не вызывается cron — просто не добавлять источники/не запускать.
Risks
- Издатели режут по User-Agent / отдают капчу — лечится
RSS_USER_AGENT(честный, с контактом) иenabled: falseдля проблемных. - Ленты с кривой датой (
published_parsedNone) — ставимfetched_at, не падаем. - summary-only ленты — trafilatura может не найти полный текст; тогда сохраняем summary, пост всё равно попадает в конвейер (пометка в content_type/text).
- Огромные ленты (мега-фиды): ограничение — первые N записей, остальные догонятся следующими запусками (инкрементально), как в telegram_crawler.
- Троттлинг издателя: проставляем last_error и status='dead' после N=5 ошибок подряд (правило фид-здоровья), watchdog молчит до перехода alive↔dead.