-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathanalyze_log.py
More file actions
232 lines (199 loc) · 7.66 KB
/
Copy pathanalyze_log.py
File metadata and controls
232 lines (199 loc) · 7.66 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Nginx访问日志分析脚本
分析正常访问数量并对访问来源进行分类
"""
import re
from collections import defaultdict
from datetime import datetime
def parse_log_line(line):
"""解析nginx日志行"""
# 跳过命令提示符行
if line.startswith('ubuntu@') or line.startswith('$'):
return None
# nginx日志格式: IP - - [时间] "方法 路径 HTTP版本" 状态码 大小 "Referer" "User-Agent"
pattern = r'(\S+) - - \[([^\]]+)\] "(\S+) (\S+) ([^"]+)" (\d+) (\S+) "([^"]*)" "([^"]*)"'
match = re.match(pattern, line)
if not match:
return None
ip = match.group(1)
timestamp = match.group(2)
method = match.group(3)
path = match.group(4)
http_version = match.group(5)
status_code = int(match.group(6))
size = match.group(7)
referer = match.group(8)
user_agent = match.group(9)
return {
'ip': ip,
'timestamp': timestamp,
'method': method,
'path': path,
'status_code': status_code,
'size': size,
'referer': referer,
'user_agent': user_agent
}
def classify_visitor(user_agent, path, status_code, method):
"""对访问者进行分类"""
if not user_agent:
user_agent = ''
ua_lower = user_agent.lower()
# 搜索引擎爬虫
if 'googlebot' in ua_lower:
return '搜索引擎爬虫 - Google'
if 'bingbot' in ua_lower:
return '搜索引擎爬虫 - Bing'
if 'ahrefsbot' in ua_lower:
return '搜索引擎爬虫 - Ahrefs'
if 'amazonbot' in ua_lower:
return '搜索引擎爬虫 - Amazon'
if '360spider' in ua_lower or '360Spider' in user_agent:
return '搜索引擎爬虫 - 360'
# 社交媒体爬虫
if 'twitterbot' in ua_lower:
return '社交媒体爬虫 - Twitter'
if 'meta-externalagent' in ua_lower or 'facebook' in ua_lower:
return '社交媒体爬虫 - Facebook'
# AI/工具爬虫
if 'gptbot' in ua_lower:
return 'AI爬虫 - GPTBot'
if 'headlesschrome' in ua_lower:
return '自动化工具 - HeadlessChrome'
# 安全扫描工具
if 'censysinspect' in ua_lower:
return '安全扫描 - Censys'
if 'palo alto' in ua_lower or 'cortex-xpanse' in ua_lower:
return '安全扫描 - Palo Alto'
if 'internetmeasurement' in ua_lower:
return '安全扫描 - InternetMeasurement'
# 其他工具
if 'curl' in ua_lower:
return '工具 - curl'
if 'python-requests' in ua_lower:
return '工具 - Python requests'
if 'go-http-client' in ua_lower:
return '工具 - Go HTTP Client'
if 'kiroide' in ua_lower:
return '工具 - KiroIDE'
if 'dalvik' in ua_lower:
return '工具 - Android Dalvik'
# 扫描/攻击行为(需要检查路径和方法)
if status_code in [400, 404, 405, 444, 499]:
# 检查可疑路径
suspicious_paths = [
'/cgi-bin', '/geoserver', '/wfs', '/ows',
'/+CSCOL+', '/+CSCOE+', '/ecp/', '/developmentserver',
'/ab2g', '/ab2h', '/alive.php', '/teorema505',
'zgrab', 'mstshash', 'MGLNDD'
]
if any(sp in path for sp in suspicious_paths) or 'zgrab' in ua_lower:
return '扫描/攻击 - 可疑路径扫描'
if method in ['POST', 'OPTIONS', 'PROPFIND'] and status_code != 200:
return '扫描/攻击 - 异常请求方法'
if status_code == 400 and path == '/':
return '扫描/攻击 - 恶意请求'
# 其他4xx/5xx错误
return '异常访问 - HTTP错误'
# 正常用户(浏览器)
if any(browser in ua_lower for browser in ['chrome', 'firefox', 'safari', 'edge', 'opera']):
if status_code in [200, 304, 301, 308]:
return '正常用户 - 浏览器访问'
else:
return '正常用户 - 错误请求'
# 未分类
if status_code in [200, 304, 301, 308]:
return '其他 - 正常访问'
else:
return '其他 - 异常访问'
def analyze_log(file_path):
"""分析日志文件"""
stats = {
'total': 0,
'normal': 0,
'categories': defaultdict(int),
'status_codes': defaultdict(int),
'ips': defaultdict(int)
}
with open(file_path, 'r', encoding='utf-8') as f:
for line in f:
line = line.strip()
if not line:
continue
parsed = parse_log_line(line)
if not parsed:
continue
stats['total'] += 1
stats['status_codes'][parsed['status_code']] += 1
stats['ips'][parsed['ip']] += 1
# 判断是否正常访问(状态码200、304、301、308)
if parsed['status_code'] in [200, 304, 301, 308]:
stats['normal'] += 1
# 分类
category = classify_visitor(
parsed['user_agent'],
parsed['path'],
parsed['status_code'],
parsed['method']
)
stats['categories'][category] += 1
return stats
def print_report(stats):
"""打印分析报告"""
print("=" * 80)
print("Nginx访问日志分析报告")
print("=" * 80)
print()
print(f"总访问次数: {stats['total']}")
print(f"正常访问次数: {stats['normal']} ({stats['normal']/stats['total']*100:.2f}%)")
print(f"异常访问次数: {stats['total'] - stats['normal']} ({(stats['total'] - stats['normal'])/stats['total']*100:.2f}%)")
print()
print("-" * 80)
print("访问来源分类统计:")
print("-" * 80)
for category, count in sorted(stats['categories'].items(), key=lambda x: x[1], reverse=True):
percentage = count / stats['total'] * 100
print(f" {category:40s} {count:5d} ({percentage:5.2f}%)")
print()
print("-" * 80)
print("HTTP状态码统计:")
print("-" * 80)
for status, count in sorted(stats['status_codes'].items()):
percentage = count / stats['total'] * 100
status_name = {
200: '200 OK',
304: '304 Not Modified',
301: '301 Moved Permanently',
308: '308 Permanent Redirect',
400: '400 Bad Request',
404: '404 Not Found',
405: '405 Method Not Allowed',
444: '444 Connection Closed',
499: '499 Client Closed Request'
}.get(status, str(status))
print(f" {status_name:30s} {count:5d} ({percentage:5.2f}%)")
print()
print("-" * 80)
print("访问最多的IP地址 (Top 10):")
print("-" * 80)
for ip, count in sorted(stats['ips'].items(), key=lambda x: x[1], reverse=True)[:10]:
percentage = count / stats['total'] * 100
print(f" {ip:20s} {count:5d} ({percentage:5.2f}%)")
print()
if __name__ == '__main__':
log_file = r'c:\Users\kingdee\Desktop\日志.txt'
report_file = 'log_analysis_report.txt'
print("正在分析日志文件...")
stats = analyze_log(log_file)
# 打印到控制台
print_report(stats)
# 同时保存到文件
import sys
with open(report_file, 'w', encoding='utf-8') as f:
original_stdout = sys.stdout
sys.stdout = f
print_report(stats)
sys.stdout = original_stdout
print(f"\n分析报告已保存到: {report_file}")