添加邮件解码工具 decode_email.py,用于解析和显示.eml文件内容
This commit is contained in:
265
decode_email.py
Normal file
265
decode_email.py
Normal file
@@ -0,0 +1,265 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
邮件解码工具
|
||||||
|
用于解析.eml文件并显示可读的邮件内容
|
||||||
|
"""
|
||||||
|
|
||||||
|
import base64
|
||||||
|
import sys
|
||||||
|
import re
|
||||||
|
import email
|
||||||
|
from email import policy
|
||||||
|
import os
|
||||||
|
import argparse
|
||||||
|
import html
|
||||||
|
|
||||||
|
def decode_eml_file(filename):
|
||||||
|
"""解析并显示.eml文件的内容"""
|
||||||
|
print(f"解析邮件文件: {filename}")
|
||||||
|
|
||||||
|
# 如果文件不存在
|
||||||
|
if not os.path.exists(filename):
|
||||||
|
print(f"错误: 文件不存在 {filename}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# 解析.eml文件
|
||||||
|
try:
|
||||||
|
with open(filename, 'r', encoding='utf-8', errors='replace') as f:
|
||||||
|
msg = email.message_from_file(f, policy=policy.default)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"读取文件错误: {str(e)}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# 显示邮件头信息
|
||||||
|
print("\n===== 邮件头信息 =====")
|
||||||
|
print(f"主题: {msg.get('Subject', '无主题')}")
|
||||||
|
print(f"发件人: {msg.get('From', '未知')}")
|
||||||
|
print(f"收件人: {msg.get('To', '未知')}")
|
||||||
|
print(f"日期: {msg.get('Date', '未知')}")
|
||||||
|
|
||||||
|
# 提取并显示邮件内容
|
||||||
|
print("\n===== 邮件内容 =====")
|
||||||
|
|
||||||
|
body_text = ""
|
||||||
|
body_html = ""
|
||||||
|
|
||||||
|
# 处理多部分邮件
|
||||||
|
if msg.is_multipart():
|
||||||
|
for part in msg.iter_parts():
|
||||||
|
content_type = part.get_content_type()
|
||||||
|
|
||||||
|
if content_type == "text/plain":
|
||||||
|
try:
|
||||||
|
body_text = part.get_content()
|
||||||
|
except Exception as e:
|
||||||
|
print(f"解析纯文本内容出错: {str(e)}")
|
||||||
|
payload = part.get_payload(decode=True)
|
||||||
|
if payload:
|
||||||
|
charset = part.get_content_charset() or 'utf-8'
|
||||||
|
try:
|
||||||
|
body_text = payload.decode(charset, errors='replace')
|
||||||
|
except:
|
||||||
|
body_text = payload.decode('utf-8', errors='replace')
|
||||||
|
|
||||||
|
elif content_type == "text/html":
|
||||||
|
try:
|
||||||
|
body_html = part.get_content()
|
||||||
|
except Exception as e:
|
||||||
|
print(f"解析HTML内容出错: {str(e)}")
|
||||||
|
payload = part.get_payload(decode=True)
|
||||||
|
if payload:
|
||||||
|
charset = part.get_content_charset() or 'utf-8'
|
||||||
|
try:
|
||||||
|
body_html = payload.decode(charset, errors='replace')
|
||||||
|
except:
|
||||||
|
body_html = payload.decode('utf-8', errors='replace')
|
||||||
|
else:
|
||||||
|
# 处理单部分邮件
|
||||||
|
content_type = msg.get_content_type()
|
||||||
|
try:
|
||||||
|
if content_type == "text/plain":
|
||||||
|
body_text = msg.get_content()
|
||||||
|
elif content_type == "text/html":
|
||||||
|
body_html = msg.get_content()
|
||||||
|
else:
|
||||||
|
print(f"未知内容类型: {content_type}")
|
||||||
|
try:
|
||||||
|
# 尝试作为纯文本处理
|
||||||
|
body_text = msg.get_content()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
except Exception as e:
|
||||||
|
print(f"解析邮件内容出错: {str(e)}")
|
||||||
|
payload = msg.get_payload(decode=True)
|
||||||
|
if payload:
|
||||||
|
charset = msg.get_content_charset() or 'utf-8'
|
||||||
|
try:
|
||||||
|
decoded = payload.decode(charset, errors='replace')
|
||||||
|
if content_type == "text/plain":
|
||||||
|
body_text = decoded
|
||||||
|
elif content_type == "text/html":
|
||||||
|
body_html = decoded
|
||||||
|
else:
|
||||||
|
body_text = decoded
|
||||||
|
except:
|
||||||
|
body_text = payload.decode('utf-8', errors='replace')
|
||||||
|
|
||||||
|
# 显示纯文本内容
|
||||||
|
if body_text:
|
||||||
|
print("\n----- 纯文本内容 -----")
|
||||||
|
print(body_text)
|
||||||
|
|
||||||
|
# 显示HTML内容 (可选,HTML内容通常很长)
|
||||||
|
if body_html:
|
||||||
|
print("\n----- HTML内容摘要 -----")
|
||||||
|
# 只显示HTML内容的前500个字符
|
||||||
|
print(body_html[:500] + "..." if len(body_html) > 500 else body_html)
|
||||||
|
|
||||||
|
# 尝试提取验证码
|
||||||
|
verification_code = None
|
||||||
|
|
||||||
|
# 从HTML内容中提取
|
||||||
|
if body_html:
|
||||||
|
# 尝试多种正则表达式匹配可能的验证码格式
|
||||||
|
patterns = [
|
||||||
|
r'letter-spacing:\s*\d+px[^>]*>([^<]+)<', # 通常验证码有特殊样式
|
||||||
|
r'<div[^>]*>(\d{4,8})</div>', # 数字在div中
|
||||||
|
r'验证码[::]\s*([A-Z0-9]{4,8})', # 中文标记的验证码
|
||||||
|
r'code[^\d]+(\d{4,8})', # 英文标记的验证码
|
||||||
|
r'\b([A-Z0-9]{6})\b' # 6位大写字母或数字
|
||||||
|
]
|
||||||
|
|
||||||
|
for pattern in patterns:
|
||||||
|
matches = re.findall(pattern, body_html)
|
||||||
|
if matches:
|
||||||
|
verification_code = matches[0].strip()
|
||||||
|
break
|
||||||
|
|
||||||
|
# 从纯文本中提取
|
||||||
|
if not verification_code and body_text:
|
||||||
|
patterns = [
|
||||||
|
r'验证码[::]\s*([A-Z0-9]{4,8})', # 中文格式
|
||||||
|
r'code[^\d]+(\d{4,8})', # 英文格式
|
||||||
|
r'\b(\d{6})\b' # 6位数字
|
||||||
|
]
|
||||||
|
|
||||||
|
for pattern in patterns:
|
||||||
|
matches = re.findall(pattern, body_text)
|
||||||
|
if matches:
|
||||||
|
verification_code = matches[0].strip()
|
||||||
|
break
|
||||||
|
|
||||||
|
# 显示提取到的验证码
|
||||||
|
if verification_code:
|
||||||
|
print("\n===== 提取结果 =====")
|
||||||
|
print(f"验证码: {verification_code}")
|
||||||
|
|
||||||
|
# 尝试提取验证链接
|
||||||
|
verification_link = None
|
||||||
|
if body_html:
|
||||||
|
link_match = re.search(r'href=[\'"]([^\'"]*(?:verify|confirm|activate)[^\'"]*)[\'"]', body_html)
|
||||||
|
if link_match:
|
||||||
|
verification_link = link_match.group(1)
|
||||||
|
|
||||||
|
if not verification_link and body_text:
|
||||||
|
link_match = re.search(r'https?://\S+?(?:verify|confirm|activate)\S+', body_text)
|
||||||
|
if link_match:
|
||||||
|
verification_link = link_match.group(0)
|
||||||
|
|
||||||
|
if verification_link:
|
||||||
|
print(f"验证链接: {verification_link}")
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def decode_all_emails(directory):
|
||||||
|
"""解析指定目录下的所有.eml文件"""
|
||||||
|
print(f"扫描目录: {directory}")
|
||||||
|
email_files = []
|
||||||
|
|
||||||
|
# 遍历目录
|
||||||
|
for root, dirs, files in os.walk(directory):
|
||||||
|
for file in files:
|
||||||
|
if file.endswith(".eml"):
|
||||||
|
email_files.append(os.path.join(root, file))
|
||||||
|
|
||||||
|
# 按修改时间排序
|
||||||
|
email_files.sort(key=lambda x: os.path.getmtime(x), reverse=True)
|
||||||
|
|
||||||
|
print(f"找到 {len(email_files)} 个邮件文件")
|
||||||
|
|
||||||
|
# 如果文件过多,只显示最新的几个
|
||||||
|
if len(email_files) > 5:
|
||||||
|
print("只显示最新的5封邮件")
|
||||||
|
email_files = email_files[:5]
|
||||||
|
|
||||||
|
# 解析每个文件
|
||||||
|
for i, email_file in enumerate(email_files, 1):
|
||||||
|
print(f"\n\n======= 邮件 {i}/{len(email_files)} =======")
|
||||||
|
print(f"文件: {email_file}")
|
||||||
|
decode_eml_file(email_file)
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(description='邮件解码工具')
|
||||||
|
parser.add_argument('path', nargs='?', help='邮件文件路径或目录路径')
|
||||||
|
parser.add_argument('--all', action='store_true', help='解码所有找到的邮件')
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
# 如果指定了--all参数,扫描email_data目录
|
||||||
|
if args.all:
|
||||||
|
email_data_dir = 'email_data'
|
||||||
|
if not os.path.exists(email_data_dir):
|
||||||
|
email_data_dir = os.path.join(os.getcwd(), 'email_data')
|
||||||
|
|
||||||
|
if not os.path.exists(email_data_dir):
|
||||||
|
print(f"错误: 找不到邮件数据目录 {email_data_dir}")
|
||||||
|
return 1
|
||||||
|
|
||||||
|
decode_all_emails(email_data_dir)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
# 如果指定了路径
|
||||||
|
if args.path:
|
||||||
|
# 检查是文件还是目录
|
||||||
|
if os.path.isfile(args.path):
|
||||||
|
return 0 if decode_eml_file(args.path) else 1
|
||||||
|
elif os.path.isdir(args.path):
|
||||||
|
decode_all_emails(args.path)
|
||||||
|
return 0
|
||||||
|
else:
|
||||||
|
print(f"错误: 路径不存在 {args.path}")
|
||||||
|
return 1
|
||||||
|
|
||||||
|
# 如果没有指定路径,尝试找到最新的邮件
|
||||||
|
email_data_dir = 'email_data'
|
||||||
|
if not os.path.exists(email_data_dir):
|
||||||
|
email_data_dir = os.path.join(os.getcwd(), 'email_data')
|
||||||
|
|
||||||
|
if not os.path.exists(email_data_dir):
|
||||||
|
print(f"错误: 找不到邮件数据目录 {email_data_dir}")
|
||||||
|
print("请指定邮件文件路径: python decode_email.py <eml文件路径>")
|
||||||
|
return 1
|
||||||
|
|
||||||
|
# 查找最新的邮件文件
|
||||||
|
latest_email = None
|
||||||
|
latest_time = 0
|
||||||
|
|
||||||
|
for root, dirs, files in os.walk(email_data_dir):
|
||||||
|
for file in files:
|
||||||
|
if file.endswith(".eml"):
|
||||||
|
file_path = os.path.join(root, file)
|
||||||
|
mtime = os.path.getmtime(file_path)
|
||||||
|
if mtime > latest_time:
|
||||||
|
latest_time = mtime
|
||||||
|
latest_email = file_path
|
||||||
|
|
||||||
|
if latest_email:
|
||||||
|
print(f"解析最新的邮件文件: {latest_email}")
|
||||||
|
return 0 if decode_eml_file(latest_email) else 1
|
||||||
|
else:
|
||||||
|
print(f"找不到任何.eml文件在 {email_data_dir}")
|
||||||
|
return 1
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(main())
|
||||||
Reference in New Issue
Block a user