如何编写正则捕获组解析自定义日志并导出为CSV文件?
如何用Python正则解析自定义日志并导出为CSV?
我完全懂你现在的困扰——单独写的正则片段能匹配单个字段,但组合起来就抓不到内容了对吧?这其实是因为没有考虑日志的完整结构,忽略了中间的主机名、服务名这些过渡部分,而且还要兼容像logrotate这种不带PID的日志条目。下面我给你一步步解决这个问题:
第一步:编写兼容所有日志的正则表达式
先看你的日志结构,统一格式是:
日期 时间 主机名 服务标识[PID]: 消息内容
(其中服务标识可能是sshd(pam_unix)或者logrotate,后者没有PID)
所以我们需要写一个能覆盖两种情况的正则,把四个字段都用捕获组对应上:
import re log_pattern = r'^(\w{3} \d{2}) (\d{2}:\d{2}:\d{2}) \w+ (?:\w+\(\w+\)\[(\d+)\]|\w+): (.*)$'
我来拆解一下这个正则:
^(\w{3} \d{2}):匹配开头的日期(比如Jun 15),作为第一个捕获组对应Date(\d{2}:\d{2}:\d{2}):匹配时间,第二个捕获组对应Time\w+:匹配主机名(比如combo),这里不需要捕获,所以直接跳过(?:\w+\(\w+\)\[(\d+)\]|\w+):这是一个非捕获组,用来处理两种服务情况:\w+\(\w+\)\[(\d+)\]:匹配带PID的服务(比如sshd(pam_unix)[20897]),里面的(\d+)是第三个捕获组对应PID\w+:匹配不带PID的服务(比如logrotate),这种情况第三个捕获组会是None
: (.*)$:匹配冒号后的所有内容作为消息,第四个捕获组对应Message
第二步:编写完整的Python解析代码
下面是把日志解析成字典,再导出为CSV的完整代码:
import re import csv # 定义正则模式 log_pattern = r'^(\w{3} \d{2}) (\d{2}:\d{2}:\d{2}) \w+ (?:\w+\(\w+\)\[(\d+)\]|\w+): (.*)$' regex = re.compile(log_pattern) # 日志样本(你可以替换成读取文件的代码,比如用open()逐行读取) logs = [ "Jun 15 02:04:59 combo sshd(pam_unix)[20897]: authentication failure; logname= uid=0 euid=0 tty=NODEVssh ruser= rhost=220-135-151-1.hinet-ip.hinet.net user=root", "Jun 15 02:04:59 combo sshd(pam_unix)[20898]: authentication failure; logname= uid=0 euid=0 tty=NODEVssh ruser= rhost=220-135-151-1.hinet-ip.hinet.net user=root", "Jun 15 04:06:18 combo su(pam_unix)[21416]: session opened for user cyrus by (uid=0)", "Jun 15 04:06:19 combo su(pam_unix)[21416]: session closed for user cyrus", "Jun 15 04:06:20 combo logrotate: ALERT exited abnormally with [1]", "Jun 15 04:12:42 combo su(pam_unix)[22644]: session opened for user news by (uid=0)", "Jun 15 04:12:43 combo su(pam_unix)[22644]: session closed for user news" ] # 解析每条日志 parsed_logs = [] for log in logs: match = regex.match(log) if match: date, time, pid, message = match.groups() parsed_logs.append({ "Date": date, "Time": time, "PID": pid if pid else "", # 无PID时设为空字符串,避免CSV出现None "Message": message.strip() }) # 导出为CSV文件 with open('parsed_logs.csv', 'w', newline='', encoding='utf-8') as csvfile: fieldnames = ['Date', 'Time', 'PID', 'Message'] writer = csv.DictWriter(csvfile, fieldnames=fieldnames) writer.writeheader() for entry in parsed_logs: writer.writerow(entry)
为什么之前的组合正则失效?
你之前单独写的字段正则没问题,但组合时忽略了日志中间的主机名、服务名这些“无关但必须匹配”的部分,而且没考虑logrotate这种不带PID的特殊情况,导致正则无法匹配整条日志。上面的正则通过非捕获组处理了两种服务格式,同时跳过不需要的主机名,就能完美匹配所有日志条目了。
测试一下代码,导出的CSV会包含所有你需要的四个字段,无PID的条目也会正确处理为空。
内容的提问来源于stack exchange,提问作者Aaron
相关产品推荐
相关产品推荐

