Skip to content

Мини-проект: исследование Хабрахабра

31/07/2010

На днях я, вдохновлённый этим постом на HackerNews, решил тоже проверить кого чаще всего фолловят пользователи Хабрахабра. И хотя окончательных результатов ещё нет (хабрапост будет ориентировочно в понедельник), кое-какие предварительные вещи можно написать.

Методика исследования

Вначале я планировал исследовать всех хабражителей, но взглянув на их количество на сайте решил ограничиться самыми активными. Из списка отсортированного по рейтингу были взяты топ 2000 пользователей. Потом у этих пользователей были получены твиттер-аккаунты из их хабрапрофилей. И в конце концов через Twitter API были запрошены списки — кого фолловит каждый хабражитель.

Техническая часть

Большая часть работ проводилась с помощью модифицированного спайдера на Node.js. В качестве системы хранения награбленного была выбрана CouchDB. Всего было сделано три вариации спайдера и один скрипт для окончательной обработки результатов (в самой CouchDB этого сделать не получилось). В результате работы было сделано несколько выводов:

Node.js

  • API почти стабилизировалось. Чтобы портировать спайдера с версии 0.1.90 до 0.1.102 потребовалось изменить всего один метод (SetBodyEncoding переименовали в SetEncoding)
  • С асинхронным кодом работы с БД надо быть очень осторожным. V8 — чрезвычайно быстрый движок, и при асинхронной обработке ему не составит труда открыть 200-300 соединений с базой (а то и больше) и ждать ответа от всех сразу. При неряшливом программировании будут постоянно заканчиваться то открытые соединения, то файловые дескрипторы, то бог знает что ещё.
  • Надо делать очереди для сопряжения тех частей системы которые работают с разной скоростью. Вообще надо сделать какой то общий модуль очереди, я что то слишком часто с этим сталкивался.

CouchDB

  • CouchDB неудобна для обработки данных. Да, там можно создать map/reduce вьюшку, но потом эти данные будет сложно куда нибудь перенести. Я например так и не нашёл простого способа сделать map/reduce на результатах вьюшки. Также вьюшку нельзя отсортировать по значению — только по ключу. Я, например, так и не понял как в CouchDB сделать классический пример map/reduce — список самых часто встречающихся в тексте слов.
  • Впрочем, обработать данные — ещё пол-дела. Вторая проблема — достать их оттуда в каком нибудь пригодном для дальнейшей работы формате. Я просто дампил страницу REST API в json-файл, который потом всё равно особо никуда не загрузишь.
  • Даже если CouchDB отдаст вьюшку, не факт что удастся получить её целиком. Я столкнулся со странными проблемами когда вьюшка открывалась не полностью, и CouchDB просто сбрасывал соединение, не отдав данные до конца. Причём это, судя по всему, какой то встроенный таймаут — скачивание идёт ровно 1 минуту 40 секунд, после чего в файл сбрасывается сообщение об ошибке. При этом Couch отдаёт этот файл со скоростью 5 кб/сек, соответственно размер его получается чуть более 600 килобайт.
  • У CouchDB (по крайней мере моей версии 0.8.0) есть проблемы со вьюшками, когда они просто не хотят открываться ни в какую. Помогает перезапуск сервера.
  • Единственный плюс CouchDB: удобная встроенная админка. Но я в следующий раз всё равно буду использовать Riak.

Twitter и Хабрахабр

  • На Хабрахабре зарегистрированные и незарегистрированные пользователи видят немного разные страницы пользователей. В принципе вполне логично, но я на это попался.
  • На Хабре страница пользователя свёрстана достаточно странно, кучей вот таких конструкций:
    <dl>
        <dt>Свойство</dt>
        <dd>Значение</dd>
    </dl>
    

    Чтобы найти значение, хранящее твиттер-аккаунт пользователя, нам надо получить все элементы dl со страницы (их от 20 до 70) и последовательно перебрать в поисках того, у которого значение dt равно Twitter:

  • В API Твиттера используется много кодов статуса, в том числе для того чтобы показать что Вы вышли за разрешённую частоту запросов (150 запросов в час). Все эти коды статуса нужно понимать и правильным образом обрабатывать.
  • Твиттер оперирует не именами пользователей, а их id. То есть, запрашивать можно и по именам, но в ответах будут всегда приходить id-шники. Поэтому последний шаг (после подсчёта и сортировки) — преобразование id в юзернеймы.

Предварительные результаты

На данный момент обработано примерно 30% данных, и уже можно делать предварительные заключения. Вообще, я думал что самым популярным пользователем окажется @shoohurt или @habrahabr, но нет. По предварительным результатам самый популярный среди хабражителей твиттер-пользователь — @KremlinRussia. За ним идут дружным строем @shoohurt, @bobuk, @umputun и @boomburum. Также интересно присутствие в предварительной десятке @KermlinRussia (не путать с кандидатом на первое место) и @mio. Остальные результаты — в понедельник на Хабрахабре (там будет топ-20, думаю).

P.S. и да, больше я CouchDB использовать не собираюсь, и другим не посоветую. Обновление до 1.0 не решило ни одной из проблем. Проблема с таймаутом — вообще что то за гранью добра и зла.

P.P.S. Частично беру свои слова обратно — проблема была в инсталляции CouchDB. При переустановке с нуля версии 1.0 всё заработало. К тому же, оказалось что один из спайдеров пропустил несколько твиттер-аккаунтов, поэтому финальные результаты несколько отличаются от результатов первого раунда. Относительно CouchDB меняю рекомендацию на «не ставить через apt-get»🙂

Ссылки по теме

9 комментариев
  1. Василий Гладков permalink

    Неужели всё настолько плохо с каучем? Читаю сейчас Definiteve Guide и на первый взгляд кажется, что сделать можно практически всё, вообще без sql только на нём.
    Вот например вьюшка с часто встречающимися в тексте словами — в map функции режем доку на слова по пробелам и делаем emit({doc_id, word}, 1) для каждого слова.
    А в reduce:
    function (key, values, rereduce) {
    return sum(values);
    }

    А потом читаем этот вид с фильтом по doc_id и сортировкой по убыванию. Или так нельзя?

    • Мы можем сделать сортировку только по ключу, не по значению, насколько я понял. Т.е. вывести этот список в алфавитном порядке мы можем, а с сортировкой по количеству вхождений — нет. Впрочем, возможно, я просто что то неправильно делаю.

      Как именно в Definitive Guide предлагается сортировать результаты вьюшки?

      • Василий Гладков permalink

        Всё правильно, сортировать можно только по ключу, сначала написал а потом понял, что ерунда какая-то. Только читаю, даже не пробовал ещё на практике.
        Да, таки составить топ самых частых слов можно — вот здесь есть объяснение. Но работает это только благодаря недокументированной фиче кауча, которая может исчезнуть — http://wiki.apache.org/couchdb/View_Snippets#Retrieve_the_top_N_tags
        А на стековерфлоу даже говорят, что простого решения задачи нет и вообще…

        • Вот про это примерно я и пишу. Задача тривиальная донельзя, но простого решения всё равно нет. Печально это. Впрочем, для простого хранения и (очень) несложной обработки CouchDB вполне годится.

          Пример по ссылке — это жесть настоящая. Чтобы подсчитать количество вхождений каждого слова, нужна однострочная вьюшка (встроенная функция _sum работает так же как то что вы написали, только быстрее). А чтобы просто отсортировать полученный результат, надо увеличить reduce до 40 строк плюс полагаться на недокументированные функции. Ужас ведь.

  2. Василий Гладков permalink

    Могу только поделиться общими мыслями.

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

    Вот например ещё одна задача — сортировка фотографий в галерее по популярности или количеству поставленных плюсиков в лоб не решается по той же причине — нельзя сортировать по результату reduce.

    Но на SQL она не решается тоже! Я имею ввиду большие объёмы данных и разумное время отклика. Можно сделать с подзапросом, или сортировать по агрегатному полю (сумме плюсиков). MySql для вычисления таких запросов тихо создаёт временные таблицы, чаще всего даже на диске и дела становятся совсем плохи, когда нужна ещё и постраничная навигация.

    И это очень правильно.

    В нашей маленькой уютной соцсети, в разработке которой я участвую, как часто, видимо, и делается в подобных случаях, в базу вводится избыточное поле — количество плюсиков за картинку. Которое индексируется и используется для сортировки в рейтинге. Если переводить на язык MapReduce — я делаю reduce вручную ещё во время записи в базу каждого плюсика, а потом для выборки рейтинга по убыванию использую ВТОРОЙ map, индекс по полю «количество плюсиков».

    • В Couch нельзя сделать второй map/reduce на результатах первого, вот в чём проблема. Если бы было можно, половина проблем бы отпала. В Riak можно вообще сколько угодно map/reduce/walk шагов соединять в одном запросе, там и не нужна сортировка по результату — достаточно ещё раз сделать map и отсортировать по чему нужно.

      Избыточность это конечно решение, но хотелось бы всё таки нормальной реализации в самой базе. В MySQL, кстати, при нормальной индексации думаю эту задачу можно было бы решить. В Riak её можно решить. С Couch приходится выдумывать костыли. Печально это.

  3. Василий Гладков permalink

    Мир не идеален:)
    Я тут бегло почитал документацию по риаку — у него тоже есть недостатки в сравнении с каучем, кажется. Неправильно сказал, не недостатки — особенности.

Trackbacks & Pingbacks

  1. Кого фолловят хабрапользователи? | Твиттер блог

Добавить комментарий

Заполните поля или щелкните по значку, чтобы оставить свой комментарий:

Логотип WordPress.com

Для комментария используется ваша учётная запись WordPress.com. Выход / Изменить )

Фотография Twitter

Для комментария используется ваша учётная запись Twitter. Выход / Изменить )

Фотография Facebook

Для комментария используется ваша учётная запись Facebook. Выход / Изменить )

Google+ photo

Для комментария используется ваша учётная запись Google+. Выход / Изменить )

Connecting to %s

%d такие блоггеры, как: