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

CLAY
Оффлайн
Регистрация
25.01.17
Сообщения
763
Реакции
225
Репутация
292
Еще один 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';" и таблица будет уже такой:

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

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

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