Python email模块解析非标准编码邮件Subject异常问题排查
邮件Subject编码解析问题
问题背景
解析采用win-1252编码的邮件时遇到异常,该邮件的Subject原始Header内容如下:
Subject: Счета на оплату по заказу . .
对应区域的十六进制dump:
000008a0 56 4e 4f 53 41 52 45 56 40 41 43 43 45 4e 54 2e |VNOSAREV@ACCENT.| 000008b0 52 55 3e 0d 0a 53 75 62 6a 65 63 74 3a 20 d1 f7 |RU>..Subject: ..| 000008c0 e5 f2 e0 20 ed e0 20 ee ef eb e0 f2 f3 20 ef ee |... .. ...... ..| 000008d0 20 e7 e0 ea e0 e7 f3 20 20 20 2e 20 20 2e 20 20 | ...... . . | 000008e0 20 20 0d 0a 58 2d 4d 61 69 6c 65 72 3a 20 4f 73 | ..X-Mailer: Os| 000008f0 74 72 6f 53 6f 66 74 20 53 4d 54 50 20 43 6f 6e |troSoft SMTP Con|
该Subject未遵循RFC 1342规定的=?charset?encoding?encoded-text?=格式,但多数邮件客户端能正常显示,需实现正确提取。
当前处理代码及结果
使用Python内置email模块处理,代码如下:
import email with open('data.eml', 'rb') as fp: content = fp.read() mail = email.message_from_bytes(content) print(mail.get('subject')) # ����� �� ������ �� ������ . . print(mail.get('subject').encode()) # '=?unknown-8bit?b?0ffl8uAg7eAg7u/r4PLzIO/uIOfg6uDn8yAgIC4gIC4gICAg?='
核心问题
- 能否让
email模块正确解析此类邮件的Subject? - 如果不能,如何不访问私有属性
mail._headers获取该Header的原始数据? - 如果以上都不行,能否推荐更通用的Python邮件解析模块?
额外观察
a) 通过私有属性mail._headers提取Subject原始内容:
[hd[1] for hd in mail._headers if hd[0] == 'Subject']
结果:
['\udcd1\udcf7\udce5\udcf2\udce0 \udced\udce0 \udcee\udcef\udceb\udce0\udcf2\udcf3 \udcef\udcee \udce7\udce0\udcea\udce0\udce7\udcf3 . . ']
b) 调用mail.get_charsets()返回[None, 'windows-1251', None],模块理论上可猜测正确字符集。
补充:邮件文件SHA256哈希值为1aee4d068c2ae4996a47a3ae9c8c3fa6295a14b00d9719fb5ac0291a229b4038
解答
1. 让email模块正确解析的方法
Python 3.11+的email模块支持通过自定义策略处理非标准编码Header。可以指定header_factory手动解码未知8位编码内容:
import email from email import policy from email.headerregistry import UnstructuredHeader def custom_header_factory(name, value): if name.lower() == 'subject': # 转换为原始字节后用windows-1251解码 raw_bytes = value.encode('raw-unicode-escape') return raw_bytes.decode('windows-1251') # 其他Header沿用默认处理逻辑 return UnstructuredHeader(name, value) # 克隆SMTP策略并替换header工厂 custom_policy = policy.SMTP.clone(header_factory=custom_header_factory) with open('data.eml', 'rb') as fp: mail = email.message_from_bytes(fp.read(), policy=custom_policy) print(mail.get('subject')) # Счета на оплату по заказу . .
若不确定字符集,可结合get_charsets()返回的结果(此处包含windows-1251)尝试解码,或遍历常见西里尔字符编码(如utf-8、koi8-r)。
2. 不访问私有属性获取原始Header数据
使用Python 3.3+新增的raw_items()方法,该方法返回Header的原始键值对(字节形式),无需依赖私有属性:
import email with open('data.eml', 'rb') as fp: mail = email.message_from_bytes(fp.read()) for name, raw_value in mail.raw_items(): if name.lower() == 'subject': # 直接用正确字符集解码原始字节 subject = raw_value.decode('windows-1251') print(subject) # Счета на оплату по заказу . . break
3. 通用邮件解析模块推荐
mailparser:第三方模块,专门适配各种非标准邮件格式,自动识别编码,使用简洁:
安装命令:
使用示例:pip install mailparserfrom mailparser import parse_from_file mail = parse_from_file('data.eml') print(mail.subject) # Счета на оплату по заказу . .imapclient:若从IMAP服务器拉取邮件,该模块能较好处理原始编码Header,配合email模块使用灵活性更高,核心侧重IMAP协议交互。
内容的提问来源于stack exchange,提问作者born
相关产品推荐
相关产品推荐

