Контент – самое главное скажет любой редактор медиа. В этой статье мы рассказываем, как написали нейросеть-парсер, который пишет для нас новости.
Посмотреть, как он работает – можно тут. Ссылка открытая.
Не призываем подписываться на канал. Но зачем вкладывать только скрины в статью, если можно увидеть работу нейронки вживую на самом канале.
В это статье рассказываю:
Контентщик против нейросети: собираем парсер правильно
Учим ChatGPT правильно писать посты: крупный лайфхак по промтам
Что у нас получилось?
Я уверен, многие Хабровчане будут недовольны: «Вы плодите мусорный контент из новостей Туризма!» — дай бог, чтобы людям стал интересен туризм, мы бы начали писать авторский контент с обзорами винных городов с дизайном Лебедева или расценки горнолыжного Шерегеша.
Я бы сам писал обзоры, советовался с архитектором по поводу новой планировки винного города в Геленджике без деревьев и теней, офигевал от цен на отели горнолыжных курортов. Но это всё никому не нужно.
В сфере туризма откликаются некоторые из тем обычной прессы: ненавистные ограничения гнилой Европы и «Омерики», возможности получения ВНЖ и виз, розыгрыши Грин‑Карт, смешные видео с обезьянами из Тайланда, выходки пьяных пассажирок на внутренних рейсах, фатальные ошибки пилотов из S7, штрафы и аресты.
И, конечно же, красивые места: Аргентина, Тбилиси, Тай…
Красивые места — это всегда про помечтать, отвлечься от серого мира Челябинска за окном 64 автобуса. А провокационные новости — это провокационные новости…
Привлечение действительно путешествующей аудитории могло выходить за границы нашего бюджета (примерно в три раза), охваты на той стороне держались идентично, рекламные интеграции выходили в меньшую цену. Бизнес есть бизнес.
Мы привлекали женскую аудиторию. Надежд на рекламирование премиальных автомобилей не было, как в некоторых каналах с потенциально «богатой аудиторией», но мы бы и не потянули по бюджету.
Хуже обстояло дело, что мы в погоне за дешевым подписчиком из‑за байт‑постов получали не целевого рафового релоканта — женщину‑сварщицу с местного предприятия или хуже — пенсионерку.
Новости любят читать все — на них и делалась ставка.
Так как канал не особо большой, то экономия на контентщике существенная и делает прибавку в 20–30% к чистой прибыли. Поэтому я решил написать бота, который бы компилировал новости и давал бы некоторую оценку и комментарий.
Постараюсь не добавлять сюда слишком много кода, а сориентироваться на теории и конкретном результате.
Возможно, после консультации с кодером и доведения бота до ума — выложу на Гитхаб или продам конкурентам.
Суть таких парсеров — они позволяют собирать новости с огромного числа источников за полчаса, пока бедный контентщик накопает лишь несколько топ‑новостей из каналов конкурентов.
На самом деле, писать интересные комментарии под новостями, как то делают в тг‑каналах по маркетингу — топ для поднятия ERR. Но в нашей тематике «авторский» контент не работает. Поэтому нам достаточно минимально «живого» комментария в конце поста.
Проблемы:
Нужны не все новости. А агрегаторы, даже платные с настройками — это чаще про выбор тематики. Нам неинтересны новости про поднятие стоимости визы на 2 доллара в Италию — нам интересен полный запрет на визы в Италию.
Нужна правильная компоновка новости. Нужны новости по ключевым словам: «неадекватный пассажир», «катастрофа», «бесплатные», «льготы и так далее»
Нужно предварительно собрать список агрегаторов, где таких новостей больше всего.
Помимо новостей — нужны изображения к постам. Иногда для новости видео или изображение важно — иногда достаточно рандомной «пикчи» с Пинтереста.
Источниками могут послужить RSS‑ленты крупных новостных сайтов: BBC, CNN, а также cпец. туристических порталов вроде Skift. Дополнительно можно подключить API новостных сервисов: Google News API или Bing News API. Плюс крупных агрегаторов — фильтры.
Cюда можно подключить агрегаторы социальных сетей. Но всегда лучше парсинг отдельных твиттер и инстаграм аккаунтов — наилучший вариант, так как самые важные и свежие посты заливаются туда. Особенно, видео с «бибизьянами» и все остальное.
Тут есть некоторая проблема:
Настройка по ключевым словам. Это сложно, ведь словарь должен содержать такие ключевые слова, которые бы могли пересекаться.
Для этого составляется SEO‑словарь, разделенных по категориям. Например, к категории «инциденты» можно отнести такие слова, как «неадекватный пассажир», «конфликт», «скандал». Для экономии — «бесплатные», «льготы».
Совет: на самом деле, лучше написать несколько парсеров для сбора конкретных новостей в одной рубрике. Предположим, рубрик всего 7: бесплатное, авиапроисшествия, новые ограничения, красивые места и так далее.
Почему много узкоспециализированных парсеров лучше, чем один универсальный? – универсальность – это сложно.
Например.
Мы знаем: основную информацию National Geographic выписывает в «лиде», первом абзаце. Это их редакционная политика. Лишние данные — медвежьи данные для нейросети, так как в одном посте может появиться: «самолет был красный» с «самолет разбился». Второстепенное соседствует с главным.
Нейросеть не до конца понимает, что важно в тексте, а что нет. Нет смысла парсить всю страницу, если можно получить сразу всю выжимку.
Но в журнале Discovery другая ситуация. В лиде — только «водянистое» приветствие.
Желательно создать несколько функций с разной сортировкой новостей в зависимости от источников, рубрикаций и типов собираемого контента. Один парсер на одну рубрику — дичь.
Один из парсеров, например, у нас под красивые места с коротким описанием. Тут нужно парсить не только текст, но и медиа.
Я нашел источник, откуда можно взять и видео, и описание к месту сразу.
Используя API или регулярные выражения, можно вычленять только те записи, которые содержат необходимые фразы.
Пример кода, реализующего данный подход с использованием FeedParser, может выглядеть следующим образом:
import feedparser
# Список RSS-источников (список может пополняться)
rss_feeds = [
"https://www.bbc.com/news/rss.xml",
"https://skift.com/feed/"
]
# Ключевые слова (нужно подобрать, как можно больше ключей)
keywords = ["неадекватный пассажир", "катастрофа", "бесплатные", "льготы"]
# Фильтруем функцию (заголовок/ссылка/описание)
def fetch_relevant_news(feeds, keywords):
relevant_news = []
for feed_url in feeds:
feed = feedparser.parse(feed_url)
for entry in feed.entries:
if any(keyword.lower() in entry.title.lower() or keyword.lower() in entry.summary.lower() for keyword in keywords):
relevant_news.append({
"title": entry.title,
"link": entry.link,
"summary": entry.summary
})
return relevant_news
filtered_news = fetch_relevant_news(rss_feeds, keywords)
# Выводим результат
for news in filtered_news:
print(f"Заголовок: {news['title']}\nСсылка: {news['link']}\nОписание: {news['summary']}\n")
Следующим этапом является обработка собранных данных, включая очистку от HTML-кода и приведение текста к читаемому виду.
Это можно реализовать с помощью библиотеки BeautifulSoup для удаления нежелательных элементов: теги, скрипты и стили. Например:
from bs4 import BeautifulSoup
# Очистка HTML
def clean_html(raw_html):
soup = BeautifulSoup(raw_html, 'html.parser')
return soup.get_text()
for news in filtered_news:
news["summary"] = clean_html(news["summary"])
Это слегка упрощенный формат парсинга.
Создаем несколько словарей под рубрики с ключевыми словами. Используем агрегаторы ключевых слов: Яндекс‑Вордстат или Keyword.tool.
Лучше всего собрать SEO самостоятельно или через нейросеть после загрузки подходящих новостей в PDF‑файл и прогонки через нейросеть. Поверьте, эти SEO вам еще понадобятся для создания авторского контента.
Пишем несколько функций с индивидуальными фильтрами и принципом парсинга. Разный тип контента, редакционная политика источников, формат — все это нужно учесть, иначе конечный контент превратится в суп.
Очищаем данные от примесей.
Мы привели объяснение простого парсера, который мы использовали в самом начале. Наш совет: ориентироваться на узкоспециализированные источники и писать парсеры конкретно под них.
Писать словари с ключевыми словами, оптимизировать фильтры и финальную очистку — все под источники, а не тип новостей — так конечные новости и тип текста будет ближе к желаемому. Еще лучше — собрать несколько парсеров под сбор конкретных рубрик в конкретных источниках.
Так вы будете получать самые релевантные результаты. Как говорится: подготовка данных — самое важное в обучении.
Предположим, что таким образом мы получаем 50 новостей в день, разбитых по рубрикам и источникам.
Внушительнаячасть «нейровизионеров» записывает килотонны туториалов по написанию промтов… Это настоящий мрак.
Большинство промтов выглядит как‑то так:
«Преврати следующий текст в провокационный пост для социальной сети. Используй резкий стиль, добавь эмоции, сделай заголовок, который привлекает внимание. В конце добавь комментарий от автора с выражением сильного мнения».
ChatGPT хорошо реагирует на структурированные инструкции. И их нужно приводить, но их роль — чисто косметическая (сократить заголовок, разбить на абзацы, убрать вводные конструкции). Но главное — задание контекста.
КОН‑ТЕКСТ. Нужно задать определенную семантическую фактуру для нейронки, чтобы она правильно подбирала комментарии к постам, преобразовывала их, давала интересный заголовок. Это связано с принципом работы самого GPT.
Вот примерное объяснение: GPT или трансформер (как его называют в мире нейросетей и программирования) — это огромная сетка весов, значимостей одних слов для других, одних текстовых конструкций для других. В GPT встроен механизм «внимания», который выбирает, как одни слова относятся к другим.
Хочешь, чтобы GPT строчило как Довлатов? — покажи, а не приводи инструкции.
Поэтому промты должны предполагать включение собранных вами SEO, внедрение стиля письма под конкретные ситуации.
Для создания нейросети, которая будет преобразовывать спаршенные данные в отформатированные посты, используя API ChatGPT, необходимо грамотно организовать весь процесс от подготовки данных до вызова модели и интерпретации её ответа.
У нас есть уже очищенные и структурированные данные, содержащие заголовок, описание, ключевые слова и ссылку на источник. А также необходимый набор SEO, промтов.
Совет: и тут для каждой рубрики следует собрать свои промты с предварительной загрузкой SEO, жаргонизмов, сленга — необходимо словарь из типов высказываний и слов для разных ситуаций. Так мы уникализируем контекст, стиль письма и разнообразим контент.
Из структуры: сформировать провокационный заголовок, уникализировать текст и добавить авторский комментарий.
Первым шагом является проектирование запроса (prompt), который будет отправляться в API.
Далее реализуем интеграцию через API. Используя Python и библиотеку openai, создаем запрос. Предположим, данные из парсера уже сохранены в JSON‑формате.
Пример кода:
import openai
# Вводим ключик
openai.api_key = "ключ_API"
# Пример данных (конечно, саммари может быть длиннее от заданных параметров)
news_item = {
"title": "Авиакомпания ввела новые правила перевозки багажа",
"summary": "Теперь каждый пассажир может провозить только одну единицу ручной клади.",
"keywords": ["багаж", "новые правила", "авиакомпания"],
"link": "https://example.com/news/123"
}
# Формирование запроса для нейросети
def generate_post(data):
prompt = f"""
Преврати следующий текст в провокационный пост:
Заголовок: {data['title']}
Описание: {data['summary']}
Ключевые слова: {", ".join(data['keywords'])}
Проанализируй, является ли новость положительной. Проработай описание, чтобы оно выглядело уникальным. Задействуй keywords, используй (сюда выписываем список жаргонизмов), “прокомментируй”.
"""
response = openai.ChatCompletion.create(
model="gpt-4",
messages=[{"role": "user", "content": prompt}],
max_tokens=300,
temperature=0.8 # Увеличиваем креативность
)
return response["choices"][0]["message"]["content"]
# Генерация поста
post = generate_post(news_item)
print(post)
Обратите внимание на параметр temperature. Значение 0.8 позволяет модели выдавать более нестандартные, креативные ответы, что идеально для задач, требующих создания, например, провокационных заголовков.
Так как мы загрузили нейронку нужным SEO и контекстом — ответы будут интересными, но не уходить куда‑то в сторону.
После вызова API нейросеть возвращает структурированный текст.
Следующий шаг — постобработка данных для интеграции в наш супер‑проект.
Для повышения уникальности текста, если это требуется дополнительно, можно использовать параметр temperature совместно с top_p, что позволяет нейросети выбирать менее очевидные слова и фразы.
Пример обновленного вызова:
response = openai.ChatCompletion.create(
model="gpt-4",
messages=[{"role": "user", "content": prompt}],
max_tokens=300,
temperature=0.9,
top_p=0.85 # Балансируем между креативностью и правдоподобием
)
Для полного цикла автоматизации результат можно сохранять в базу данных или передавать напрямую в API социальной сети.
Например, с использованием библиотеки requests для взаимодействия с Telegram Bot API. Это позволяет сразу публиковать посты в канал или группу. Тогда следует ограничить количество новостей для парсинга до, например, четырех.
Но мы советуем уделять 5 минут времени и отсеивать новости самостоятельно. Ведь основа новостника — это сами новости, а не их «текстовая» обработка.
Такой подход объединяет парсинг, генерацию контента и публикацию в единую систему, минимизируя необходимость ручного вмешательства.
Итого:
Примерно 5 из 7 рубрик превосходно работают. Удалось найти удачный контекст и ключевые слова, чтобы сформировать почти авторский новостной контент. Читать не так скучно.
Остальные две рубрики постоянно требуют коррекции.
На разработку бота потрачено часов 10, сэкономлено за все существование канала около 150 тысяч.
Если грамотно составить парсеры на отдельные источники, грамотно подобрать SEO и подготовить ИИ можно добиться результатов уровня “авторского текста”.
Безусловно, сюда не подойдут каналы о маркетинге, где часто фигурирует видео – его описать ИИ не сможет. К сожалению, нейросети не могут выдавать рассуждения на достойном уровне.
Но любая экономическая новость всегда может быть разжевана при помощи чужих рассуждений на основе теории Хайека, например. Важно, чтобы в материале была фактура, от которой GPT смог бы оттолкнуться.
Если хочется посмотреть, как работает бот – ссылка на канал. Некоторые посты авторские. Но вы их сразу заметите.