import poplib import ssl import email from email.header import decode_header import time import pymysql # 关闭SSL校验,解决老Python握手重置 #ssl._create_default_https_context = ssl._create_unverified_context # ========== 邮箱配置 自行修改 ========== MAIL_HOST = "pop.163.com" MAIL_PORT = 995 MAIL_USER = "lyudream@163.com" MAIL_PWD = "DVZxyF5NKCkR6fL5" # ========== 本地MariaDB配置 ========== DB_HOST = "127.0.0.1" DB_USER = "root" DB_PWD = "hp93000" DB_NAME = "alert_mail_stat" # 解码邮件标题、正文 def decode_str(s): value, charset = decode_header(s)[0] if charset: value = value.decode(charset) return value # 获取邮件正文 def get_email_content(msg): content = "" if msg.is_multipart(): for part in msg.walk(): content_type = part.get_content_type() content_disposition = str(part.get("Content-Disposition")) if content_type == 'text/plain' and 'attachment' not in content_disposition: payload = part.get_payload(decode=True) charset = part.get_charset() if charset is None: charset = 'utf-8' content = payload.decode(charset, errors='ignore') break else: payload = msg.get_payload(decode=True) charset = msg.get_charset() if charset is None: charset = 'utf-8' content = payload.decode(charset, errors='ignore') return content # 简单解析告警关键字(适配Alertmanager中文邮件模板:告警名称、实例、级别) def parse_alert_info(text): alert_name = "" instance = "" severity = "unknown" lines = text.splitlines() for line in lines: if "告警名称" in line: alert_name = line.split(":")[-1].strip() if "实例" in line: instance = line.split(":")[-1].strip() if "级别" in line: s_val = line.split(":")[-1].strip() if s_val.lower() in ["critical", "严重"]: severity = "critical" elif s_val.lower() in ["warning", "警告"]: severity = "warning" return alert_name, instance, severity # 主抓取逻辑 def crawl_mail(): # 连接数据库 db = pymysql.connect(host=DB_HOST, user=DB_USER, password=DB_PWD, database=DB_NAME, charset='utf8mb4') cursor = db.cursor() # 连接POP3邮箱 pop_conn = poplib.POP3_SSL(MAIL_HOST, MAIL_PORT) pop_conn.user(MAIL_USER) pop_conn.pass_(MAIL_PWD) total_num, total_size = pop_conn.stat() print(f"当前邮箱总邮件数量: {total_num}") if total_num == 0: print("暂无新邮件") pop_conn.quit() db.close() return # 从最新一封开始遍历 for idx in range(total_num, 0, -1): # 获取邮件原始内容 resp, raw_lines, octet = pop_conn.retr(idx) raw_msg = b'\r\n'.join(raw_lines).decode('utf-8', errors='ignore') msg = email.message_from_string(raw_msg) # 生成唯一标识去重 mail_uid = msg.get("Message-ID", str(time.time())) # 查重,避免重复入库 cursor.execute("SELECT id FROM alert_log WHERE mail_uid=%s", (mail_uid,)) if cursor.fetchone(): print(f"邮件{idx}已存在数据库,跳过") continue # 解析标题、发件人 subject = decode_str(msg.get("Subject", "")) from_addr = msg.get("From", "") # ===================== 临时关闭发件人过滤,全部邮件放行测试 ===================== # if "prometheusalert" not in from_addr.lower(): # print(f"邮件{idx}发件人不匹配,跳过,发件人:{from_addr}") # continue # 调试打印所有邮件基础信息 print("\n========================================") print(f"【调试】正在处理第 {idx} 封邮件") print(f"发件人:{from_addr}") print(f"邮件标题:{subject}") print("========================================") # 正文解析告警字段 body = get_email_content(msg) alert_name, instance, severity = parse_alert_info(body) if not alert_name: print(f"邮件{idx}未识别到【告警名称】关键字,跳过") continue # 区分触发/恢复告警 alert_type = 1 if "恢复" in subject or "resolved" in subject.lower(): alert_type = 2 now = time.strftime("%Y-%m-%d %H:%M:%S") sql = """ INSERT INTO alert_log (mail_uid, alert_type, alert_name, instance, severity, starts_at, ends_at, content, receive_time) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s) """ cursor.execute(sql, (mail_uid, alert_type, alert_name, instance, severity, now, None, body, now)) db.commit() print(f"入库成功 | 告警:{alert_name} 实例:{instance}") pop_conn.quit() cursor.close() db.close() print("\n本次抓取全部完成") if __name__ == "__main__": crawl_mail()