我的知识记录

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以内。
  • 重复跑脚本会重复下载所有附件,配合下一篇"按主题筛选"或"只下载未读"能避免重复劳动。
  • 下载目录用绝对路径更稳,相对路径跟着运行时的工作目录走,容易找错地方。

Python自动下载邮件附件 imaplib实战

标签:

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

上一篇:Python邮件抄送密送cc bcc设置方法

下一篇:Python下载邮件附件自动归档到文件夹 按日期发件人分类