Рубрики
Без рубрики

Создание облаков новостных слов с использованием Python и Reply.it

Пошаговое руководство по созданию веб-приложения с использованием Python, Flask и Reply.он очищает последние новости, преобразует статьи в облака слов и отображает их на простой странице.

Автор оригинала: Gareth Dwyer.

Примечание: этот учебник представляет собой отрывок из Код с Reply.it: Проекты на Python для начинающих , книга и набор учебных пособий для начинающих, чтобы получить практический опыт программирования на Python.

Облака слов, которые представляют собой изображения, показывающие разбросанные слова разных размеров, являются популярным способом визуализации больших объемов текста. Слова, которые чаще встречаются в данном тексте, больше, а менее распространенные слова меньше или вообще не отображаются.

В этом уроке мы создадим веб-приложение с использованием Python и Flask, которое преобразует последние новости в облака слов и отображает их нашим посетителям.

В конце этого урока наши пользователи увидят страницу, похожую на показанную ниже, но содержащую последние заголовки новостей из BBC news. По пути мы узнаем некоторые хитрости о веб-очистке, RSS-каналах и создании файлов изображений непосредственно в памяти.

**Изображение: 1**

Обзор и требования

Мы будем создавать простое веб-приложение шаг за шагом и подробно объяснять каждую строку кода. Чтобы следовать этому, вы должны обладать некоторыми базовыми знаниями о программировании и веб-концепциях, например, о том, что такое операторы if и как использовать URL-адреса. Мы будем использовать Python для этого урока, но мы не будем предполагать, что вы являетесь экспертом по Python.

В частности, мы будем:

  • Посмотрите на RSS-каналы и как их использовать в Python
  • Покажите, как настроить базовое веб-приложение Flask
  • Используйте BeautifulSoup для извлечения текста из новостных статей в Интернете
  • Используйте облако Word для преобразования текста в изображения
  • Импортируйте Bootstrap и добавьте некоторые базовые стили CSS

Мы будем использовать онлайн-среду программирования Reply.it таким образом, вам не нужно будет устанавливать какое-либо программное обеспечение локально, чтобы следовать шаг за шагом. Если вы хотите адаптировать это руководство к своим собственным потребностям, вам следует создать бесплатную учетную запись, перейдя на reply.it и следить за процессом регистрации.

Соскабливание паутины

Ранее мы рассматривали базовую очистку веб-страниц во введении к очистке веб-страниц . Если вы совершенно новичок в идее автоматического извлечения контента из Интернета, сначала ознакомьтесь с этим учебником.

В этом уроке вместо того, чтобы удалять ссылки на новостные статьи непосредственно с домашней страницы BBC, мы будем использовать RSS – каналы – старый, но популярный стандартизированный формат, который публикации используют, чтобы информировать читателей о появлении нового контента.

Взгляните на RSS-каналы

RSS-каналы публикуются в виде XML-документов. Каждый раз, когда BBC (и другие места) публикуют новую статью на своей домашней странице, они также обновляют XML-машиночитаемый документ по адресу http://feeds.bbci.co.uk/news/world/rss.xml. Это довольно простой канал, состоящий из элемента , который имеет некоторые метаданные, а затем список элементов , каждый из которых представляет новую статью. Статьи расположены в хронологическом порядке, с самыми новыми в верхней части, так что легко получить новый контент.

**Изображение: 2**

Если вы нажмете на ссылку выше, вы не увидите XML напрямую. Вместо этого у него есть некоторая связанная информация о стиле, так что большинство веб-браузеров будут отображать что-то более дружелюбное к человеку. Например, при открытии страницы в Google Chrome отображается следующая страница. Чтобы напрямую просмотреть необработанный XML, вы можете щелкнуть правой кнопкой мыши на странице и выбрать “просмотреть исходный код”.

**Изображение: 3**

RSS-каналы используются внутри программного обеспечения, такого как программа чтения новостей Feedly и различные почтовые клиенты. Мы будем использовать эти RSS-каналы с библиотекой Python для извлечения последних статей из BBC.

Настройка нашей онлайн-среды (Reply.it)

В этом уроке мы будем создавать наше веб-приложение с помощью Repl.it , что позволит нам иметь согласованный редактор кода, среду и структуру развертывания в один клик. Отправляйтесь туда и создайте учетную запись. Выберите создать репл Python, и вы увидите редактор, в котором можно писать и запускать код Python, подобный изображению ниже. Вы можете написать код Python в средней панели, запустить его, нажав зеленую кнопку “выполнить”, и увидеть выходные данные в правой панели. В левой панели вы можете увидеть список файлов с main.py добавлено туда по умолчанию.

**Изображение: 4**

Извлечение данных из нашей ленты и извлечение URL-адресов

В предыдущем уроке по веб-очистке мы использовали BeautifulSoup для поиска гиперссылок на странице и их извлечения. Теперь, когда мы используем RSS, мы можем просто проанализировать ленту, как описано выше, чтобы найти эти же URL-адреса. Для этого мы будем использовать библиотеку Python feedparser .

Давайте начнем с того, что просто распечатаем URL-адреса всех последних статей Би-би-си. Переключитесь обратно на main.py файл в Reply.it УМРИТЕ и добавьте следующий код.

import feedparser

BBC_FEED = "http://feeds.bbci.co.uk/news/world/rss.xml"
feed = feedparser.parse(BBC_FEED)

for article in feed['entries']:
    print(article['link'])

Feedparser выполняет большую часть тяжелой работы за нас, поэтому нам не нужно слишком близко подходить к немного громоздкому формату XML. В приведенном выше коде мы анализируем ленту в хорошее представление Python (строка 4), перебираем все записи | (записи из XML, которые мы рассматривали ранее) и распечатываем элементы link .

Если вы запустите этот код, вы увидите несколько десятков URL-адресов, выводимых на правой панели, как показано на рисунке ниже.

**Изображение: 5**

Настройка веб-приложения с помощью Flask

Мы не просто хотим распечатать эти данные в консоли Repl. Вместо этого наше приложение должно возвращать информацию любому, кто использует веб-браузер для посещения нашего приложения. Поэтому мы установим легкий веб-фреймворк Flask и будем использовать его для предоставления веб-контента нашим посетителям.

В main.py файл, нам нужно изменить наш код, чтобы он выглядел следующим образом:

import feedparser
from flask import Flask

app = Flask(__name__)

BBC_FEED = "http://feeds.bbci.co.uk/news/world/rss.xml"

@app.route("/")
def home():
    feed = feedparser.parse(BBC_FEED)
    urls = []

    for article in feed['entries']:
        urls.append(article['link'])
    
    return str(urls)
        

if __name__ == '__main__':
    app.run('0.0.0.0')

Здесь мы по-прежнему анализируем ленту и извлекаем все последние URL-адреса статей, но вместо того, чтобы распечатывать их, мы добавляем их в список ( URL-адреса ) и возвращаем их из функции. Интересными частями этого кода являются:

  • Строка 2 : мы импортируем колбу
  • Строка 4 : мы инициализируем Flask, чтобы превратить наш проект в веб-приложение
  • Строка 8: мы используем декоратор для определения домашней страницы нашего приложения (пустой маршрут или /|/). Строки 19-20
  • : Мы запускаем встроенный веб-сервер Flask для обслуживания нашего контента.

Нажмите “выполнить” еще раз, и вы увидите, как в правом верхнем углу появится новое окно. Здесь мы видим базовую веб-страницу (которую уже можно просмотреть любому человеку в мире, поделившись URL-адресом, который вы видите над ней), и мы видим тот же вывод, который мы ранее распечатали на консоли.

**Изображение: 6**

Загрузка статей и извлечение текста

URL-адреса не очень полезны для нас, так как в конечном итоге мы хотим отобразить сводку содержимого каждого URL-адреса. Фактический текст каждой статьи не включен в RSS-канал, который у нас есть (некоторые RSS-каналы содержат полный текст каждой статьи), поэтому нам нужно будет еще немного поработать, чтобы загрузить каждую статью. Во-первых, мы добавим сторонние библиотеки requests и BeautifulSoup в качестве зависимостей, снова просто используя “волшебный импорт”. Мы будем использовать их для загрузки содержимого каждой статьи с URL-адреса и удаления дополнительных CSS и JavaScript, чтобы оставить нам простой текст.

Теперь мы готовы загрузить контент из каждой статьи и предоставить его пользователю. Измените код в main.py выглядеть следующим образом.

import feedparser
import requests

from flask import Flask
from bs4 import BeautifulSoup

app = Flask(__name__)

BBC_FEED = "http://feeds.bbci.co.uk/news/world/rss.xml"
LIMIT = 2

def parse_article(article_url):
    print("Downloading {}".format(article_url))
    r = requests.get(article_url)
    soup = BeautifulSoup(r.text, "html.parser")
    ps = soup.find_all('p')
    text = "\n".join(p.get_text() for p in ps)
    return text

@app.route("/")
def home():
    feed = feedparser.parse(BBC_FEED)
    article_texts = []

    for article in feed['entries'][:LIMIT]:
        text = parse_article(article['link'])
        article_texts.append(text)
    return str(article_texts)
        
if __name__ == '__main__':
    app.run('0.0.0.0')

Давайте подробнее рассмотрим, что изменилось.

  • Мы импортируем наши новые библиотеки в строки 2 и 5 .
  • Мы создаем новую глобальную переменную LIMIT on line 10 , чтобы ограничить количество статей, которые мы хотим загрузить.
  • Строки 12-18 определяют новую функцию, которая принимает URL-адрес, загружает статью и извлекает текст. Он делает это с помощью грубого алгоритма, который предполагает, что все, что находится внутри тегов HTML (абзац), является интересным контентом.
  • Мы изменяем строки 23, 25, 26 и 27 , чтобы использовать новую функцию parse_article для получения фактического содержимого URL-адресов, найденных в RSS-канале, и возвращаем его пользователю вместо прямого возврата URL-адреса. Обратите внимание, что мы ограничим это двумя статьями, сократив наш список до LIMIT на данный момент, так как загрузка занимает некоторое время, а ресурсы ответа на бесплатных аккаунтах ограничены.

Если вы запустите код сейчас, вы увидите вывод, аналогичный показанному на рисунке ниже (возможно, вам потребуется нажать обновить в правой панели). Теперь вы можете увидеть текст из первой статьи в правом верхнем углу, а текст для второй статьи находится дальше по странице. Вы заметите, что алгоритм извлечения текста не идеален, и в верхней части все еще есть дополнительный текст о “Поделиться этим”, который на самом деле не является частью статьи, но этого достаточно, чтобы мы могли создавать облака слов позже.

**Изображение: 7**

Возврат HTML вместо обычного текста пользователю

Хотя Flask позволяет нам возвращать объекты Python str непосредственно нашим посетителям, необработанный результат уродлив по сравнению с тем, как люди привыкли видеть веб-страницы. Чтобы воспользоваться преимуществами форматирования HTML и стиля CSS, лучше определить HTML шаблоны и использовать механизм шаблонов Flasks, jinja , чтобы добавить в них динамический контент. Прежде чем мы приступим к созданию файлов изображений из нашего текстового содержимого, давайте создадим базовый шаблон колбы.

Чтобы использовать шаблоны колб, нам нужно настроить определенную файловую структуру. Нажмите кнопку “новая папка” (рядом с кнопкой “новый файл” на левой панели) и назовите полученную новую папку шаблоны . Это специальное имя, распознаваемое Flask, поэтому убедитесь, что вы правильно написали его.

Выберите новую папку и нажмите кнопку “новый файл”, чтобы создать новый файл в нашей папке templates . Вызов файла home.html . Обратите внимание ниже, как home.html файл имеет отступ на один уровень, показывая, что он находится внутри папки. Если у вас его нет, перетащите его в папку templates , чтобы Flask мог его найти.

**Изображение: 8**

В home.html файл, добавьте следующий код, который представляет собой смесь стандартного HTML и синтаксиса шаблонов Jinja для смешивания динамического содержимого в HTML.


    
        

News Word Clouds

Too busy to click on each news article to see what it's about? Below you can see all the articles from the BBC front page, displayed as word clouds. If you want to read more about any particular article, just click on the wordcloud to go to the original article

{% for article in articles %}

{{article}}

{% endfor %}

Jinja использует специальные символы {% и {{ (в открывающих и закрывающих парах), чтобы показать, куда следует добавлять динамическое содержимое (например, переменные, вычисленные в нашем коде Python), и определить структуры управления. Здесь мы перебираем список статей и отображаем каждую из них в наборе тегов.

Нам также нужно будет немного подправить наш код Python, чтобы учесть шаблон. В main.py файл, внесите следующие изменения.

  • Добавьте новый импорт в верхней части файла, под существующим импортом колбы
from flask import render_template 
  • Обновите последнюю строку функции home () , чтобы вызвать render_template вместо прямого возврата str следующим образом.
@app.route("/")
def home():
    feed = feedparser.parse(BBC_FEED)
    article_texts = []

    for article in feed['entries'][:LIMIT]:
        text = parse_article(article['link'])
        article_texts.append(text)
    return render_template('home.html', articles=article_texts)

Вызов render_template сообщает Flask подготовить некоторый HTML-код для возврата пользователю, объединив данные из вашего кода Python и содержимого в нашем home.html шаблон. Здесь мы передаем article_texts в средство визуализации как articles , которое соответствует переменной articles , которую мы перебираем в home.html .

Если все прошло хорошо, теперь вы должны увидеть другой вывод, который содержит наш заголовок из HTML и статического первого абзаца, за которым следуют два абзаца, показывающие то же содержание статьи, которое мы вытащили раньше. Если вы не видите обновленную веб-страницу, вам может потребоваться снова нажать кнопку обновить в правой панели.

**Изображение: 9**

Теперь пришло время перейти к созданию реальных облаков слов.

Создание облаков слов из текста на Python

Еще раз, есть отличная библиотека Python, которая может нам помочь. Он будет принимать текст и возвращать облака слов в виде изображений и называется облако слов .

Изображения обычно подаются в виде файлов, находящихся на вашем сервере или с хоста изображений, например imgur . Поскольку мы будем динамически создавать небольшие, недолговечные изображения из текста, мы просто будем хранить их в памяти, а не сохранять их где-либо постоянно. Для этого нам придется немного повозиться с библиотеками Python io и base64 , а также с нашей недавно установленной библиотекой wordcloud .

Чтобы импортировать все новые библиотеки, которые мы будем использовать для обработки изображений, измените верхнюю часть вашего main.py выглядеть следующим образом.

import base64
import feedparser
import io
import requests

from bs4 import BeautifulSoup
from wordcloud import WordCloud
from flask import Flask
from flask import render_template

Мы будем преобразовывать текст из каждой статьи в отдельное облако слов, поэтому будет полезно иметь другую вспомогательную функцию, которая может принимать текст в качестве входных данных и создавать облако слов в качестве выходных данных. Мы можем использовать base64 для представления изображений, которые затем могут отображаться непосредственно в веб-браузерах наших посетителей.

Добавьте следующую функцию в main.py файл.

def get_wordcloud(text):
    pil_img = WordCloud().generate(text=text).to_image()
    img = io.BytesIO()
    pil_img.save(img, "PNG")
    img.seek(0)
    img_b64 = base64.b64encode(img.getvalue()).decode()
    return img_b64

Это, вероятно, самая сложная часть нашего проекта с точки зрения удобочитаемости. Обычно мы генерируем облако слов с помощью библиотеки wordcloud , а затем сохраняем полученное изображение в файл. Однако, поскольку мы не хотим использовать здесь нашу файловую систему, мы создадим объект BytesIO Python в памяти и сохраним изображение непосредственно в нем. Мы преобразуем полученные байты в base64, чтобы, наконец, вернуть их как часть нашего HTML-ответа и показать изображение нашим посетителям.

Чтобы использовать эту функцию, нам придется внести некоторые небольшие изменения в остальную часть нашего кода.

Для нашего шаблона, в home.html файл, измените цикл for на следующий.

{% for article in articles %}
    
{% endfor %}

Теперь вместо того, чтобы отображать нашу статью в тегах , мы поместим ее в тег , чтобы она могла отображаться в виде изображения. Мы также указываем, что он отформатирован как png и закодирован как base64.

Последнее, что нам нужно сделать, это изменить нашу функцию home () , чтобы вызвать новую функцию get_wordcloud() и построить и отобразить массив изображений вместо массива текста. Измените функцию home () , чтобы она выглядела следующим образом.

@app.route("/")
def home():
    feed = feedparser.parse(BBC_FEED)
    clouds = []
    
    for article in feed['entries'][:LIMIT]:
        text = parse_article(article['link'])
        cloud = get_wordcloud(text)
        clouds.append(cloud)
    return render_template('home.html', articles=clouds)

Мы внесли изменения в строки 4, 8, 9 и 10, чтобы перейти к массиву clouds , заполнить его изображениями из нашей функции get_wordcloud() и вернуть его в вызове render_template .

Если вы перезагрузите Repl и обновите страницу, вы увидите что-то похожее на следующее. Мы можем видеть один и тот же контент из статей, однако теперь мы можем видеть важные ключевые слова без необходимости читать всю статью целиком.

**Изображение:10**

Для более широкого просмотра вы можете открыть веб-сайт на новой вкладке браузера с помощью кнопки в правом верхнем углу редактора Repl (обозначенной красным цветом выше).

Последнее, что нам нужно сделать, это добавить стиль, чтобы страница выглядела немного красивее, и связать изображения с оригинальными статьями.

Добавление некоторых завершающих штрихов

Наш текст выглядит немного резким, и наши изображения соприкасаются друг с другом, что затрудняет понимание того, что это отдельные изображения. Мы исправим это, добавив несколько строк CSS и импортировав фреймворк начальной загрузки.

Добавление CSS

Отредактируйте home.html файл должен выглядеть следующим образом


  
    News in WordClouds | Home
    
      
    
  

  
    

News Word Clouds

Too busy to click on each news article to see what it's about? Below you can see all the articles from the BBC front page, displayed as word clouds. If you want to read more about any particular article, just click on the wordcloud to go to the original article

{% for article in articles %} {% endfor %}

В строке 3 мы добавляем заголовок, который отображается на вкладке браузера. В строке 4 мы импортируем Bootstrap , который имеет некоторые хорошие CSS-значения по умолчанию прямо из коробки (это, вероятно, немного тяжеловато для нашего проекта, так как у нас так мало контента и мы не будем использовать большинство функций Bootstrap, но это хорошо, если вы планируете расширить проект.)

В строках 6-8 мы добавляем отступы к основному корпусу , чтобы текст не приближался к краям экрана, а также добавляем отступы к нашим изображениям, чтобы они не касались друг друга.

В строке 16 мы используем тег для добавления ссылки на наше изображение. Мы также меняем шаблоны Jinja на {{article.url}} и {{article.image}} , чтобы у нас были изображения, которые ссылаются на исходную новостную статью.

Теперь нам нужно снова настроить наш внутренний код, чтобы пройти через URL-адрес и изображение для каждой статьи, так как шаблон в настоящее время не имеет доступа к URL-адресу.

Прохождение по URL-адресам

Чтобы легко отслеживать пары URL-адресов и изображений, мы добавим базовый вспомогательный класс Python под названием Article . В main.py файл, добавьте следующий код перед определениями функций.

class Article:
    def __init__(self, url, image):
        self.url = url
        self.image = image

Это простой класс с двумя атрибутами: url и изображение. Мы сохраним исходный URL-адрес из RSS-канала в url и окончательное облако слов base64 в image .

Чтобы использовать этот класс, измените функцию home() следующим образом.

@app.route("/")
def home():
    feed = feedparser.parse(BBC_FEED)
    articles = []

    for article in feed['entries'][:LIMIT]:
        text = parse_article(article['link'])
        cloud = get_wordcloud(text)
        articles.append(Article(article['link'], cloud))
    return render_template('home.html', articles=articles)

Мы изменили имя нашего списка clouds на articles и заполнили его , инициализировав объекты Article в цикле for и добавив их в этот список. Затем мы передаем articles=articles вместо articles=clouds в операторе return, чтобы шаблон мог получить доступ к нашему списку объектов Article , каждый из которых содержит изображение и URL-адрес каждой статьи.

Если вы снова обновите страницу и развернете окно с помощью кнопки “Всплывающее окно”, вы сможете щелкнуть любое из изображений, чтобы перейти к исходной статье, что позволит читателям просмотреть краткое резюме новостей за день или прочитать более подробную информацию о любых историях, которые привлекли их внимание.

Куда дальше?

Мы включили несколько функций в наше веб-приложение и рассмотрели, как использовать RSS-каналы, обрабатывать и обслуживать изображения непосредственно в Python, но мы могли бы добавить гораздо больше функций. Например:

  • Наше приложение показывает только две истории одновременно, так как время загрузки идет медленно. Вместо этого мы могли бы рассмотреть возможность реализации потокового решения для загрузки веб-страниц, чтобы мы могли обрабатывать несколько статей параллельно. В качестве альтернативы (или в дополнение) мы также могли бы загружать статьи по расписанию и кэшировать полученные изображения, чтобы нам не приходилось загружать и анализировать ресурсы каждый раз, когда посетитель посещает наш сайт.
  • Наше веб-приложение показывает статьи только из одного источника (BBC) и только с сегодняшнего дня. Мы могли бы добавить еще несколько функций для отображения статей из разных источников и разных временных рамок. Мы также могли бы рассмотреть возможность предоставления зрителю возможности выбирать, какую категорию статей просматривать (новости, спорт, политика, финансы и т.д.), Используя различные RSS-каналы в качестве источников.
  • Наш дизайн и планировка очень просты. Мы могли бы сделать наш сайт лучше и более отзывчивым, добавив больше CSS. Мы могли бы разложить изображения в сетку строк и столбцов, чтобы они лучше выглядели на небольших экранах, таких как мобильные телефоны.

Если вы хотите продолжить работу над веб-приложением, просто перейдите в Ответ и раскошелитесь, чтобы продолжить свою собственную версию.

В следующем уроке мы рассмотрим , как создать нашего собственного чат-бота Discord.