Как искать новости или RSS-агрегатор моей мечты

Недавно web-аналитики объявили о появлении в интернете миллиардного сайта. Цифра весьма впечатляет и заставляет задуматься. Но одновременно с этим вспоминается и старая шутка о том, почему люди постоянно только глупеют — население планеты увеличивается, а количество мудрости является величиной постоянной. Так и с сайтами. Есть ценные жемчужины полезной информации, но найти их в гигантской навозной куче становится с каждым днем все сложнее.

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

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

Части новостного агрегатора

Web-паук

Первая часть является фоновым процессом. Это web-паук, который выполняет две подзадачи. Во-первых, переходит с сайта на сайт и собирает базу URL существующих сайтов. Поскольку вычислительные ресурсы ограничены, а арендовать крупный дата-центр мне не на что, запрашивается только 1 страница — главная. Загружать все страницы не получится, это не нужно, да и владельцы сайтов защищают контент и не дадут выкачать сайт целиком, а просто заблокируют IP. Особо продвинутые админы уже настроили iptables на автоматическую блокировку самых наглых качальщиков.

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

Вторая подзадача состоит в том, чтобы в загруженной главной странице каждого сайта отыскать ссылку на RSS. Эта задача и простая, и сложная одновременно. Простая она потому, что ссылка на RSS указывается в заголовке документа (внутри тега head) и имеет явно указанный тип "application/rss+xml":

Вытянуть ссылку можно простым запросом на языке XPath:

//link[@type='application/rss+xml']

Чтобы сделать работу с XPath приятнее, можно воспользоваться прекрасным и к тому же бесплатным пакетом для парсинга HTML-документов HTML Agility Pack. Конечно, велик соблазн использовать для этой цели регулярные выражения, но гуру программирования категорически это запрещают.

На этом простота заканчивается и начинаются сложности. Дело в том, что RSS-лента вовсе необязательно должна быть указана в head. Вебмастер может сослаться на RSS-канал в каком-нибудь сайдбаре. В этом случае потребуется перебрать все внутренние ссылки, каждую проверить на вхождение подстрок типа "rss", "feed", "atom" и так далее, а потом оставшихся претендентов проверить, запросив конечный документ по ссылке.

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

Кстати, сначала у меня были сомнения, будет ли активно пополняться база адресов сайтов, если начать брать адреса всего с одного сайта (я взял его с потолка — lenta.ru) и потом пополнять базу, извлекая внешние ссылки только с главной? Не закончится ли пополнение базы, когда в нее войдет несколько тысяч самых популярных ресурсов, ведь на главной странице уважаемых сайтов не принято ссылаться на мелкие и неавторитетные ресурсы, а количество крупных и уважаемых сайтов относительно невелико. Как оказалось, мои опасения были напрасны и размер базы сразу же начинал расти по экспоненте. (Хотя в течении нескольких первых итераций в базу действительно попадали преимущественно крупные порталы, типа VK, Facebook, Rambler, LJ, Yandex, Google и т.п.).

Вспоминается и курьёзный случай. Я допустил небольшую ошибку, забыв явно задать количество параллельных потоков на скачивание страниц. Для распараллеливания я использовал базовую библиотеку Task Parallel Library, которая при отсутствующем параметре количества потоков пытается определить это количество самостоятельно, исходя из наличия свободных вычислительных ресурсов. На компьютере с 4 ядерным (8 логических ядер с Hyper-threading) процессором и 16 Гб ОЗУ количество одновременных соединений получилось где-то в районе 500. Часть страниц корректно загрузилась и после этого сетевая подситема Windows сдохла. Не помогло ни переподсоединение к интернету, ни переподключение сетевого адаптера. Только перезагрузка. А вот в Linux паук повел себя иначе. Вместо .Net Framework 4.5.1 в Линуксе программа запускалась под Mono (не помню какой версии). Так вот, та же самая программа открыла не 500 параллельных соединений, а где-то 30-40, скачала примерно то же количество страниц и программа сдохла, перестав отвечать, но сетевая подсистема Linux выдержала и другие программы соединение с интернетом не потеряли. Выписываем Линуксу орден за стойкость и идем дальше.

Загрузчик RSS-лент

Вторая часть новостного агрегатора моей мечты — это опять фоновый процесс со множеством параллельных потоков. Этот модуль занимается только тем, что с некоторой автоматически подстраиваемой периодичность пробегает по списку RSS-лент, добытому web-пауком, загружает XML-документ с RSS, парсит его и если в ленте появились новые материалы, добавляет новость в базу данных. Следует отметить, что под новостью понимается совокупность из заголовка, описания, ссылки на полный текст новости, языка и даты. Полный текст не загружается из опасений, что повышенная активность бота может быть расценена, как вредительство и IP-адрес паука будет забанен (прецеденты имеются). Полный текст открывается в браузере привычным образом только тогда, когда пользователь выбирает интересующую новость и кликает по заголовку. Хочу еще раз подчеркнуть, что задачи написать второй Яндекс или Google нет. Даже если бы сайты не банили за выкачивание новостей, не так-то просто регулярно скачивать несколько десятков страниц с десятков тысяч сайтов. Да и как провайдер отнесется к такой активности домашнего пользователя неизвестно.

Выбор базы данных для хранения новостей — вопрос отдельный. Так как я знал, что многие десктопные RSS-читалки используют для хранения SQLite, я решил начать именно с этой СУБД. Ее основное преимущество заключаются в том, что это встраиваемая база данных, поэтому не требуется установление соединения клиент-сервер и простые SQL-запросы выполняются (должны) значительно быстрее. А еще приятная особенность в том, что вся база хранится в одном файле, что упрощает перенос и администрирование.

Подключив SQLite, я к великому удивлению обнаружил, что запросы обрабатываются чудовищно медленно. Пришлось таки прочитать документацию. Оказалось, что SQLite действительно уделывает MySQL по скорости выполнения запросов, однако это распространяется только на чтение из базы. Если поступает запрос на запись (insert, update и delete), SQLite блокирует всю базу до завершения операции. Учитывая, что загрузчик лент преимущественно записью и занимается, SQLite висел чуть реже, чем всегда. И даже не из-за записи, а из-за большого числа параллельных запросов на запись. То есть, блокировка базы делала невозможным распараллеливание задачи по обновлению лент.

Второй СУБД стала MySQL. Прекраснейшая система, которая уделала и немощный SQLite и PostgreSQL. Но когда количество новостей перевалило за сотню тысяч, преимущество по скорости было утеряно. Вперед, хотя и ненамного, вышла СУБД PostgreSQL, на которой я и остановился. Да, может PostgreSQL нетороплив, зато у меня нет опасений, что по мере роста база развалится. Да и специалисты рекомендуют для больших объемов данных использовать именно PgSQL.

Парсить RSS легко и приятно, ведь это XML, а более удобного для парсинга языка еще не придумано. Никакие сторонние библиотеки не понадобились, хватило System.Xml из стандартной библиотеки классов.

Файл формата RSS имеет вид:

<?xml version="1.0" encoding="utf-8"?>

Liberatum.ru
http://liberatum.ru

ru

Не спешите выбрасывать: 11 полезных применений для старого смартфона
http://liberatum.ru/exclusive/staryj-smartfon
У вас имеется старый смартфон, которым вы давно не пользуетесь? Не спешите относить электронного ветерана на помойку
Mon, 22 Sep 2014 23:18:36 +0000
pomidorium
26241 at http://liberatum.ru

Все просто: сначала выбираются все элементы item, а потом из каждого извлекаются title, link, description и language и переносятся в базу данных. Разумеется, предварительно стоит убедиться, что они не были занесены туда ранее, ведь в RSS попадает n последних новостей, а не только новые, поэтому задача по отслеживанию "прочитанного" перекладывается на клиента.

Дату новости лучше брать не из фида, а по времени загрузки. Дело в том, что, как оказалось, некоторые сайты преднамеренно корректируют дату в ленте, чтобы еще раз преподнести читателю старый документ под видом нового. Для новостей такие шутки не практикуются, а для сайтов со статьями очень даже. Как я понимаю, расчет на то, что пользователь узнал о сайте относительно недавно и наверняка заинтересуется давно написанными, но популярными статьями, которые еще не читал. Идея не лишена логики и надо будет ее опробовать на Либератуме. Ну так вот, новостному агрегатору такие "старые новые" документы не нужны и правильнее ставить первую дату, а не обновленную. Поэтому дате из ленты лучше не доверять.

Самое сложное в работе загрузчика лент — автоматически определить, какие сайты чаще проверять на обновления, а какие реже, ведь на крупном новостном портале за час может появится несколько десятков новостей, а блог Васи Пупкина может обновляться раз в месяц. Проверять обе ленты с одинаковым интервалом не только нет смысла, но и можно получить проблему. Дело в том, что в ленту входит только заданное админом количество последних новостей. Если в ленте 25 новостей, а за день выходит 50, то при обновлении ленты 1 раз в день 25 новостей агрегатор вообще не увидит. Следовательно, для таких ресурсов интервал между проверками должен сокращаться. За счет тех сайтов, которые обновляются медленно. Ибо, как говорил К.Прутков, «нельзя объять необъятное». Особенно, если обнимать предполагается со скромного домашнего компа.

Проверять на дубликаты добавляемую новость можно различными способами. Самый простой — проверять новость по заголовку. Недостаток этого метода очевиден — разные новости могут иметь одинаковый заголовок. Особенно, если новость пишет хреновый журналист, который в заголовке указывает не ключевые слова, а какую-нибудь высокохудожественную окололитературную хрень. Отличные заголовки у Ленты.ру (по крайней мере, были до тех пор, пока из Ленты не сбежала основная группа журналистов). Самые дебильные — у Газеты.ру. Мало того, что из заголовка непонятно о чем речь, так еще заголовки могут совпадать. Примеры с Газеты:

  • «Эффективное последнее место»
  • «Возвращение оружия»
  • «Шестерка с плюсом»
  • «Женское дело»
  • «Если что-то пошло не так»
  • и т.п.

Даже в рамках одного сайта может быть несколько новостей на разные темы с одним заголовком. Попробуйте, например, поискать в Google документы с точным вхождением фразы "если что-то пошло не так".

Другой способ проверять на дубликаты — хранить контрольные суммы поля description. Недостаток этого способа в том, что иногда description попросту отсутствует. Не верите? Вот пример — http://www.leenks.com/rss.php.

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

Собственно, сама RSS-читалка

Самая простая часть. Представляет собой просто интерфейс к БД с новостями и только. Пользователь задает несколько ключевых слов, читалка ищет все новости за указанный период с вхождением ключевого слова в заголовок и описание и выводит на экране компактный список найденного. Клик по заголовку открывает страницу с полным содержанием в браузере.

Пока я дальше не продвинулся из-за ограниченного свободного времени. Но эта часть самая интересная. Вот лишь немногие вопросы, которые еще предстоит решить:

  1. Как ранжировать новости? Пока список упорядочивается только по времени, в самом верху появляется самое свежее, как и в любой другой читалке.
  2. Как объединять новости в сюжеты?
  3. Как выкачивать новости в рамках одного сюжета и объединять так, чтобы получить все детали события на одной странице, по возможности без повторений и пропусков фактов.
  4. Как вообще извлечь из HTML-страницы только текст, без сайдбаров, рекламы и прочих элементов (частично решено)?
  5. Как разбивать новости по рубрикам?
  6. Как отфильтровывать явный мусор, дорвеи и прочий хлам?
  7. Как заставить читалку обучаться, выявлять интересы пользователя и автоматически показывать те новости, которые теоретически будут пользователю интересы (частично решено)?

Последний пункт самый важный, самый интересный и ради него все и затевалось. Для решения этой задачи прекрасно подходят статистические методы. Но самые интересные результаты получаются при использовании нейронных сетей, которые позволяют учитывать не только вхождение ключевых слов, но и контекст. Например, если пользователь интересуется Линуксом и начинает искать новости по ключевому слову "СПО" (свободное программное обеспечение), то ему будут предложены новости именно в этом контексте, а новости о другом "СПО" (среднее профессиональное образование) показаны не будут. Но об этом уже в следующем посте, а то я что-то подустал по клавишам стучать.

Оценка: 
5
Средняя: 4.7 (3 оценки)

Комментарии

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

Оценка: 
Средняя: 5 (1 оценка)

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

Оценка: 
Средняя: 5 (1 оценка)

Не для того, чтобы вы написали такие же читалки

Как будто это что-то плохое… :)

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

Как вариант, можно было бы сделать распределённую систему. Что-то типа ботнета. ;) Но не в том смысле, чтобы взламывать чьи-то чужие компьютеры, а в том, чтобы использовать ресурсы многочисленных добровольцев: если бы программа была достаточно распространённой среди пользователей, можно было бы собирать распределённую базу ресурсов, с каждого IP по чуть-чуть, а потом объединять это всё в единый доступный для всех ресурс, который также можно хранить распределённо, ну, примерно как торренты или биткоины, — этакий crowd computing. IMHO, если проект действительно требует больших ресурсов, а мощного кластера/дата-центра под рукой нет, это единственный реально осуществимый вариант.

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

IMHO, никто лучше браузера не умеет парсить HTML. Теоретически можно использовать голый Webkit- или Gecko-движок (в принципе, и Trident тоже, но тогда придётся забыть о кроссплатформенности) с прикрученным к нему модулем на JavaScript, который бы обрабатывал результат парсинга и выдавал его в удобном виде — можно даже сразу в ту же sqlite-базу (например, именно так работает Firefox).

Вебмастер может сослаться на RSS-канал в каком-нибудь сайдбаре. В этом случае потребуется перебрать все внутренние ссылки, каждую проверить на вхождение подстрок типа "rss", "feed", "atom" и так далее, а потом оставшихся претендентов проверить, запросив конечный документ по ссылке.

А разве в этом случае файл канала не использует тот же XML с соответствующим расширением файла? Тогда ведь не придётся проверять все строки на вхождение подстрок, достаточно проверить только те, которые ссылаются на «.xml».

Если кратко, алгоритм работы паука заключается в том, чтобы отыскать сайт, найти на нем RSS-ленту (или убедиться, что ее нет)

Так ведь все современные браузеры уже это умеют, можно использовать готовый функционал того же Firefox, вызывая его всё в том же JavaScript-сценарии.

ведь на главной странице уважаемых сайтов не принято ссылаться на мелкие и неавторитетные ресурсы, а количество крупных и уважаемых сайтов относительно невелико

Не забываем про комментарии пользователей. ;)
Хотя тут, кстати, есть проблема с системами типа Disqus или встроенными виджетами соцсетей, потому что фактически это уже другой сайт. Придётся все айфреймы перебирать… >_<

Хочу еще раз подчеркнуть, что задачи написать второй Яндекс или Google нет

Но как-то так само собой получается… :)

XML, а более удобного для парсинга языка еще не придумано

Есть мнение (не моё, но тем не менее), что XML безнадёжно устарел, а будущее за такими форматами, как JSON. Во всяком случае, вроде как именно этот аргумент в том числе выдвигали в Google, когда закрывали свой Reader. Это может привести к тому, что вместо RSS появится какой-нибудь RSSON — то же самое, только с JS-синтаксисом.

Как я понимаю, расчет на то, что пользователь узнал о сайте относительно недавно и наверняка заинтересуется давно написанными, но популярными статьями, которые еще не читал. Идея не лишена логики и надо будет ее опробовать на Либератуме

Вот, пожалуйста, не надо так делать. Лучше отдельную ленту завести.

Самое сложное в работе загрузчика лент — автоматически определить, какие сайты чаще проверять на обновления, а какие реже

Так а как это определить-то?

Недостаток этого метода очевиден — разные новости могут иметь одинаковый заголовок

Обратное, кстати, тоже справедливо, правда, обычно на на разных сайтах.

Попробуйте, например, поискать в Google документы с точным вхождением фразы «если что-то пошло не так».

Ну так правильно, эта фраза уже успела стать мемом, вот её и используют все кому не лень, к месту и не к месту. Потому что используя мемы (а также распространённые эрративы), мы какбэ намекаем, что мы такие модные, современные, продвинутые, тренданутые, вот это всё. ;)

Как ранжировать новости? Пока список упорядочивается только по времени, в самом верху появляется самое свежее, как и в любой другой читалке.
Как объединять новости в сюжеты?
Как выкачивать новости в рамках одного сюжета и объединять так, чтобы получить все детали события на одной странице, по возможности без повторений и пропусков фактов.
Как разбивать новости по рубрикам?

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

Как вообще извлечь из HTML-страницы только текст, без сайдбаров, рекламы и прочих элементов (частично решено)?

В JavaScript — document.documentElement.textContent

Как отфильтровывать явный мусор, дорвеи и прочий хлам?

Так это… «выявление дорвеев на марковских цепях» же. :)

Как заставить читалку обучаться, выявлять интересы пользователя и автоматически показывать те новости, которые теоретически будут пользователю интересы (частично решено)?

Стать Гуглом, Фейсбуком или Твиттером, сканировать почту пользователя и следить за его активностью, как минимум в течение 6 месяцев (как требует закон), не забывая вовремя постукивать куда надо. :)
А если серьёзно, то при помощи тегов это тоже можно решить, достаточно попросить пользователя ввести или выбрать из готовых интересные ему теги.

Последний пункт самый важный, самый интересный и ради него все и затевалось. Для решения этой задачи прекрасно подходят статистические методы.

То есть таки, пусть даже обезличенная, но слежка? Столлман не одобряет же! ;)

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

Видите ли, у всех эти программ есть как минимум один «фатальный недостаток»… :)

Оценка: 
Средняя: 5 (1 оценка)

можно было бы сделать распределённую систему. Что-то типа ботнета

Предложение не лишено здравого смысла. Распределеность придает старым технологиям новый смысл. Например, взяли банальную идею электронных денежных переводов, добавили peer-to-peer и получили Биткоин, от которого у правительств крупнейших стран до сих пор кровавый понос. Думаю, кто-то когда-нибудь догадается как гармонично скрестить поисковые технологии с peer-to-peer и появится не менее потрясающий продукт. ;)

никто лучше браузера не умеет парсить HTML. Теоретически можно использовать голый Webkit- или Gecko-движок

Так все равно придется обращаться к браузеру через привычный GetElementById. Эту же функцию предоставляет и HTML Agility Pack. Только браузер — это десятки мегабайт, а HAP — 1 dll-ка размером 150 кб. Или я что-то неправильно понял? В чем преимущества использования браузерного движка? Там же 90% тратится на рендеринг, который персеру не нужен...

А разве в этом случае файл канала не использует тот же XML с соответствующим расширением файла?

Неа. Посмотрел сейчас собранную базу — встречается 4 вида основных расширений:

  • вообще без расширения (пр.: http://lenta.ru/rss)
  • .xml
  • .rss
  • .php

Последний уж точно не отловить, не проверяя содержимое.

Не забываем про комментарии пользователей. ;)

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

Хотя тут, кстати, есть проблема с системами типа Disqus или встроенными виджетами соцсетей, потому что фактически это уже другой сайт.

Проблемы как раз нет. Это вставка на JavaScript и обрабатывать ее слишком затратно. Да и зачем? Прирост сайтов и так по экспоненте, только за счет одних явных ссылок с главной.

XML безнадёжно устарел, а будущее за такими форматами, как JSON

XML — это классика, а классика не может устареть. :) XML слишком гениален, чтобы когда-нибудь исчезнуть. А JSON может заменить XML только в AJAX. Он действительно компактней, но на этом его преимущества заканчиваются.

XML — это поэзия! :)


<?xml version="1.0" encoding="utf-8"?>

lenta.ru
Checked
lenta.ru/rss
http://lenta.ru

awaps.yandex.ru
Checked
No
http://awaps.yandex.ru

...

Самое сложное в работе загрузчика лент — автоматически определить, какие сайты чаще проверять на обновления, а какие реже

Так а как это определить-то?

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

Проблема в том, что у RSS-записей нет тегов, следовательно, придётся придумать способ формировать их самостоятельно.

Вообще-то есть. Тег category. Только доверять его содержимому нельзя, так как он заполняется не на основе реальных ключевых слов, а сочиняется автором статьи. Например, вот какие теги в RSS к этой статье:

Идеи
Интернет
Программирование
Тесты
Технологии
Системы управления контентом
СУБД

Кстати, и самостоятельно считать теги тоже не очень просто, так как объем текста, отдаваемого фидом, не всегда достаточен. Как я уже говорил, иногда вообще description пустой, только заголовок новости и ссылка на статью.

В JavaScript — document.documentElement.textContent

А какой именно элемент брать? Если сайт известен, то проблем нет. А если структура неизвестна? А если сайт к тому же использует табличную верстку?

Так это… «выявление дорвеев на марковских цепях» же. :)

Недостаточно текста.

А если серьёзно, то при помощи тегов это тоже можно решить, достаточно попросить пользователя ввести или выбрать из готовых интересные ему теги.

Замучается. К тому же, трудно просто так сесть и точно перечислить все свои интересы. Тут алгоритм хитрее. Надо смотреть по каким заголовкам кликает юзер и составлять профиль из ключевых слов. Потом программа может быстро пробегать по тысячам заголовков и искать вхождение ключевых слов из профиля. Но еще круче — и это решение напрашивается само собой — привинтить нейронную сеть. На вход подавать частотность ключевого слова, а на выходе получать значение от 0 (не интересно) до 1 (обязательно прочитать). Только пока я не понимаю как организовать входной слой. На каждое слово по входу? Никаких ресурсов не хватит. :)

Оценка: 
Средняя: 5 (1 оценка)

Думаю, кто-то когда-нибудь догадается как гармонично скрестить поисковые технологии с peer-to-peer и появится не менее потрясающий продукт. ;)

Думаю, такой продукт уже давно в разработке, просто о нём пока мало кто знает.

Так все равно придется обращаться к браузеру через привычный GetElementById. Эту же функцию предоставляет и HTML Agility Pack. Только браузер — это десятки мегабайт, а HAP — 1 dll-ка размером 150 кб. Или я что-то неправильно понял?

Это, наверное, я что-то неправильно понял. Просто я не очень хорошо представляю, что такое HTML Agility Pack (описание читал, но яснее не стало, чтобы было понятно, нужно попробовать самому или хотя бы посмотреть, как это делают другие).

В чем преимущества использования браузерного движка?

Например, в том, что не нужно самому парсить контент, отплясывая дикие танцы с регулярками. Задача решается при помощи простой функции на JavaScript:

var getRemoteDocument = function(url) {
var remoteDoc = null;
try {
var request = new XMLHttpRequest();
request.open('GET', url, false);
request.setRequestHeader("Content-Type", "text/html");
request.send(null);
var responseHTML = request.responseText;
remoteDoc = new DOMParser().parseFromString(responseHTML, "text/html");
}catch(e){
console.log(e);
}
return remoteDoc;
}

И последующего вызова её в сценарии через переменную:

var sitePageDocument = getRemoteDocument("протокол://имя.домен/каталог/подкаталог/подподкаталог/подподпод…");

К которой затем можно применять те же методы, что и к обычному документу:

var news = sitePageDocument.querySelectorAll(".newsblock");

Ну а далее как обычно. И если HTML Agility Pack тоже всё это умеет, то, совершенно согласен, полноценный браузер в таком случае и не нужен.

Там же 90% тратится на рендеринг, который персеру не нужен...

Логично.

Неа. Посмотрел сейчас собранную базу — встречается 4 вида основных расширений

А по mime-типу отловить не получится? Или тут тоже «кто в лес, кто по дрова»?

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

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

Проблемы как раз нет. Это вставка на JavaScript

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

Да и зачем? Прирост сайтов и так по экспоненте, только за счет одних явных ссылок с главной.

Во-первых, комментарии иногда бывают интереснее самой новости.
Во-вторых, «Нужно больше золота, милорд!» ;)

XML — это классика, а классика не может устареть. :) XML слишком гениален, чтобы когда-нибудь исчезнуть

Ваши бы слова да в уши разработчикам Mozilla, которые выкинули из своего продукта поддержку E4X, который как раз нужен был, чтобы автоматически парсить XML в определённых местах (конкретнее — в сценариях JavaScript, например), а также разработчикам Google, которые сделали известно что.

Вообще-то есть. Тег category.

А, я про HTML говорил. В нём, насколько знаю, такого тега нет. А жаль. Определённо не помешало бы иметь возможность задать в документе такую структуру:

Первый тег к новости
Второй тег к новости
Третий тег к новости


Парсеру явно было бы проще. Но пока этого нет. Наверное, придётся подождать HTML6…

Например, вот какие теги в RSS к этой статье

Мне кажется, тут не хватает самых главных: «Искуственный интеллект», «Работа с новостями», «Обработка данных». Хотя если нужно уложиться в заранее сформированный набор, то это понятно. Приходится в некотором смысле быть той самой обезьяной, которая сразу и умная, и красивая! :)

А какой именно элемент брать?

Если заранее не известно, то логично, наверное, взять body. Правда, мусора в этом случае будет много…

Если сайт известен, то проблем нет

А вот не факт. Некоторые, в том числе и как раз весьма известные, сайты очень любят менять вёрстку. Официально это делается для того, чтобы не отставать от «трендов современного веба», но фактически, это, заодно и вполне себе способ бороться с любителями парсить содержимое чужих сайтов, чтобы стимулировать их получать данные через API, доступ к которому обычно предоставляется небескорыстно. Вот только тот метод, который приведён выше, позволяет не сильно напрягаться по этому поводу — чаще всего переписать один или несколько сравнительно маленьких строчек с селекторами куда проще, чем переписывать внезапно ставшие бесполезными регулярки. И всё, в общем случае снаряд с этой стороны всё равно получается быстрее. :)

А если сайт к тому же использует табличную верстку?

Тогда либо придётся учитывать это в селекторах (а в особо тяжёлых случаях можно попробовать и jQuery прикрутить), либо убрать несколько явно лишних пробелов и/или переносов строк.

Недостаточно текста.

А простой спам тут не годится? Этого добра всегда полно. :)

Замучается. К тому же, трудно просто так сесть и точно перечислить все свои интересы

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

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

OK, Glass! ;)

Но еще круче — и это решение напрашивается само собой — привинтить нейронную сеть. На вход подавать частотность ключевого слова, а на выходе получать значение от 0 (не интересно) до 1 (обязательно прочитать). Только пока я не понимаю как организовать входной слой. На каждое слово по входу? Никаких ресурсов не хватит. :)

Нужно просто изобрести искуственный интеллект, который ни в чём не будет уступать человеческому, всего и дел-то. ;) Тогда многие проблемы отпадут сами собой! :)

Оценка: 
Средняя: 4.5 (2 оценки)

По поводу парсинга на JavaScript через движок. Вот как я брал адрес RSS-ленты со страницы:

HtmlDocument doc = new HtmlDocument();
doc.LoadHtml(html);
HtmlNode node = doc.DocumentNode.SelectSingleNode("//link[@type='application/rss+xml']");
string rss = node.Attributes["href"].Value;

На JS больше писать, а работать будет медленнее — не забывайте, что параллельных потоков много.

Оценка: 
Средняя: 4.5 (2 оценки)

Прочитал про этот HTML Agility Pack на Хабрахабре. Штука интересная, но querySelector и querySelectorAll он не поддерживает, да и о jQuery можно только мечтать. :( Зато есть XPath, что, конечно, не так круто (IMHO) но тоже неплохо.

На JS больше писать

В основном больше писать за счёт AJAX-запроса. Если же подключить jQuery, писать придётся ещё меньше.

работать будет медленнее

Это да. Интерпретируемый JavaScript, да ещё с довеском в виде браузерного движка, разумеется, будет медленнее, чем компилируемый C#. Но тут уже зависит от ситуации, требований и т. д. Лично мне для моих задач хватает более чем. Для сканера всея Интернета, конечно, не хватит.

Оценка: 
Средняя: 4 (2 оценки)

но querySelector и querySelectorAll он не поддерживает

О боже, а селекторы-то тут зачем? 8) Требуется же выдрать только одну ссылку заданного типа.

Для сканера всея Интернета, конечно, не хватит.

Для сканера и C# не хватит. Такие вещи пишут на C/C++. Но для быстрой проверки гипотез и прототипирования C# — самое то. В принципе, для прототипного программирования JavaScript тоже подходит, только лично я не переношу его. Какой-то он слишком говнистый.

Оценка: 
Средняя: 5 (1 оценка)

Попробуйте сравнить
"Akregator Version 4.11.5"
http://akregator.kde.org/

По идеи всё необходимое, тут уже работает.

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

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

Оценка: 
Средняя: 1.5 (2 оценки)

Соловей жжот! Этот кде-шный мусор научился сканировать интернет в поиске лент, хранить в базе сотни тысяч записей и проводить какую-никакую аналитику?! 8-O

в чем тогда «изюминка» новости, если уже знаешь что искать

А Вы подписываетесь на все новости без разбора: от макраме до сельского вестника? Или все же хоть немного представляете круг своих интересов? Ой, да чего я спрашиваю?!… ;)

Оценка: 
Средняя: 5 (1 оценка)

«Хозяйке на заметку» — не так давно на том же Хабрахабре была подборка инструментов для веб-разработчиков, так вот, там есть сервисы, которые могут быть полезны в данном мероприятии:

  • Pup — HTML-парсер, работающий из командной строки. Судя по описанию, умеет работать с CSS-селекторами! Адрес внутри Github — /EricChiang/pup
  • textract — программа для извлечения текста. Умеет извлекать текст из разных источников, в том числе HTML. Кажется, тоже умеет работать из командной строки. Адрес внутри Github — /deanmalmgren/textract
Оценка: 
Пока без оценки

О, за textact спасибо! Сейчас разбираюсь с этой библиотекой. HTML ей напрямую не парсится и юзеру просто предоставляется обертка к библиотеке Beautiful Soup на Пиздоне. Сейчас изучаю ее возможности. Увы, похоже на клон HTML Agility Pack, но может и продвинутые функции найдутся. Например, уже вижу автоматическое приведение кодировки к Юникоду, чего нет в HAP (есть, но не работает).

Вообще, в идеале было бы здорово получить что-то типа https://www.readability.com/. Сервис позволяет указать любую страницу в вебе и получить очищенный текст. Сейчас сервис работает только через Web API, но раньше выкладывались исходники на JavaScript. Их, кстати, использует Яндекс для некоторых своих сервисов.

И еще, знающие люди подсказали простой, но эффективный алгоритм вычленения текста: считать в каждом div количество точек, найти в котором больше всего и дальше выдрать привычным innerText. :)

Оценка: 
Пока без оценки

Сейчас сервис работает только через Web API

Ну я как раз примерно об этом и говорил…

И еще, знающие люди подсказали простой, но эффективный алгоритм вычленения текста: считать в каждом div количество точек, найти в котором больше всего и дальше выдрать привычным innerText. :)

Не факт, что этого всегда будет достаточно: может получиться так, что какой-нибудь комментарий будет больше по объёму и содержательнее, чем сама новость (см. нынешнюю Компьютерру, например). В этом случае скорее имеет смысл вычислять соотношение количества точек к общему количеству символов, и если оно подпадает под определённый процент, то рассматривать блок.
Опять же в тему недавно статья на Хабрахабре попалась: post/238359.

P. S. Есть предложение, чтобы не мучиться со ссылками, разрешить их таки публиковать, но в текстовом виде и автоматически оборачивать в < noindex >тег< /noindex >. Такие ссылки не должны индексироваться, следовательно, смысла накручивать счётчик ради поисковых ботов не будет, а для прочего мусора и так модераторы есть. :)

Оценка: 
Пока без оценки

может получиться так, что какой-нибудь комментарий будет больше по объёму и содержательнее, чем сама новость

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

Есть предложение, чтобы не мучиться со ссылками, разрешить их таки публиковать

Есть встречное предложение, чтобы не мучиться со ссылками, а заодно и с каптчей — зарегистрироваться. А публикацию ссылок анонимам ни в коем случае разрешать нельзя, ведь на этом выстраивается вся спамооборона. У ботов есть 3 отличительных свойства:

  • они не проверяют опубликован ли говнокомментарий;
  • они «печатают» комментарии достаточно быстро;
  • следующий комментарий будет опять со ссылкой.

Остается фиксировать неудачные попытки комментирования и передавать IP мудаков в iptables для блокировки. Просто и эффективно.

смысла накручивать счётчик ради поисковых ботов не будет

Да, остается только пустяковая задача — убедить в этом каждого заходящего спам-бота. Не думаете же Вы, что бот будет проверять свой опубликованный комментарий, не обернута ли ссылка в noindex/nofollow? Ботописатели даже статус 200/404 ленятся проверять.

а для прочего мусора и так модераторы есть

Тогда придется вводить предмодерацию, а я не думаю, что это слишком хорошая идея. Если не предмодерировать, то обилие ключевых слов типа «пенис» и «виагра» в комментарии может привести к тому, что поисковик наложит санкции или неверно станет трактовать содержимое страницы. К тому же, удалять чужой комментарий с поносом неприятно, а я хочу, чтобы Либератум приносил всем только счастье, радость и блаженство, как пользователям, так и модераторам. ;)

Оценка: 
Пока без оценки

Комментировать

Filtered HTML

  • Use [fn]...[/fn] (or <fn>...</fn>) to insert automatically numbered footnotes.
  • Доступны HTML теги: <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd> <blockquote> <strike> <code> <h2> <h3> <h4> <h5> <del> <img>
  • Адреса страниц и электронной почты автоматически преобразуются в ссылки.
  • Строки и параграфы переносятся автоматически.

Plain text

  • HTML-теги не обрабатываются и показываются как обычный текст
  • Адреса страниц и электронной почты автоматически преобразуются в ссылки.
  • Строки и параграфы переносятся автоматически.