Создаем PERL-парсер

Тема в разделе "Софт и инструменты", создана пользователем CLAY, 21 апр 2017.

  1. CLAY

    CLAY Member

    Сообщения:
    419
    Симпатии:
    5
    Еще один PERL-парсер

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

    Начинается все с того, что у меня есть две сохраненные странички: 20100801.html и 20100901.html. Это просто сохраненная, соответственно, первого августа и первого сентября страница Ссылка доступна только зарегистрированным пользователям, то есть данные на начало месяца и на конец.

    Работу (если так можно назвать вырезание частей скриншотов и помещение их в один файл) с графикой я произвожу обычно в GIMP или Paint, в зависимости от используемой операционной системы. Чаще GIMP.

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

    В крайней правой колонке что-то вроде =E13-B13, растянутое на всю таблицу. То есть разность между рейтингом в начале августа и в начале сентября. Но уже с четвертого места произошли изменения в занимаемом месте. И таких изменений иногда бывает очень много. Фактически до какого-то момента неизменной оставались лишь шесть первых мест, а остальные постоянно перетасовывались. Кроме того, уже в первом же обзоре выяснилось, что участник легко может сменить ник. Соответственно, привязка к нику тоже может подвести.

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

    Какие данные нам потребуются? Во-первых, дата. Сначала я хотел брать число прямо со страницы:

    Однако, иногда там написано просто:

    Поэтому дату я стал брать из названия файла.

    Вместо ника нужно брать id-пользователя. Его можно узнать из ссылки на профиль - Ссылка доступна только зарегистрированным пользователям, соответственно у alextim id=844.

    Ну а текущий рейтинг - это просто текущий рейтинг.

    Таким образом таблица в MySQ имеет всего 4 поля - дата, id, ник, рейтинг.

    Вот и переходим к скрипту, который все это обрабатывает.

    Начинается все с подключения к базе:

    Запрос DROP TABLE IF EXISTS "ratio"; удалит уже существующую таблицу. Это было сделано в начале в целях отладки - приходилось постоянно в ручную удалять введенные данные, чтобы они не путались с новыми и было проще искать ошибки, если они возникнут. Потом запрос так и остался.

    Затем мы сканируем каталог, с сохраненными файлами и получаем из имен даты:

    В переменную $filename по очереди помещаются имена файлов, находящихся в каталоге HOMEDIR. Имя каталога задается в переменной $dir. При этом, если имя файла равно точке или двум точкам, то обработка файла не производится. Что за файлы имеют такие странные имена? Об этом можно узнать в википедии. Коротко - .. обозначает каталог, находящийся на один выше текущего, а . - текущий каталог. В общем, чтобы не отвлекать вас ненужными подробностями - каждый каталог содержит файлы с такими именами, но нас они не интересуют.

    Так как файлы имеют формат названия <date>.html, то из него нужно удалить .html. Для этого делим имя файла по точке: split( /./, $filename ); и сохраняем то, что до этой самой точки: $filename = $temp[0]; И сохраняем полученное в переменную $date = $filename;

    После этого, выбранный и открытый файл помещается в массив, в котором он разделен на отдельные строки: @file_to_parse = <INFILE>;

    Теперь из всех строк нам нужно выбрать ту, в которой содержатся данные о рейтинге. Такая строка в файле всего одна, но она здоровущая. Ее можно найти, открыв код страницы с рейтингом и поискав по "td class= topname"

    Но перед обработкой необходимо конвертировать строку из по непонятным причинам используемой кодировки windows-1251 в куда более современный и общепринятый utf-8. Все это и происходит вот тут:

    Строка выбрана. Как же из нее выбрать id и рейтинг? Сначала ее нужно разделить по ключевой фразе: Ссылка доступна только зарегистрированным пользователям. После тире идет как раз id. Таким образом у нас получится массив, первым элементом которого будет ненужный кусок, от которого мы избавимся, а остальные куски будут начинаться с цифр id. Поэтому от первого элементы мы избавляемся: shift(@stage1);, а остальные строки будут выглядеть как-то так:

    Как видно, достать id нетрудно - достаточно взять часть строки до первых кавычек. Это делаем следующим циклом:

    Делим строку по кавычкам и сохраняем первый элемент.

    Для получения количества баллов эту же строку нужно поделить так:

    Чтобы добраться до цифр, подчеркнутых красным, нужно резать по ключевой строке, подчеркнутой синим. А потом отбросить все, что после цифр. За это отвечает такой код:

    В итоге, мы получаем два массива, состоящих из одинакового количества элементов. Один содержит id, второй - рейтинг. При этом каждому элементу одного массива строго соответствует такой же элемент второго массива.

    На данном этапе можно помещать в базу все собранные данные:

    Перебираем массивы @Ratio и @UIDs. Помещаем в базу дату, полученную из имени файла в самом начале, id пользователя из массива @UIDs, вместо ника пока вставляем в базу -1, и текущий рейтинг из массива @Ratio.

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

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

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

    Для пущего удобства можно внести в базу и ники. Это просто сделать имея номера. Автоматически формируем список вида: Ссылка доступна только зарегистрированным пользователям + номер, а затем парсим скачанные страницы с профилями. Потом просто обновляем таблицы запросом UPDATE "ratio" SET name="$name" WHERE uid="$id"; и таблица будет уже такой:

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

    Вот такая автоматизация и позволяет мне делать обзоры рейтинга довольно оперативно и вроде бы даже без ошибок.

    Но есть еще одна деталь, которую не подскажет ни один скрипт - отношение ко всем участникам, как живым людям, а не просто как к цифрам на страничке. Так что вы уж постарайтесь быть еще активнее и заметнее, чтобы было чем подбодрить и поддержать каждого!

Поделиться этой страницей