Привет! Давно ничего не писал. Обещанного в прошло статье бота закончил, проект оказался довольно сложным, но опыта и знаний заметно прибавилось. А значит время начать новый проект! В этот раз, вдохновившись глубочайшими мыслями японских поэтов-философов, мы будем делать бота, который не просто постит хокку, а сам пишет и подбирает картинку по теме. Строго говоря, бот не придумывает хокку, а формирует новые из уже существующих, но хуже, как мне кажется, он от этого не становится. Итоговый код я оставлю на своём GitHub, а за работой бота можно следить в этом Телеграм канале. Подпишитесь, очень хочу, чтобы этот канал набрал аудиторию.
Готовый результат
Как это работает?
Первым делом нужно создать базу хокку, из которой наш бот будет брать строчки своих стихов. Для этого пришлось изучить несколько сотен хокку и выбрать подходящие. Вставляем всё в текстовый документ, чтобы у нас всегда была возможность добавить строки. Всего 1903 строки, так как хокку всегда состоят из трёх строк, то такого набора хватит на примерно столько вариантов:
Как вы уже могли догадаться, наш бот будет брать 3 случайных строки из списка и формировать из них хокку. Казалось бы, должно получиться нечто совсем бессмысленное, но учитывая специфику жанра, даже самые бредовые сочетания будут иметь (очень глубокий) смысл. С именами авторов мы поступим таким же образом, а год будем генерировать случайный от 0 до 2022 года (н.э или до н.э).
Код
В итоге у нас получилась такая функция:
def hokky_bot():
f = open('hokky.txt', 'r', encoding='UTF-8') # Открываем файл с хокку
all_hokky = f.read().split('\n') # Записываем каждую строчку в отдульный элемент списка
f.close()
f = open('names.txt', 'r', encoding='UTF-8') # То же самое для файла с именами
all_names = f.read().split('\n')
f.close()
j = 0
print('Power on!')
while j < 10000:
a = randint(0,1) # генерируем случайное число для вставки н.э или до н.э.
if a == 1:
era ='до н.э.'
else:
era = 'н.э'
i =0
name = [1, 2, 3]
text = [1, 2, 3]
while i<=2:
name[i] = all_names[randint(1, len(all_names)-1)] # Формируем списки из 3 строчек хокку и 3-х имён
text[i] = all_hokky[randint(1, len(all_hokky)-1)]
i += 1
message = (f'{text[0]}\n{text[1]}\n{text[2]}\n\n - {name[0].title()} {name[1].title()} {name[2].title()}, {randint(0, 2022)} г. {era}')
j += 1
search = text[randint(0,2)]
print(f'Японская живопись {search}')
picture(message, search)
time.sleep(randint(28800, 57600))
Про последние 4 строки подробнее далее.
Теперь наш бот способен выводить хокку, если прописать message внутрь bot.send_message. Примерно так:
Но так как нам этого мало, давайте научим бота отправлять нам картинку, желательно в той же стилистике и с соответствующей тематике.
Как вариант, можно было бы найти несколько изображений руками и выводить их в случайном порядке. Но так нам не получится сделать так, чтобы картинки соответствовали по смыслу тексту хокку. Поэтому мы будем искать (парсить) наши картинки в поиске Яндекса. На помощь нам тут приходит библиотека request и волшебный алгоритм, написанный давно моим близким другом.
def request_photo(message):
req = requests.get("https://yandex.ru/images/search?text="+message)
ph_links = list(filter(lambda x: '.jpg' in x, re.findall('''(?<=["'])[^"']+''', req.text)))
ph_list = []
for i in range(len(ph_links)):
if (ph_links[i][0] == "h"):
ph_list.append(ph_links[i])
del ph_links
return ph_list[randint(0, len(ph_list))]
Итак, ищем картинки в поиске, извлекаем ссылку из кода страницы и вуаля, бот вставляет случайную картинку. Чтобы содержание её соответствовало нашим стихам, посылаем поисковой запрос в форме 'Японская живопись + {строчка из хокку}'. То есть в 28-й строке функции hokky_bot м посылаем такой запрос в функцию picture (о ней далее), откуда мы отправляем наш запрос в функцию request_photo.
Штош, нам удалось сделать, чтобы бот выводил то что нам нужно.
Такой результат меня уже более чем устраивает, но как насчёт сделать из картинки полноценное изображение, содержащее хокку. Это позволит нам убрать текст из постов и улучшить общий вид изображений. Для этого нам и нужна функция picture.
def picture(message, search):
# Код для вставки своего хокку в изображение из request_photo
im = requests.get(request_photo('японcкая живопись'))
out = open("img.jpg", "wb")
out.write(im.content)
out.close()
image = Image.open('img.jpg')
# Создаем объект со шрифтом
font = ImageFont.truetype('font.name', size= int(image.width/15))
draw_text = ImageDraw.Draw(image)
draw_text.text(
(int(image.width/50), int(image.height/4)),
message,
# Добавляем шрифт к изображению
font=font,
fill='#d60000') # Цвет
В этом варианте решения мы записываем найденный запрос в файл img.jpg, после чего уже открываем для редактирования (библиотека Pillow), куда вставляем наш message.
В итоге после настройки и подбора разных шрифтов, лучшее что у меня вышло выглядит так:
Но тут уже не угадаешь, какое у нас будет изображение, тёмное или светлое, а фильтры и т.п. вставлять не хочется. Поэтому этот вариант мы оставим для других проектов, а пока довольствуемся результатом.
Всё! Ещё раз напоминаю, код проекта можно найти на GitHub.
Телеграм канал с хокку. Подпишитесь, мне будет очень приятно.
Если есть идеи, как можно доработать бота, делитесь в комментариях. Всем пока, до встречи в новых статьях!
Комментарии (9)
NickCave
02.05.2022 09:21+2Помимо правила трёх строк у хокку есть ещё правило размерности каждой строки 5 7 5 (слогов), оно не всегда четко соблюдается, но по крайней мере вторая строка должна быть больше первой и последней.
Mingun
02.05.2022 10:07+4Текст на картинках совершенно нечитаемый. В чём проблема вставить простейшую обводку чёрным цветом на 1-2 пикселя, чтобы гарантированно отделить текст от фона?
DWM
03.05.2022 20:01+1Всего 1903 строки, так как хокку всегда состоят из трёх строк, то такого набора хватит на примерно столько вариантов
Там не может быть 3 в степени 1900, так стих состоит из 3х строк по 2-5-7 слов, а не из 1900 строк по 3 варианта в каждой. Максимум 1900 * 1899 * 1898 (строки не могут повторяться).
Да и идея с добавлением автора делает хуже -- дезинформация.
moscow_intelligent
04.05.2022 08:12+1Технически, никто не научил бота писать хокку. Он просто берет рандомные строчки и постит их под рандомными именами.
Признаться честно, кликнув на эту статью я ожидал что то про ML и DS. А так, это просто визуализация рандома, как уже подметил один из комментаторов. Для начинающего это хорошо, для Хабра, сомнительно.
drdead
Простите, но, наверно, это всё же хайку, а не хокку. Эксперты могут меня поправить, если не прав.