IMAP 是唯一能安全预览、按 UNSEEN 过滤且保留服务器状态的方案;需递归解析 MIME、用 decode_header 处理中文名、清洗文件名并加时间戳防覆盖,且必须用 get_payload(decode=True) 获取二进制内容。

用 imaplib + email 可以稳定读取附件,但必须手动解析 MIME 结构,跳过 HTML 正文、识别 Content-Disposition、处理编码,否则容易漏附件或保存乱码文件。
为什么不能直接用 poplib 或第三方库(如 mail-parser)?
POP3 协议不支持按条件搜索邮件,且会删除服务器副本;mail-parser 对嵌套 multipart/related 处理不稳定,遇到带内联图片的邮件时可能把图片当附件误存。IMAP 是唯一能安全预览、按 UNSEEN 标记过滤、保留服务器状态的方案。
-
imaplib是 Python 标准库,无需额外安装,兼容所有主流邮箱(Gmail/Outlook/企业邮箱) - 必须显式调用
mail.fetch()获取完整邮件体,仅靠mail.search()返回的 ID 不含附件数据 - Gmail 需开启「App Password」并用该密码登录,不能用账户密码(OAuth2 超出基础需求)
如何正确提取附件并避免中文文件名乱码?
附件名藏在 Content-Disposition 头里,常见格式为 attachment; filename="=?UTF-8?B?5L2g5aW9LnBkZg==?=",直接取值会得到编码字符串。必须用 email.header.decode_header() 解码,再根据返回的编码类型(如 'utf-8')做 .decode()。
- 忽略
filename*(RFC 5987)字段,国内邮箱基本不用,且email模块对其支持不一致 - 若解码失败(如编码标识缺失),回退到用
quopri.decodestring()或原样用 ASCII 文件名 + 时间戳重命名 - 检查
part.get_filename()是否非空,且part.get_content_maintype() == 'multipart'的部分要跳过(那是正文容器,不是附件)
保存附件时路径和覆盖风险怎么控制?
附件名可能含非法字符(/、..、控制符)或重复,直接拼接路径会导致写入错误目录或覆盖旧文件。必须清洗文件名,并用 os.path.join() 构造绝对路径。
立即学习“Python免费学习笔记(深入)”;
- 用
re.sub(r'[\/*?:"|]', '_', filename)替换 Windows 禁止字符(Linux 下也建议统一处理) - 对同名文件加时间戳后缀:
f"{clean_name}_{int(time.time())}{ext}",避免静默覆盖 - 目标路径需提前用
os.makedirs(save_dir, exist_ok=True)创建,否则open()报FileNotFoundError - 二进制内容必须用
part.get_payload(decode=True)获取原始字节,传给open(..., 'wb'),不能用str()或decode()
最易被忽略的是:邮件可能有多个 multipart/mixed 嵌套层级,附件不一定在第一层 part 里——必须递归遍历所有 part,但只对 part.get_content_maintype() == 'application' 且 part.get_filename() 非空的才保存。没做递归的话,Outlook 发来的带 PDF 附件的邮件大概率漏掉。


















