Продолжайте получать ошибки `NoneType` при синтаксическом анализе журналов с помощью регулярных выражений - PullRequest
1 голос
/ 02 ноября 2019

У меня есть пример того, как два из журналов выглядят ниже. Я пытаюсь получить IP, date_time, метод, эту часть (/071300/242153 HTTP/1.1"), код ответа (просто 404 / 200 часть), а остальные в одной группе:

66.249.69.97 - - [24/Sep/2014:22:25:44 +0000] "GET /071300/242153 HTTP/1.1" 404 514 "-" "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)"

и

71.19.157.174 - - [24/Sep/2014:22:26:12 +0000] "GET /error HTTP/1.1" 404 505 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.94 Safari/537.36"

Моя функция выглядит следующим образом:

def parse_logs(logs):
  log_list = []
  for log in logs:
    p = re.compile(r'''(?P<ip_addr>\d+(\.\d+){3}) - - \[(?P<date_time>.+?)\] (?P<http_method>\".+?\") (?P<return_code>\d+) \d+ "-" (?P<client>\".+?\")''')

    m = p.search(log)

    log_list.append([m.group('ip_addr'), m.group('date_time'), m.group('http_method'), m.group('return_code'), m.group('client')])

rdd_prepped = parse_logs(rdd.take(5))

Когда я передаю список этих журналов функции изапустить его, я получаю сообщение об ошибке: AttributeError: 'NoneType' object has no attribute 'collect'.

Когда я помещаю print(m.group('ip')) строку в m = p.search(log), я получаю ошибку:

AttributeError: 'NoneType' object has no attribute 'group'

Почему я продолжаю получать NoneTypes? Я использую Python2.7 кстати.

1 Ответ

1 голос
/ 02 ноября 2019

Когда это было впервые опубликовано, регулярное выражение выглядело так:

p = re.compile(r'''(?P<ip>\d+(\.\d+){3}) - - \[(?P<date_time>.+?)\] (?P<method>\".+?\") \
    (?P<response_code>\d+) \d+ "-" (?P<client>\".+?\")''')

Обратите внимание на символ продолжения строки ('\') в конце первой строки. Но шаблон также в тройных кавычках. Таким образом, шаблон включает в себя текст '\\n ' (косая черта + новая строка + отступ). В результате шаблон не будет совпадать.

Перепишите шаблон в одну строку, и он должен работать:

p = re.compile(r'''(?P<ip>\d+(\.\d+){3}) - - \[(?P<date_time>.+?)\] (?P<method>\".+?\") (?P<response_code>\d+) \d+ "-" (?P<client>\".+?\")''')

Для сложных регулярных выражений мне нравится использовать подробный режим:

regex = re.compile("""
    (?P<ip>\d+(?:\.\d+){3})     # four, dot-separated sets of digits
    .*?                         # skip ahead
    \[(?P<date_time>.*?)\]      # date time is everything between '[ ]'
    .*?                         # skip 
    "(?P<method>.*?)"           # method is everything between quotes
    .*?                         # skip 
    (?P<response_code>\d+)      # multiple digits
    .*?                         # skip
    "-"                         # don't care
    .*?                         #
    "(?P<client>.*?)"           # client is everything between quotes
    """, re.VERBOSE)

Еще несколько вещей:

Если вы ожидаете, что регулярное выражение будет совпадать (почти) с каждой строкой в ​​журнале, вы должны напечатать / записать все строки, которые не совпадают. Это помогает отлавливать ошибки в вашем регулярном выражении или когда кто-то изменяет формат журнала, не сообщая вам.

Переместите шаг re.compile из цикла.

MatchObject.group() может принимать несколько аргументов ивозвращает кортеж из перечисленных групп.

def parse_logs(logs):
  log_list = []

  p = re.compile(...whichever regex style you like...)

  for log in logs:

    m = p.search(log)

    if m:
      log_list.append(m.group('ip_addr', 'date_time', 'http_method, 
                              'return_code', 'client'))
    else:
        print(log)

rdd_prepped = parse_logs(rdd.take(5))
...