CoderCastrov logo
CoderCastrov
Питон

Как я собрал 1000 многоязычных новостных статей?

Как я собрал 1000 многоязычных новостных статей?
просмотров
5 мин чтение
#Питон

Насколько это сложно - должно быть легко с помощью Python, верно? На самом деле это было не так просто - давайте взглянем поглубже.

Почему мне понадобилось собрать 1000 новостей?

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

Как найти новостные статьи?

Задача требует не только собрать новостные статьи, но и данные о их политической идеологии. Хорошим местом для начала поиска такой информации является Википедия (или Wikidata, которая содержит все данные из Википедии в форме, доступной для запросов). Некоторые газеты имеют свою политическую идеологию, указанную в Wikidata. Мы можем использовать сервис Wikidata Query для сбора всех газет, у которых определена политическая идеология.

Сервис Wikidata Query предоставляет SPARQL-точку доступа, которая позволяет выполнять запросы к базе знаний Wikidata. Я не буду вдаваться в подробности о базах знаний и SPARQL, но вкратце можно рассматривать базу знаний как большой граф, который связывает объекты с другими объектами через предикаты (или свойства).

Мы можем использовать SPARQL для создания запросов к базе знаний и получения всей информации, которую Wikidata имеет о газетах. Начнем с простого запроса, который возвращает все объекты, классифицированные как газеты.

Несколько замечаний о синтаксисе: строки, начинающиеся с ?, являются переменными, а wdt: или wd: - это префиксы, содержащие свойства и значения объектов. Этот запрос выбирает все объекты, которые удовлетворяют свойству instance of (P31) со значением newspaper (Q11032). В результате мы получаем 41300 газет.

Теперь мы можем расширить этот запрос, чтобы вернуть все газеты, у которых есть ссылка на веб-сайт. Теперь мы хотим все объекты, которые не только удовлетворяют свойству instance of со значением newspaper, но и имеют свойство с именем official website со значением (сохраненным в переменной ?link).

Теперь из 41300 газет мы переходим к 7132 газетам с ссылками.

Последнее, что нам нужно, - это политическая идеология для этих веб-сайтов. Политическая идеология может быть выражена двумя свойствами - political ideology (P1142) и political alignment (P1387). Мы можем создать два отдельных запроса для каждого из свойств, а затем объединить оба набора результатов.

Результат этого запроса дает 217 веб-сайтов с меткой политической идеологии. Одна проблема с этими данными заключается в том, что есть метки, у которых есть один или два веб-сайта - это проблема, потому что нейронные сети работают лучше, когда данные сбалансированы (есть одинаковое количество примеров для каждой метки). Чтобы исправить это, я взял топ-10 меток и удалил все остальные веб-сайты. Это действительно уменьшает количество веб-сайтов для работы, но гарантируется, что у каждой метки есть достаточное количество газет. После этой фильтрации у меня осталось 83 веб-сайта. 17 веб-сайтов были недоступными, поэтому я удалил их. Кроме того, были дублирующиеся веб-сайты, и после удаления дубликатов у меня осталось 44 новостных веб-сайта, ссылка на них и метка политической идеологии.


Но на самом деле, как парсить статьи?

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

Еще один способ решения этой проблемы - использование забытой RSS-ленты. Для тех, кто не знает (или не помнит) о RSS-лентах, в основном они представляют собой простой XML-файл, который содержит новостные статьи (или другие ссылки) с заданного веб-сайта. Эти ленты периодически обновляются, что позволяет пользователю получать самые свежие новости в режиме реального времени.

Это означает, что если сайт поддерживает RSS-ленту, я могу разобрать ее содержимое и извлечь ссылки на новостные статьи. Теперь мне нужно было вручную проверить, есть ли у веб-сайтов RSS-лента. Из 44 веб-сайтов 30 имели RSS. У большинства веб-сайтов была кнопка RSS, другие можно было получить, добавив "rss" к URL-адресу.

Фактический разбор статей

Для парсинга веб-сайтов наиболее широко используемой библиотекой на языке Python является BeautifulSoup. Однако получение содержимого страницы не так просто. BeautifulSoup может возвращать DOM-дерево веб-страницы и предоставлять простой интерфейс для навигации по элементам веб-страницы. Это означает, что мне нужно было изучить структуру каждого веб-сайта, чтобы знать, из каких HTML-элементов мне нужно получить значение. К моему удивлению, большинство веб-сайтов имели похожую структуру - основное содержимое статьи находилось внутри тега <article> в виде нескольких тегов <p> для каждого абзаца. Чтобы разобрать такие веб-страницы, мне нужно было найти тег <article> и взять содержимое каждого тега <p> внутри этого тега <article>.

Из 30 веб-сайтов 19 имели структуру <article>/<p>. Еще 8 из них содержали контент, расположенный в тегах <p>, но на этот раз контент находился внутри тега <div> с уникальным class или id. Для разбора этих страниц потребовалось минимальное изменение парсера тега <article> - в основном поиска тегов <div> с заданным class или id.

Другие 3 веб-сайта были немного сложными - у них не было достаточно простой структуры для разбора. Создание специального парсера для разбора этих 3 веб-сайтов не стоило таких усилий.

После того, как текст был разобран, все, что оставалось, - это сохранить содержимое в файл. Я хотел, чтобы каждый веб-сайт находился в отдельной директории, а статьи были в файлах в формате обычного текста.

Итоговые результаты

Объединение всего кода просто — при заданной ссылке на RSS-ленту веб-сайта, код разбирает все ссылки, парсит содержимое и сохраняет его в правильной директории.

Используя этот код на 23 веб-сайтах, мне удалось спарсить 1006 статей. Код не делает никаких предположений о языке статей, что позволяет его использовать на любом новостном веб-сайте, независимо от языка статьи.

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

В конечном итоге, 1006 статей были успешно использованы для обучения модели, которая имела около 90% точности. Полный код можно найти на моем Github — ссылка здесь.