Дискуссии: Своя сборка Яндекс новостей

Свои Яндекс-Новости

By Firepush |

Я, как и миллионы сограждан, узнаю новости из топ-5 ленты Яндекс-Новостей на главной поисковика. Одним прекрасным днем я задумался о том, как именно работает их алгоритм.

 

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

 

Цель максимум:

 

Научиться предсказывать темы новостей из топ-5 яндекс-ленты.

 

Цель минимум:

 

Научится просто грамотно выделять темы новостей.

 

Основные шаги такие:

 

Сбор ссылок на все СМИ, которые участвуют в Яндекс-новостях

Сбор ссылок на RSS-ленты этих ресурсов

Настройка парсера RSS-лент и запись сырой информации о статьях в базу данных (сбор Raw Data)

Составление алгоритма определения тем новостей за определенный период (задача кластеризации) и записи информации в БД (processed data)

Разработка веб-интерфейса с топом всех новостей и различными параметрами

 

Сбор ссылок на СМИ

 

Сбор ссылок я осуществлял с помощью программы Content Downloader (http://sbfactory.ru/). Крайне рекомендую! Программа платная, но не дорогая (от 1000 до 2000 рублей в зависимости от кол-ва потоков) Научиться пользоваться не сложно – есть много обучающих материалов и автор активно выходит на связь. Я пользуюсь не самой новой версией, просто потому что привык к старому интерфейсу.

 

Алгоритм сбора ссылок выглядел следующим образом:

 

а) сбор ссылок на страницы сайта http://news.yandex.ru/ методом массового обхода всего сайта (есть такая функция в Content Downloader). Весь сайт само собой обойти не удастся да и не нужно. Я собрал несколько тысяч страниц, чего вполне хватило.

 

б) сбор ссылок на новости СМИ со страниц, собранных выше. Чтобы указать программе, что именно нужно парсить, нужно просмотреть html-код искомого элемента. Нужная ссылка выглядит как-то вот так:

 

<a class=”b-link” …здесь всякий мусор… href=”http://russian.rt.com/article/68852″ target=”_blank”>Россия в 2015 году укрепит войска в Крыму, Калининграде и Арктике</a>

 

 

 

Указываем программе границы парсинга:

 

начало:

 

<a class=”b-link”{skip}”href=”

 

конец:

 

” target=”_blank”>

 

({skip} – макрос позволяющий включить в границу все, что угодно до тех пор пока не встретится последовательность символов, которая стоит после него)

 

Указываем “не включать границы” в парсинг (нам ведь нужна только ссылка а не html код)

 

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

 

Запускаем парсинг. В результате получаем огромный список ссылок на новости.

 

Но новости сами по себе нам не интересны, нам нужны ссылки на ресурсы.

 

в) Получение списка СМИ. Воспользуемся старым добрым Excel. Выгружаем список в таблицу. Удаляем все вхождения “http://” и “www.” через ctrl+h. Далее пользуемся функцией “разделить по столбцам”. Разделителем будет слеш “/”. Удаляем все, кроме первого столбца. Мы почти у цели – осталось удалить дубли (новости то разные, но ресурсы могут быть одинаковыми). Выделяем столбец, жмем “удалить дубли” – готово!

 

У меня получилось более 2000 самых разных ресурсов.

 

 

 

Сбор ссылок на RSS-ленты этих ресурсов

 

Для меня этот этап оказался самым унылым. Но начнем с начала. Прежде чем собирать список RSS желательно отсортировать список СМИ по популярности. Ведь для теста сойдет и сотня источников, но лучше сразу отобрать самые рейтинговые из них. Выход очень простой – собрать ТИЦ (тематический индекс цитирования Яндекса) для всего списка.

 

По запросу “массовое определение ТИЦ” на первом же месте видим http://www.raskruty.ru/tools/cy/

 

То что надо! Делаем три подхода (больше 1000 url за раз нельзя) – копируем результат в новую табличку Excel, сортируем по убыванию значения ТИЦ. Есть!

 

А теперь, собственно, вопрос знатокам. Как, зная ссылку на ресурс, получить ссылку на его rss-ленту?

 

Казалось бы, что может быть проще? Ан, нет. Нет никакой закономерности в формате ссылок на rss у ресурсов. Более того страницы rss-лент не индексируются. Поэтому что-то вроде “inurl: site.ru rss” в поисковиках почти никогда не работает.

 

Оказалось, существует специальный поисковик RSS – http://ctrlq.org/rss/

 

Однако верную ссылку на RSS при указании запроса в виде site:thenextweb.com показывает слишком редко.

 

Потом я вспомнил, что существует Яндекс Лента – она же rss-читалка от Яндекса. Проверив несколько топовых сайтов, я был в полном восторге – вбиваешь ссылку на сайт, получаешь ссылку на rss. Но покопавшись внимательней, обнаружил такие досаднейшие недочеты:

 

Ссылки на rss часто устаревшие. При клике по такой ссылке, как правило, попадаешь на 404 страницу. А новости, которые показываются Яндексом с такого ресурса, могут датироваться даже 2006 годом.

Для Яндекса сайт c www и без – два разных ресурса.

Бывает не находит ссылку вовсе.

Смирившись с тяжкой судьбой, решил все же автоматизировать процесс поиска rss через яндекс-подписки. Ничего не получилось: проблема авторизации, https соединение и подгрузка rss-ссылки “на лету”. Признаюсь, пробовал даже автокликер, но все впустую.

 

Пришлось собирать ручками. Если не находил через Яндекс – искал вручную на сайте. В итоге собрал с приятелем около 500 ссылок. Потом решу, как добить остальное.

 

 

 

Настройка парсера RSS-лент и запись информации о статьях в базу данных

 

Я использовал php + mysql для решения задачи.

 

“Верхний” цикл – обход rss-лент всех имеющихся ресурсов

“Нижний” цикл – обход всех новостей с конкретного ресурса и выявление “свежих”.

Здесь стоит задача определения “свежести” новости. В базе данных заводится таблица “rssurls”, которая состоит из двух полей: rssurl и lastdate.

 

Для каждого ресурса в базе нужно при каждом обходе записывать дату последней замеченной на ресурсе статьи. Так при следующем обходе скрипт сравнивает дату конкретной статьи из RSS ленты с датой из базы и если время статьи “новее” – записывает статью в базу и обновляет дату последней публикации.

 

Для статей заводится отдельная таблица “articles”, куда в отдельные поля сохраняется следующая информация: id записи, rssurl, title, date, link, category, record (время записи в базу) и tags(об этом поле – в следующей части).

 

Title, date, link, category – парсятся из rss. RSS, кто не знает, представляет и себя обыкновенный XML-файл, а значит с ними наверняка легко работать встроенными функциями php. Так и оказалось – есть замечательная функция simplexml_load_file, которая позволяет легко получать необходимые элементы. Но у этой функции есть недостаток – она не предназначена для работы с внешними источниками. По хорошему нужно работать через cURL, но для начала и так сойдет. Около четверти ресурсов отказывались подгружаться через simplexml_load_file, хотя ссылка в браузере открывалась.

 

Не смотря на то, что RSS довольно “строг”, формат данных очень часто отличался. Особенно все плохо с датами: кто указывает timezone, а кто нет, кто указывает дни недели, а кто нет и т.д. Пришлось долго повозиться с регулярными выражениями, чтобы привести все нормальный вид.

 

 

 

Составление алгоритма определения тем новостей

 

Самый интересный и творческий этап. Наверняка можно было бы применить модные и сложные статистические алгоритмы кластеризации. Но мне хотелось попробовать провести анализ “вручную” и в целом – получилось неплохо.

 

Я решил, что “темы” нужно вычленять из заголовков исходя из частоты упоминаний слов в них. Это довольно очевидно, но считать частоту слов в необработанном виде совершенно бессмысленно. Нужно как-то приводить к начальной форме все слова из конкретных заголовков.

 

Покопавшись в интернете я наткнулся на потрясающую библиотеку phpmorphy – http://phpmorphy.sourceforge.net/dokuwiki/

 

Библиотека среди прочего позволяет получать из любого слова на русском языке его базовую форму (по-научному процесс называется “лемматизация”). Это как раз то, что нам нужно!

 

Используя библиотеку, я написал функцию, которой на вход подается заголовок новости, а на выходе отдается строка из “нормированных” слов. Пример:

 

“Родители пропавших в Мексике студентов ворвались в казармы в Игуале” превращается в

 

“СТУДЕНТ РОДИТЕЛЬ ПРОПАСТЬ МЕКСИКА КАЗАРМА ВОРВАТЬСЯ ИГУАЛ”

 

(все вспомогательные знаки, кроме тире, игнорируются)

 

Дополняем функцию записи новости в таблицу articles, добавляя в поле tags строку с “нормированным” заголовком.

 

При первом же подсчете частот слов и их сортировке стало очевидно, что выбран верный путь. Стало ясно, что нужно добавить список стоп-слов, которые сами по себе не могут формировать новостную “тему”, но их частота упоминаний велика:

 

“НА”, “ЗА”, “ПО”, “ГОД”, “НЕ”, “БЫТЬ”, “ДО”, “ИЗ”, “ИЗА”, “ДЛЯ”, “ИЗ-ЗА”, “СТАТЬ”, “ЧТО”, “БОЛЕЕ”, “МОЧЬ”, “ПРИ”, и ряд других. Удаляем стоп-слова из нормированных заголовков.

 

Казалось бы после этого – все должно быть хорошо. Но часто попадаются “нормальные” слова, которые нельзя включить в стоп-лист, но которые точно не формируют “тему”. Это объясняется тем, что одно и тоже слово встречается в заголовках, посвященных разным темам. Особенно часто это касается географических названий.

 

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

 

Пример распределения частот для “ненастоящих” слов “новый” и “СМИ”:

 

645 – НОВЫЙRplot

44 – РОССИЯ

29 – СТАРЫЙ

28 – УКРАИНА

24 – ДОНБАСС

23 – НОМЕР

 

 

387 – СМИ

54 – РОССИЯ

43 – РОССИЙСКИЙ

35 – СИЛА

29 – ВОЗДУШНО-КОСМИЧЕСКИЙ

26 – РОСКОМНАДЗОР

 

 

А теперь “настоящие” слова “Нефть” и “Обстрел”

 

359 – НЕФТЬRplot02

152 – ЦЕНА

87 – БАРРЕЛЬ

63 – НИЖЕ

57 – ДОЛЛАР

52 – УПАСТЬ

 

 

279 – ОБСТРЕЛRplot03

145 – АВТОБУС

79 – ВОЛНОВАХА

50 – ЧЕЛОВЕК

46 – ОБСТРЕЛЯТЬ

43 – ДОНБАСС

 

 

По форме “хвоста” можно определить “настоящесть” найденной темы. О формулах – чуть ниже.

 

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

 

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

 

Вернемся к “настоящести”. Я не стал особо изощряться с формулами. Считал среднее геометрическое для значений частот пяти слов из “хвоста” и делил на частоту исходного слова. Эмпирическим путем пришел к коэффициенту 0,17. Если коэффициент “настоящести” равен или выше этого значения, считаем тему “настоящей”.

 

При нахождении каждой отдельной “настоящей” новости в новую таблицу top записывается “измерение”: название темы, три ключевых слова, частота основного слова, дата начала периода измерения, дата конца периода измерения, html-код трех ссылок на примеры статей по теме.

 

Веб-интерфейс с топом всех новостей

Я настроил cron так, чтобы обход всех rss-лент и сбор новостей происходил раз в десять минут.

 

Сами “измерения” происходят за 6-часовой период раз в 6 часов (пока что). Позже, когда соберем больше ссылок на rss, сделаю периоды меньше а сами измерения чаще. (оптимальные параметры еще предстоит выяснить. Сам Яндекс обновляется раз в 20 минут)

 

В самом веб-интерфейсе вывожу данные за последний период, где “частоты” сравниваются со значением из предыдущего измерения:

 

6SXJiDY

 

 

 

Задача минимум выполнена. Надеюсь, работа была проделана не зря )

Add Comment

Required fields are marked *. Your email address will not be published.