【问题标题】:NameError: name 'status_code' is not defined while parsing access.logNameError:解析 access.log 时未定义名称“status_code”
【发布时间】:2021-05-11 11:45:50
【问题描述】:

下午好,在测试解析access.log的代码时,出现如下错误:

Traceback(最近一次调用最后一次): 文件“logsscript_3.py”,第 31 行,在 dict_ip[ip][status_code] += 1 NameError: name 'status_code' 未定义

我需要将代码为 400 的前 10 个请求输出到 json 文件

代码是这样的:

import argparse
import json
import re
from collections import defaultdict

parser = argparse.ArgumentParser(description='log parser')
parser.add_argument('-f', dest='logfile', action='store', default='access.log')
args = parser.parse_args()

regul_ip = (r"^(?P<ips>.*?)")
regul_statuscode = (r"\s(?P<status_code>400)\s")


dict_ip = defaultdict(lambda: {"400": 0})

with open(args.logfile) as file:
     for index, line in enumerate(file.readlines()):
        try:
              ip = re.search(regul_ip, line).group()
              status_code = re.search(regul_statuscode, line).groups()[0]
        except AttributeError:
             pass
        dict_ip[ip][status_code] += 1

print(json.dumps(dict_ip, indent=4))
with open("final_log.json", "w") as jsonfile:
    json.dump(dict_ip, jsonfile, indent=5)

access.log 中的一行示例:

213.137.244.2 - - [13/Dec/2015:17:30:13 +0100] "GET /administrator/ HTTP/1.1" 200 4263 "-" "Mozilla/5.0 (Windows NT 6.0; rv:34.0)壁虎/20100101 火狐/34.0" 7717

【问题讨论】:

  • 你为什么忽略AttributeError
  • 扩展@khelwood 点:示例行(可能在您的日志中还有更多)不是 400 代码行。您的正则表达式包含 400,因此它将不匹配,并且整个 status_code = ... 行将失败,所有非 400 行的 AttributeError: 'NoneType' object has no attribute 'groups'。忽略异常会导致 dict_ip[ip]... 行 b/c 中的 NameError status_code 未分配值。
  • 好吧,我该如何修复代码?

标签: json python-3.x parsing


【解决方案1】:

跟进评论(为了完整起见包含在下面),我解释了为什么你会看到错误,下面我解释了一些修复代码的方法。

在@khelwood 点上扩展:示例行(可能在您的日志中还有更多)不是 400 代码行。您的正则表达式包含 400,因此它将不匹配,并且整个 status_code = ... 行将失败并出现 AttributeError: 'NoneType' object has no attribute 'groups' 对于所有非 400 行。忽略异常会导致 dict_ip[ip]... 行 b/c status_code 中的 NameError 未分配值。

首先,您可以使用一个正则表达式来解析访问日志。

>>> import re
>>>
>>> line = '213.137.244.2 - - [13/Dec/2015:17:30:13 +0100] "GET /administrator/ HTTP/1.1" 200 4263 "-" "Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0" 7717'
>>> p = r'(\S+) (\S+) (\S+) \[(.*?)\] "(\S+) (\S+) (\S+)" (\S+) (\S+) "(\S+)" "(.*?)" (\S+)'
>>> pat = re.compile(p)
>>> m = pat.match(line)
>>> m.groups()
('213.137.244.2', '-', '-', '13/Dec/2015:17:30:13 +0100', 'GET', '/administrator/', 'HTTP/1.1', '200', '4263', '-', 'Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0', '7717')
>>> m.group(1)
'213.137.244.2'
>>> m.group(2)
'-'
...

上面的 sn-p 向您展示了如何从您的日志中获取和访问各个字段,正如我在您最近提出的其他问题中所观察到的那样。

您可以稍微修改上面的内容,如下所示(因为您只关心带有400 的日志行并且只需要IP 地址)。请注意,这不是编写正则表达式的唯一方法,它只是一种可以很容易地从上述方法派生的方法。另请注意,出于说明目的,我将200 更改为400

>>> line = '213.137.244.2 - - [13/Dec/2015:17:30:13 +0100] "GET /administrator/ HTTP/1.1" 400 4263 "-" "Mozilla/5.0 (Windows NT 6.0; rv:34.0) Gecko/20100101 Firefox/34.0" 7717'
>>> p = r'(\S+) \S+ \S+ \[.*?\] "\S+ \S+ \S+" 400 \S+ "\S+" ".*?" \S+'
>>> pat = re.compile(p)
>>> m = pat.match(line)
>>> m.group(1)
'213.137.244.2'

因此,阅读您的日志,计算每个 IP 地址的 400s,并将具有最多 400s 的 10 个 IP 地址保存在 json 文件中:

>>> from collections import Counter
>>> import json
>>> import re
>>>
>>> p = r'(\S+) \S+ \S+ \[.*?\] "\S+ \S+ \S+" 400 \S+ "\S+" ".*?" \S+'
>>> pat = re.compile(p)
>>> dict_ips_400 = Counter()
>>>
>>> with open("input_log.text") as f:
>>>     for line in f:                 # see Note 1
>>>         m = pat.match(line)
>>>         if m:                      # check if there is a match
>>>             ip = m.group(1)
>>>             dict_ips_400[ip] += 1
>>>
>>> with open("final_log.json", "w") as jsonfile:
...     json.dump(dict_ips_400.most_common(10), jsonfile, indent=5)
...

注意事项:

  1. 您可能需要检查使用f.readlines() 与如上所述逐行处理文件的区别(如果您正在处理大文件)
  2. 您可以将以上内容修改为 a。使用命名组(请参阅re's docs),就像您在代码中尝试做的那样和/或 b.捕获并存储更多字段,例如 IP 地址和状态代码对计数

【讨论】:

    猜你喜欢
    • 2021-09-22
    • 1970-01-01
    • 2018-01-24
    • 1970-01-01
    • 2018-05-18
    • 1970-01-01
    • 2021-04-15
    • 2019-01-26
    相关资源
    最近更新 更多