Python邮件附件自动转发到指定邮箱 imaplib+smtplib
用imaplib读取邮件、把附件原样打包,再用smtplib自动转发到指定邮箱,适合把客户邮件自动抄送归档到公司邮箱,附完整可运行代码。
场景痛点
客户邮件发到个人QQ邮箱,公司要求所有客户沟通归档到企业邮箱。手动每封转发一次,一天几十封,还容易漏。让脚本自动读邮箱里来自客户的邮件,把正文和附件原样转发到企业邮箱,发完标记已读,整个流程不用人管。
用到的库
# imaplib、smtplib、email都是标准库,无需安装
完整代码
# -*- coding: utf-8 -*-
import imaplib
import smtplib
import email
from email.header import decode_header, make_header
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
IMAP_SERVER = "imap.qq.com"
IMAP_PORT = 993
SMTP_SERVER = "smtp.qq.com"
SMTP_PORT = 465
ACCOUNT = "your_account@qq.com"
AUTH_CODE = "your_imap_smtp_auth_code"
FORWARD_TO = "archive@company.com"
# 只转发这些发件人发来的邮件
FORWARD_SENDERS = ["client@a.com", "sales@b.com"]
def decode_str(s):
if not s:
return ""
return str(make_header(decode_header(s)))
def build_forward_msg(original):
"""把原邮件重组成一封新邮件,正文+附件都带上"""
fwd = MIMEMultipart()
fwd["From"] = ACCOUNT
fwd["To"] = FORWARD_TO
orig_subj = decode_str(original.get("Subject") or "")
fwd["Subject"] = f"[自动转发] {orig_subj}"
orig_from = decode_str(original.get("From") or "")
body = f"以下是自动转发的邮件:\n原发件人:{orig_from}\n原主题:{orig_subj}\n\n"
fwd.attach(MIMEText(body, "plain", "utf-8"))
# 把原邮件的每个部分(正文/附件)原样搬过来
for part in original.walk():
if part.is_multipart():
continue
filename = part.get_filename()
payload = part.get_payload(decode=True)
if payload is None:
continue
if filename:
# 附件:保留文件名和内容类型
ct = part.get_content_type()
maintype, subtype = ct.split("/", 1) if "/" in ct else ("application", "octet-stream")
from email.mime.base import MIMEBase
from email import encoders
att = MIMEBase(maintype, subtype)
att.set_payload(payload)
encoders.encode_base64(att)
fname = decode_str(filename)
att.add_header("Content-Disposition", "attachment",
filename=("utf-8", "", fname))
fwd.attach(att)
else:
# 纯文本/HTML正文片段,跳过(上面已经写了转发说明)
pass
return fwd
def forward_inbox():
mail = imaplib.IMAP4_SSL(IMAP_SERVER, IMAP_PORT)
mail.login(ACCOUNT, AUTH_CODE)
mail.select("INBOX")
# 先按未读拉出来,本地再按发件人过滤
_, msgs = mail.search(None, "UNSEEN")
ids = msgs[0].split() if msgs[0] else []
print(f"未读邮件 {len(ids)} 封")
sent = 0
# 连接SMTP发信
smtp = smtplib.SMTP_SSL(SMTP_SERVER, SMTP_PORT)
smtp.login(ACCOUNT, AUTH_CODE)
for mid in ids:
_, data = mail.fetch(mid, "(RFC822)")
orig = email.message_from_bytes(data[0][1])
sender = decode_str(orig.get("From") or "")
if not any(s in sender for s in FORWARD_SENDERS):
continue
new_msg = build_forward_msg(orig)
smtp.sendmail(ACCOUNT, [FORWARD_TO], new_msg.as_string())
sent += 1
print(f"已转发:{decode_str(orig.get('Subject'))}")
# 转发完标记已读
mail.store(mid, "+FLAGS", "\\Seen")
smtp.quit()
mail.logout()
print(f"完成,转发 {sent} 封到 {FORWARD_TO}")
if __name__ == "__main__":
forward_inbox()
代码讲解
- 组合使用两个协议:imaplib负责读邮件(收),smtplib负责发邮件(转)。账号和授权码通常是同一个,QQ邮箱在IMAP/SMTP设置里开一次就行。
build_forward_msg:把原邮件重新打包成MIMEMultipart,正文写一段转发说明,附件通过walk()原样提取再attach回去,文件名用UTF-8编码避免乱码。- 转发不是真的"原封不动转发",而是新建一封邮件,From变成自己账号,主题加
[自动转发]前缀。收件人在To看到的是你,原发件人写在正文里。 - 发完
+FLAGS \Seen:标记已读,下次跑不会重复转发。这是闭环的关键。 - 发件人过滤:因为是本地过滤,把
FORWARD_SENDERS列表扩一下就行。想在服务器端过滤,把search条件换成FROM即可。
运行结果
脚本跑完,企业邮箱archive@company.com收到若干封[自动转发] 原主题邮件,正文里写明原发件人,附件原样带上。原邮箱里这些邮件自动标记已读,不会重复转发。
注意事项
- QQ邮箱授权码同时支持IMAP和SMTP,不用分别申请。163、Gmail也一样。
- 转发邮件会消耗发信额度,QQ邮箱每天发信有上限,客户量特别大时注意别触发限制。
- 大附件转发会慢,SMTP单封一般限50MB,和直接发邮件一样。
- 想"原样转发"(保留原From头),技术上复杂且容易被对方当成垃圾邮件,实战里都用新邮件+正文注明原发件人这种方式。
- 转发逻辑和"已读标记"绑在一起:先确认SMTP发送成功再标已读,发送失败就保持未读,下次重试。代码里
sendmail没做异常捕获,生产环境加try。 - 配合定时任务每小时跑一次,自动归档就齐了。

更新时间:2026-09-14 21:08:51