Отправляет email-рассылки с помощью сервиса Sendsay

KirovLUG: пользователи Linux в Вятке

Парсинг строк логов и не только без использования regexp на Python

Парсинг строк логов и не только без использования regexp на Python

Как-то давненько я писал патч для SARG, который бы позволял последнему
брать статистику по логам не только SquidGuard, но и нашей,
отечественной программы REJIK. Там я реализовал механизм, позволявший в
итоге парсить логи любой программы - в конфигурационном файле необходимо
было только правильно составить формат-строку, описывающую формат одной
строки журнала.
Но речь не об этом. Для тренировки я решил написать на python класс,
который бы позволял делать нечто подобное - парсить по заданной
формат-строке любые входные данные. Это было бы очень удобно тем, что не
зная тонкостей составления регулярных выражений (regexp) можно было бы
обрабатывать не только файлы логов, но и вывод различный скриптов,
например, парсить вывод скрипта выводившего информацию для lindocs.

Итак, вот что у меня получилось:

begin UParser.py #!/usr/bin/python
# -*- coding: UTF-8 -*-

class UParser:
def __init__(self, format_string):
# Список описывающий формат-строку. Каждый элемент либо ключевое
# слово, либо группа символов - "разделитель" между двумя
# соседними ключевыми словами
self.fmt_list = []
# Флаг того, что список начинается с "разделителя"
self.flSepFirst = 0
self.set_format_string(format_string)

def set_format_string(self, format_string):
"""Устанавливает новую формат-строку для экземпляра класса"""
# В формат-строке ключевые слова выделены шарпом (#), поэтому
# необходимо выделить их.
self.fmt_list = format_string.split("#")
self.flSepFirst = 0
# После split в первом элементе списка формат-строки - может
# быть <пустая строка> в том случае, если она начинается сразу с
# ключевого слова. Необходимо убрать этот элемент
if self.fmt_list[0] == '':
self.fmt_list = self.fmt_list[1:]
else:
self.flSepFirst = 1
# Если формат-строка заканчивается ключевым словом - необходимо
# убрать последний элемент списка - <пустая строка>.
if self.fmt_list[-1] == '':
self.fmt_list = self.fmt_list[:-1]

def __getword__(self, buffer, separator):
"""Вырезает из строки левую часть отделенную сепаратором,
возвращает получившиеся правые и левые части, исключая
сепаратор, а также статус результата"""
value = ''
rez = 0
try:
i = buffer.index(separator)
value = buffer[0:i]
buffer = buffer[i+len(separator):]
except ValueError:
rez = 1
# rez = 1: Parse error
return (rez, value, buffer)

def do(self, input_string):
"""Производит разбор передаваемой строки в соответстивии с
формат-строкой, заданной при инициализации класса. Результат -
- словарь значений из данной строки"""
buf = input_string
fl = self.fmt_list
i = 0
# В формат-строке первый элемент - "разделитель"
if self.flSepFirst:
sep = fl[0]
(rez, val, buf) = self.__getword__(buf, sep)
if rez == 1:
print "Format string error!"
return {}
i += 1
result = {}
# Цикл, осуществляющий парсинг строки
while i != len(fl):
word = fl[i]
val = ''
i += 1
if i < len(fl):
# Ситуация когда за ключевым словом идет "разделитель"
sep = fl[i]
(rez, val, buf) = self.__getword__(buf, sep)
if rez == 1:
print "Format string error!"
break
i += 1
else:
# Ситуация когда за ключевым слово - последнее в
# формат-строке
val = buf
result[word] = val
return result

def main():
# Парсим лог программы REJIK
arg = "#year#-#mon#-#day# #hour# #list#:#tmp# #ip# \
#user# #tmp#/#tmp#/#url#/"
str1 = "2005-03-01 08:20:54 BANNER: 192.168.2.100 \
- http://r.mail.ru/b601875.swf (urls rule: r.mail.ru)"
str2 = "2005-03-21 10:07:37 BANNER: 192.168.2.14 \
- http://counter.yadro.ru/logo?21.6 (urls rule: yadro.ru)"
p = UParser(arg)
d = p.do(str1)
print d['year'], d['list']
d = p.do(str2)
print d['ip'], d['url']
# Парсим вывод некоторого скрипта, который выводит данные для
# lindocs
arg = "#article#:::#author#:::#index#:::#part#"
str1 = "Обработка HTML кода на Perl, HTML::TagReader:::\
Guido Socher:::perl:::0"
str2 = "Python.Manual.Ru:::Всеволод Стахов:::python:::0"
p.set_format_string(arg)
d = p.do(str1)
print d['author'], d['part']
d = p.do(str2)
print d['article'], d['index']

if __name__ == '__main__':
main()
end UParser.py Этот скрипт можно использовать как модуль для других программ. Но я
все-таки покажу, что выводит на экран именно эта программа.
Как видно из текста программы - парсятся четыре строки: две - кусочки
лога REJIK, две - результат вывода какого-то гипотетического скрипта.
Выше каждой группы строк представленна формат-строка. Итак,

$ python UParser.py
2005 BANNER
192.168.2.14 counter.yadro.ru
Guido Socher 0
Python.Manual.Ru python

Ответить   Mon, 21 Mar 2005 16:51:39 +0300 (#337285)