Пользователь
0,0
рейтинг
30 августа 2013 в 12:35

Разработка → Следим за голосованием на «Россия 10» из песочницы

Как и многие россияне, в последнее время я каждый день захожу проголосовать на сайт 10russia.ru. Если кто не в курсе, Россия 10 — всероссийский проект, в рамках которого каждый может проголосовать за свой любимый географический или архитектурный объект в России. Задача проекта – выбор десяти новых визуальных символов России.
Мне показались странными цифры в ТОП2 в голосовании, и я решил посмотреть, как они меняются. Было мало времени, и на скорую руку написал маленький парсер, который сохраняет раз в 2-3 секунды данные по количеству голосов на сайте и смс. Для отображения этих данных создал сайт (За основу был взят дизайн и основа графика отсюда habrahabr.ru/post/176547. Надеюсь, автор будет не против ). Денег не хотелось тратить, а в распоряжении был только личный слабенький VDS, который бы быстро лёг, если бы данные генерировались динамически. Поэтому решено было обойтись статическим html и генерацией по крону json файлов. Интересно будет посмотреть, какую нагрузку выдержит VDS без изменения текущей конфигурации (CPU 300MHz, RAM 128 Mb), учитывая, что там крутится два небольших сервиса и один малопосещаемый сайт. В конце голосования выложу, если кому надо, все полученные данные голосования.

Листинг парсера


<?php
set_time_limit(0);
function handleError($errno, $errstr, $errfile, $errline, array $errcontext) {
    if (0 === error_reporting()) {
        return false;
    }
    throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
}
set_error_handler('handleError');
mysql_connect('localhost', 'login', 'password');
mysql_select_db('10russia');

function addNewData($html, $key) {
    $site = $sms = 0;
    if( preg_match('/<span id="count_votes_site">([^<]+)/iu', $html, $match) ) {
        $site = (int)str_replace(' ', '', $match[1]);
    }
    if( preg_match('/<span id="count_votes_sms">([^<]+)/iu', $html, $match) ) {
        $sms = (int)str_replace(' ', '', $match[1]);
    }
    if($site && $sms) {
        $all = $sms + $site;
        mysql_query("INSERT INTO stat(`key`, sms, site, `all`) VALUES('$key', $sms, $site, $all)");
    }
}

$urls = array(
    'object_31' => 'http://10russia.ru/object_31',
    'object_61' => 'http://10russia.ru/object_61'
);
while(true) {
    foreach($urls as $key => $url) {
        try {
            $result = file_get_contents($url);
            addNewData($result, $key);
            sleep(1);
        } catch(Exception $e) {}
    }
}


Листинг скрипта для создания по крону json файлов


<?php
mysql_connect('localhost', 'login', 'password');
mysql_select_db('10russia');

$keys = array(
    'object_31' => 'object31.json',
    'object_61' => 'object61.json'
);

foreach($keys as $key => $file) {
    $filename = dirname(__FILE__) . '/' . $file . '.tmp';
    $fLink = fopen($filename, 'w');
    if($fLink) {
        $result = mysql_query("SELECT `date`, `all` FROM stat WHERE `key`='$key' ORDER BY id ASC");
        fwrite($fLink, "[\n");
        $row = mysql_fetch_assoc($result);
        $first = true;
        do {
            $str = '';
            if(!$first){
                $str = ",";
            } else {
                $first = false;
            }
            fwrite($fLink, $str . '['.strtotime($row['date']) . "000,{$row['all']}]" );
        } while($row = mysql_fetch_assoc($result));
        fwrite($fLink, "]");
        fclose($fLink);
        rename($filename, dirname(__FILE__) . '/' . $file);
    }
}

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

Интересные моменты на графиках


Максимальный прирост за 30 минут
Максимальный прирост за 30 минут

Общая динамика за всё время мониторинга
Общая динамика за всё время мониторинга

ВГТРК


Я надеюсь что не создал для вас больших проблем, учитывая большую популярность проекта, вы, думаю, были готовы к большому потоку посетителей. Кстати, если кто ищет в Москве работу системного администратора, то создатели сайта ищут себе сотрудников, уже не совсем оригинальным, но редким способом:
Работа

Ссылки


Посмотреть, что у меня получилось, ознакомиться с детальной статистикой голосования и сделать выводы: http://10russia.miningdata.ru/
Андрей Попов @Nord001
карма
1,0
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Спецпроект

Самое читаемое Разработка

Комментарии (44)

  • +8
    Вот было бы интересно получать оперативную статистику и графики в реальном времени 8 сентября…
  • +2
    У меня почему-то оба графика оранжевого цвета. UPD: после обновления страницы цвета поправились.
    Колоссальный прирост голосов за последние сутки у меня вызывает недоумение: как будто акция какая-то была…

    И удивляет отсутствие Красной Площади среди кандидатов. Я всегда думал, именно она приходит на ум у иностранцев при слове «Россия».
    • 0
      Колоссальный прирост голосов за последние сутки у меня вызывает недоумение: как будто акция какая-то была…

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

      Меня больше интересует за что там 700к голосов сняли 29 августа в 22:40…

      И удивляет отсутствие Красной Площади среди кандидатов.

      Я с вами согласен, но, вроде бы это специально сделано:
      Задача проекта – выбор десяти новых визуальных символов России посредством общенародного голосования.
  • +10
    Если честно, не вижу никакого криминала. Вполне нормальный пик. Нормальная форма.
    Вполне может объяснятся вбросом призыва голосовать на каком нибудь посещаемом ресурсе.

    п.с. а вообще слишком внимания идиотскому конкурсу. При том, что организаторам нет доверия. Нет никаких гарантий что требуемые голоса не подкрутят в политических целях. Когда я увидел в третий раз призыв голосовать за эту хрень, пошел и проголосовал за самый абсурдный вариант. Мечеть Кадырова.
    Мне как атеисту не нравится переизбыток религиозных объектов на конкурсе. Для меня символами России являются Первый Спутник, ВДНХ, Памятник Неизвестному Солдату а не церквы и мечети.
    • 0
      Ага, ну хоть бы на хабр эту грязнь не выносили, кремлю накручивает администрация Коломны, мечети накручивают то ли битарды то ли лично Кадыров, а нормальные объекты типа Байкала на пару порядков от всего этого балагана отстают.
    • +5
      Мир вам, Господе Иисусе. Надо голосовать за мечеть Кул Шариф в Казанском Кремле. Остальное банально.
      • +4
        Дожили. Символ России — мечеть!
        • +2
          Нормальный такой символ современной России, вполне соответствующий тенденциям…
        • 0
          Это всё-таки музей, культурно-просветительское заведение. И она очень красивая. И это не единственный символ, а один из новых.
          Кстати, церквей разных у нас с десяток символов: ХХС, Василия Блаженного, кремлёвские все, Исакиевский, Казанский — куча их.
    • 0
      Если честно, не вижу никакого криминала. Вполне нормальный пик. Нормальная форма.
      Вполне может объяснятся вбросом призыва голосовать на каком нибудь посещаемом ресурсе.

      Если вы это мне, то я просто показал интересные моменты на графике, что бы статья покрасивее была. Просто на момент написания статьи, это был наверное один из интересных моментов.
      Хотя, конечно, это блекнет по сравнению с 2 миллионами за пол часа в последний день голосования.
  • 0
    Это ни для кого не секрет. Президент Чечни лично и открыто продвигал эту мечеть. Когда она стала лидировать, остальные стали продвигать Коломенский кремль (я правда не понял почему именно его) чтобы мечеть не победила. Ну и сам конкурс сделан так, что накручивается очень легко. Можно почитать тут: http://lenta.ru/articles/2013/08/16/heartof/
    • 0
      Уже снял с голосования lenta.ru/news/2013/08/30/mosque/
      • –1
        Весело, почти 40 лямов голосов, даже верующих масульман столько не наберется, пытались накрутить, а когда дали по шапке обиделись.
        • +2
          Да тут сумма голосов за первые два места перекрывает, мне кажется, на порядок количество людей, участвовавших в конкурсе.
          • 0
            Там по правилам можно было много раз голосовать: платными СМС — неограниченно, а на сайте — сколько-то раз в день (вроде 5)
            • 0
              3
              • 0
                9 с одного айпи. Т.е. можно разными браузерами или удаляя куки.
      • 0
        Спектакль
  • +1
    Собственно, как и обещал, собранные данные — может быть кому пригодятся:
    10russia.sql.gz
  • +2
    А только я недоумеваю почему SMS платные? Средний доход с такой смски(за 3.X рубля) от 0.6 до 1.5 рублей. Учитывая количество проголосовавших. Я тащусь от людей которые это придумали.
    • +1
      Просто гении. Искренне завидую их дальновидности =)
    • 0
      Ага. Если в цифрах, то примерно в ситуации с ТОП 2, получается:
      32 421 805 голосов через смс, это 10 807 268,33 смс или примерно 38 041 584,53 рубля
      37 141 324 голосов через смс, это 12 380 441,33 смс или примерно 43 579 153,49 рубля
      • 0
        Учитывая, что они теперь потеряли рынок — это не гениальность и дальновидность, а грандиозный, эпический фейл.
        • 0
          Вы сами то в это верите?:) Да и мечеть эту не сняли с голосования;)
  • +1
    Когда первый раз увидел этот конкурс, подумал — гениальные люди: обернули простейший холивар в красивую обертку, да еще и бобло на этом подняли. Можно же вообще кого угодно так же красиво стравить и поддерживать «баланс» сил и напряжение.
    И прошу прощения у верующих, но если символ страны — это религиозная постройка, то всё очень печально.
  • 0
    Ну вот выступил Рамзан по какому-нибудь радио — и айда. В общем, нет здесь ничего особо удивительного. Будет правильно, если эта мечеть победит — всё-таки самый настоящий символ всего того, что творится в нашей стране в последние годы. Мече-мече-мече-мечеть, оле-оле-оле-оле!
  • 0
    /*offtop*/ Забавно, но ваш график крашит safari на ipad 2. Пятый раз подряд — зависаю ->черный экран
    • 0
      Хм. Интересно. Скорее всего это из-за того что суммарно загружается 850к записей.
  • 0
    Судя по всему, Вы с пхп познакомились недавно. Несколько советов:

    1. Не делайте пустой блок catch. Вы так никогда не узнаете, что у Вас сломалось в скрипте. Добавьте в этот блок запись ошибок в лог.

    2. Вместо регулярных выражений используйте DOMXPath

    3. Экранируйте переменные (строки) в sql-запросах при помощи mysql_real_escape_string (а лучше используйте PDO или MySQLi)

    4. Для создания json используйте json_encode

    5. Пишите в файл с помощью file_put_contents
    • +3
      Вы ошибаетесь, я на PHP программирую уже шестой год.
      Про список того что вы написали, скажу следующее, в принципе часть советов(1, 3, 4) правильна, но не в моём случае.

      1. Данная конструкция с
      set_error_handler('handleError');
      и пустым блоком catch

      Была нужна что бы не было Warning ошибок когда удалённый сервер отдаёт 404. (file_get_contents вместо тогоже cUrl был выбран исключительно для того что бы морально оправдать парсинг. Раз ВГТРК не банит такие запросы, то они не простив парсинга).
      Знаете способ элегантнее и проще? Поделитесь. Не было бы её, то в перспективе у меня бы мог лог ошибок разрастись и занять всё свободное место на HDD( не забываем что цель была в ограниченных ресурсах всё это запускать, и кстати, в конечном итоге всё отработало супер и полторы недели парсинга и «хабраэффект» почти не отобразилось на работе сервера).

      2. В данном случае preg_match для меня намного проще. Для более сложного, в плане логики, парсинга, я предпочитаю использовать PHP Simple HTML DOM Parser. Но ради интереса надо будет попробовать сравнить в конкретно этом случае производительность DOMXPath и preg_match по потреблению памяти и скорости работы.

      3. В моём случае мне нечего экранировать. Вообще конечно совет новичкам обязательный, все грешат по началу SQL инъекциями.

      4 и 5. Ну очень сомнительный совет. Простите, это для меня даже прямо таки вредный совет.
      Делать надо так что бы запустил и забыл а оно само работает. Мой код мог бы спокойно работать очень долго и объём требуемой для работы ОЗУ был бы всегда одинаковым.
      А в случае который вы мне предлагаете, мне надо было бы сначала создать массив с данными в ОЗУ, затем из него сделать json строку, удалить массив и всю эту строку записать через file_put_contents. И чем больше было бы в БД записей то тем больше он будет кушать ОЗУ и следовательно кончиться тем что отвалиться с PHP Fatal error: Allowed memory size of… :)
      • 0
        Более того, вместо регулярок иногда хватает explode/strpos и другие подобные штуки. Помню XML-ку надо было распарсить, а она под гиг, а у клиента толи 16, толи 32 метра памяти под скрипт, попутно сравнивания с данными из базы, еще под гиг данных. Готовые парсеры предлагали грузить всю XML-ку в память и создавать из неё массив… с соотестствующим результатом.
        Кстатии может кто подскажет готовые хорошие парсеры под конфиги линуксовых демонов (apache, nginx, samba, proftpd, bind9 и другие)?
        • 0
          XML-дамп Википедии нормально парсится и без извратов с explode/strpos. php.net/manual/en/book.xmlreader.php
          • 0
            Когда этот самый парсер есть. А когда дефолтный php4 (я не опечатался) без всего и мало памяти, то приходится извращаться. Всё от ситуации зависит. Нынче кодю на сервере с 32 гигами оперативы (меньше у хостера не было), крайне сложно получить фатал еррор о нехватки памяти.
            explore был не парсинг xml, если что, но грешу этим.
            • 0
              А когда дефолтный php4 (я не опечатался) без всего и мало памяти, то приходится извращаться. Всё от ситуации зависит.

              Согласен.

              Нынче кодю на сервере с 32 гигами оперативы (меньше у хостера не было), крайне сложно получить фатал еррор о нехватки памяти.

              xmlreader не загружет весь файл в память.
            • 0
              на сервере с 32 гигами оперативы (меньше у хостера не было), крайне сложно получить фатал еррор о нехватки памяти

              Кстати, легко. Все зависит от memory_limit
              • 0
                Поверьте, при 32 гигах памяти, memory_limit не пару килобайт выставлен, но и без фанатизма. Да и скрипты изредка порог даже в 200 метров переплевывают после проведенных оптимизаций.
      • 0
        что бы не было Warning ошибок когда удалённый сервер отдаёт 404

        file_get_contents может бросить варнинг и по другой причине, но Вы об этом не узнаете.
        preg_match может бросить варнинг, но Вы об этом не узнаете.
        mysql_query может бросить варнинг, но Вы об этом не узнаете.
        ну и конечно же любой нотис будет преобразован в исключение и Вы снова об этом не узнаете.

        file_get_contents вместо тогоже cUrl был выбран исключительно для того что бы морально оправдать парсинг. Раз ВГТРК не банит такие запросы, то они не простив парсинга

        ВГТРК не знает что Вы используете. Разница только в заголовках (юзер-агент в частности). Я бы использовал curl, т.к. не надо городить с обработкой ошибок.

        надо будет попробовать сравнить в конкретно этом случае производительность DOMXPath и preg_match по потреблению памяти и скорости работы

        Регулярки выиграют.

        В моём случае мне нечего экранировать

        Верно. Тогда нужно всегда помнить, что в $key нельзя писать все подряд, а только то, что не сломает запрос.

        в случае который вы мне предлагаете, мне надо было бы сначала создать массив с данными в ОЗУ, затем из него сделать json строку, удалить массив и всю эту строку записать через file_put_contents. И чем больше было бы в БД записей то тем больше он будет кушать ОЗУ и следовательно кончиться тем что отвалиться с PHP Fatal error: Allowed memory size of… :)

        Согласен. Но зачем создавать json, который в память не влазит? Я, типа, сгенерил и память сэкономил, а клиент пусть загружает его как хочет?
        • +1
          file_get_contents может бросить варнинг и по другой причине, но Вы об этом не узнаете.
          preg_match может бросить варнинг, но Вы об этом не узнаете.
          mysql_query может бросить варнинг, но Вы об этом не узнаете.
          ну и конечно же любой нотис будет преобразован в исключение и Вы снова об этом не узнаете.


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

          ВГТРК не знает что Вы используете. Разница только в заголовках (юзер-агент в частности). Я бы использовал curl, т.к. не надо городить с обработкой ошибок.


          Именно, разница в заголовках. Не посылается не user agent, не referer, не cookie. Первое что делают когда делают защиту от парсинга, отсекают всех у кого пустой user agent. По крайней мере об этом говорит моя практика.

          Согласен. Но зачем создавать json, который в память не влазит? Я, типа, сгенерил и память сэкономил, а клиент пусть загружает его как хочет?


          Вот это единственный момент, который я действительно пропустил, спасибо!

          Вам не кажется, что вы сильно всё усложняете, я сам парсер написал минут за 15, в конечном итоге он без проблем проработал всё это время, снабжая интересующими меня данными. Разве не скорость написания, надёжность и быстрота работы являются одними из важных критерий, по которым можно и, даже не побоюсь этого слова, нужно оценивать работу программы? А вообще, это, наверное, хороший пример принципа KISS.

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

          Собственно, инвайт на Хабрахабре я и захотел получить, ради хорошего спора с коллегами, потому что уже три года работаю единственным программистом в веб-студии и остро нуждаюсь в общении по профессии, ведь как известно, в споре рождается истина.
  • 0
    Ок
    • 0
      Сервис падал наверное
      • 0
        Там где большая дырка – да. Меня больше интересует «пила» в левой части. Получается, падала только «мечеть».
        • 0
          Нет. По моему мнению, «пила» у мечети ярко выражена, потому что голосов с сайта почти не поступало. А у кремля на сайте побольше голосовало, что и сглаживало зубцы.
          Собственно поэтому, мне кажется, линии и идут почти параллельно, потому что смс шлюз захлёбывался и если и выдавал, то выдавал одинаковое кол-во смс голосов.
          Так что обвинение ОпСоСов в том что они специально что-то там портили для меня является популизмом и не более.
      • 0
        СМС шлюз, голоса сайта нормально защитывались
  • +1

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.