Python自动下载邮件附件 imaplib实战
用Python标准库imaplib遍历邮件、识别附件、把Excel/PDF/图片等附件批量下载到本地文件夹,自动处理中文文件名乱码,附完整可运行代码。
场景痛点
客户每天发来报价单、合同、报表附件,手动下载要一封封打开邮件、点附件、选保存位置,一上午下几十个,还经常下重名文件被覆盖。用Python直接连邮箱把附件批量拉到本地,文件名保留中文,重复运行也不会乱。
用到的库
imaplib、email都是Python标准库,无需pip安装。
# 无需安装,Python3自带
完整代码
# -*- coding: utf-8 -*-
import os
import imaplib
import email
from email.header import decode_header, make_header
# ====== 邮箱配置 ======
IMAP_SERVER = "imap.qq.com"
IMAP_PORT = 993
ACCOUNT = "your_account@qq.com"
PASSWORD = "your_imap_auth_code"
SAVE_DIR = "./attachments" # 附件保存目录
def decode_str(s):
"""解码邮件头,处理中文"""
if not s:
return ""
return str(make_header(decode_header(s)))
def safe_filename(name):
"""去掉文件名里不能当文件系统的字符"""
for ch in '/\\:*?"<>|':
name = name.replace(ch, "_")
return name or "attachment"
def download_attachments(limit=30):
os.makedirs(SAVE_DIR, exist_ok=True)
mail = imaplib.IMAP4_SSL(IMAP_SERVER, IMAP_PORT)
mail.login(ACCOUNT, PASSWORD)
mail.select("INBOX")
# 搜索最近邮件
status, messages = mail.search(None, "ALL")
id_list = messages[0].split()[-limit:]
count = 0
for msg_id in reversed(id_list):
# 用BODY.PEEK抓取,不会把邮件自动标记成已读
status, msg_data = mail.fetch(msg_id, "(BODY.PEEK[])")
msg = email.message_from_bytes(msg_data[0][1])
subject = decode_str(msg.get("Subject"))
# 遍历邮件的所有组成部分
for part in msg.walk():
# 纯正文/内嵌图片不是附件,跳过
filename = part.get_filename()
if not filename:
continue
filename = decode_str(filename)
filename = safe_filename(filename)
# 读取附件二进制内容
payload = part.get_payload(decode=True)
if payload is None:
continue
filepath = os.path.join(SAVE_DIR, filename)
# 同名文件加序号,避免覆盖
i = 1
base, ext = os.path.splitext(filepath)
while os.path.exists(filepath):
filepath = f"{base}_{i}{ext}"
i += 1
with open(filepath, "wb") as f:
f.write(payload)
count += 1
print(f"已下载:{filename} (来自主题:{subject})")
mail.logout()
print(f"\n完成,共下载 {count} 个附件,保存到 {SAVE_DIR}")
if __name__ == "__main__":
download_attachments(limit=30)
代码讲解
part.get_filename():Message对象如果是附件,会带文件名;纯正文part返回None,用这个判断就能把附件挑出来。part.get_payload(decode=True):把base64编码的附件内容解码成原始二进制,直接写文件就是原文件。msg.walk():递归遍历邮件的MIME结构,一封邮件里可能有正文、HTML、多个附件,walk把它们全部摊平成列表。BODY.PEEK[]:比RFC822多了PEEK关键字,抓取时不会自动把邮件改成已读状态,适合做"只下载不动原邮件"的场景。- 同名文件处理:先
os.path.exists判断,存在就自动加_1、_2后缀,避免两封同名报价单互相覆盖。 safe_filename:邮件文件名偶尔带/、:等字符,Linux/Windows都不认,统一替换成下划线。
运行结果
脚本跑完后,当前目录下出现attachments文件夹,里面是最近30封邮件里所有附件的原始文件,中文名正常显示,终端打印每一个下载的文件名和来源邮件主题。
注意事项
- 授权码在邮箱"设置→账户→IMAP"里开启服务后生成,QQ邮箱授权码是16位字符串,别把QQ登录密码填进去。
- 有些邮件的附件是"内嵌图片"(邮件签名里的logo),也会被
get_filename识别出来一起下载。想排除就判断Content-ID头,正文图片一般带Content-ID。 - 大附件(几十MB)下载会慢,脚本里可以加大小过滤,比如只下50MB以内。
- 重复跑脚本会重复下载所有附件,配合下一篇"按主题筛选"或"只下载未读"能避免重复劳动。
- 下载目录用绝对路径更稳,相对路径跟着运行时的工作目录走,容易找错地方。

更新时间:2026-09-14 21:12:38