Python发邮件带附件教程详解
本文详细介绍了如何使用Python的smtplib和email.mime模块发送带附件的邮件,解决了初学者对纯文本邮件与附件邮件代码差异大的困惑。通过构建MIMEMultipart对象,结合MIMEText处理正文,MIMEBase和encoders进行附件的Base64编码,详细阐述了邮件协议的结构化过程。同时,提供了Python代码示例,并深入探讨了邮件附件处理中的编码问题、文件类型考虑,以及常见认证失败和连接超时的排查与解决方案,助你轻松掌握Python发送带附件邮件的技巧,提高邮件发送成功率。
答案:发送带附件邮件需构造MIMEMultipart对象,结合MIMEText、MIMEBase和encoders处理正文与Base64编码的附件,并通过smtplib连接SMTP服务器发送;与纯文本邮件不同,附件邮件需遵循MIME标准,结构更复杂。

用Python发送带附件的邮件,核心在于利用smtplib模块连接邮件服务器,并结合email.mime系列模块来精心构造一个多部分(multipart)邮件消息,其中包含了文本内容和附件的二进制数据。这并非简单的字符串拼接,而是需要遵循邮件协议的结构化过程。
解决方案
发送带附件的邮件需要我们构建一个MIMEMultipart对象,它能容纳邮件的各个部分,比如正文文本和附件。下面是一个实际的Python代码示例,展示了如何实现这一功能:
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
from email import encoders
import os
def send_email_with_attachment(sender_email, sender_password, receiver_email, subject, body, attachment_path):
"""
发送一封带附件的邮件。
参数:
sender_email (str): 发件人邮箱地址。
sender_password (str): 发件人邮箱密码或授权码。
receiver_email (str): 收件人邮箱地址。
subject (str): 邮件主题。
body (str): 邮件正文。
attachment_path (str): 附件文件的完整路径。
"""
try:
# 创建一个MIMEMultipart对象,用于组合邮件的正文和附件
msg = MIMEMultipart()
msg['From'] = sender_email
msg['To'] = receiver_email
msg['Subject'] = subject
# 添加邮件正文
msg.attach(MIMEText(body, 'plain', 'utf-8'))
# 处理附件
if attachment_path and os.path.exists(attachment_path):
filename = os.path.basename(attachment_path)
# 打开附件文件并读取其二进制内容
with open(attachment_path, 'rb') as attachment:
# 创建MIMEBase对象,用于表示附件
# 'application' 和 'octet-stream' 是通用的MIME类型,表示二进制数据
# 如果是特定类型,如PDF,可以是 'application', 'pdf'
part = MIMEBase('application', 'octet-stream')
part.set_payload(attachment.read())
# 对附件进行Base64编码,这是邮件传输二进制数据的标准方式
encoders.encode_base64(part)
# 添加附件头部信息,指定文件名
part.add_header(
'Content-Disposition',
f'attachment; filename= {filename}',
)
# 将附件添加到MIMEMultipart对象中
msg.attach(part)
else:
print(f"警告:附件文件 '{attachment_path}' 不存在或路径无效,将不发送附件。")
# 连接SMTP服务器
# 不同的邮件服务商有不同的SMTP服务器地址和端口
# 例如:
# Gmail: smtp.gmail.com, 587 (TLS) 或 465 (SSL)
# Outlook/Hotmail: smtp.office365.com, 587 (TLS)
# QQ邮箱: smtp.qq.com, 465 (SSL) 或 587 (TLS)
# 这里以Gmail为例,使用TLS加密
smtp_server = "smtp.gmail.com"
smtp_port = 587
print(f"尝试连接SMTP服务器: {smtp_server}:{smtp_port}...")
server = smtplib.SMTP(smtp_server, smtp_port)
server.starttls() # 启动TLS加密
print(f"尝试登录邮箱: {sender_email}...")
server.login(sender_email, sender_password)
# 发送邮件
text = msg.as_string() # 将MIMEMultipart对象转换为字符串
server.sendmail(sender_email, receiver_email, text)
print("邮件发送成功!")
except smtplib.SMTPAuthenticationError as e:
print(f"SMTP认证失败:请检查邮箱地址和密码/授权码是否正确。错误信息:{e}")
print("对于Gmail等服务,可能需要生成应用专用密码。")
except smtplib.SMTPConnectError as e:
print(f"SMTP连接失败:请检查服务器地址和端口,或网络连接。错误信息:{e}")
except smtplib.SMTPException as e:
print(f"发送邮件时发生SMTP错误:{e}")
except FileNotFoundError:
print(f"错误:附件文件 '{attachment_path}' 未找到。")
except Exception as e:
print(f"发生未知错误:{e}")
finally:
if 'server' in locals() and server:
server.quit()
print("SMTP服务器连接已关闭。")
# --- 使用示例 ---
if __name__ == "__main__":
# 请替换为你的实际邮箱信息和附件路径
SENDER_EMAIL = "your_email@gmail.com" # 你的发件邮箱
# 注意:对于Gmail等,这里通常不是你的登录密码,而是“应用专用密码”
SENDER_PASSWORD = "your_app_password"
RECEIVER_EMAIL = "recipient_email@example.com" # 收件人邮箱
EMAIL_SUBJECT = "Python发送的带附件测试邮件"
EMAIL_BODY = "你好!这是一封Python脚本发送的测试邮件,包含一个附件。"
# 假设你有一个名为 'test.txt' 的文件在当前脚本目录下
# 或者指定一个完整路径,例如:r"C:\Users\YourUser\Documents\report.pdf"
ATTACHMENT_FILE = "test.txt"
# 为了测试,创建一个虚拟的附件文件
with open(ATTACHMENT_FILE, "w") as f:
f.write("这是附件里的内容,希望你能收到!\n")
f.write("附件可以是任何类型的文件,比如图片、PDF或文档。")
send_email_with_attachment(SENDER_EMAIL, SENDER_PASSWORD, RECEIVER_EMAIL, EMAIL_SUBJECT, EMAIL_BODY, ATTACHMENT_FILE)
# 清理测试文件
if os.path.exists(ATTACHMENT_FILE):
os.remove(ATTACHMENT_FILE)
print(f"测试文件 '{ATTACHMENT_FILE}' 已删除。")
为什么直接用smtplib发送普通文本邮件和发送带附件邮件的代码看起来差别那么大?
这个问题,我个人觉得是初学者最容易感到困惑的地方。说实话,一开始我也觉得这两种方式差异巨大,似乎完全是两套体系。但深入理解后,你会发现这其实是邮件协议设计的必然结果。
简单来说,当我们只发送纯文本邮件时,邮件内容可以被看作是一个简单的字符串,直接作为邮件体发送即可。smtplib模块本身就能很好地处理这种“一维”的数据流。你可能只需要一个MIMEText对象,甚至更简单地直接传递字符串。
然而,一旦涉及附件,情况就复杂了。邮件本身是一个文本协议,它并不能直接“传输”一个二进制文件(比如图片、PDF或压缩包)。这就好比你不能直接把一个包裹塞进一个信封里,你需要把包裹打包好,贴上标签,然后交给邮局处理。
这里的“打包”和“贴标签”工作,在Python中就由email.mime模块家族来完成。
MIMEMultipart:它就像一个大信封,能把邮件的各个部分(正文、附件1、附件2等)都装进去。它负责声明这封邮件是“多部分”的,并且每个部分之间如何分隔。MIMEText:用于处理邮件的正文部分,告诉邮件客户端这部分是纯文本,编码是什么。MIMEBase:这是处理非文本附件的基础类。它需要我们把附件的二进制数据读进来,然后进行Base64编码。Base64编码能把二进制数据转换成ASCII字符,这样邮件系统就能像传输普通文本一样传输它了。encoders.encode_base64:专门负责把二进制数据编码成Base64格式。add_header:这一步至关重要,它给附件“贴上标签”,告诉邮件客户端这个部分是什么文件类型(Content-Type,比如application/pdf或image/jpeg),以及它的原始文件名(Content-Disposition)。
所以,这种“看起来差别大”的根本原因在于,发送带附件的邮件,实际上是在构建一个更复杂的、结构化的邮件对象,而不是简单地发送一段文本。它需要我们明确地告诉邮件服务器和接收方的客户端,邮件的哪些部分是正文,哪些是附件,附件是什么类型,叫什么名字,等等。这背后都是为了遵循MIME(Multipurpose Internet Mail Extensions)标准,确保邮件内容能够被正确解析和显示。
处理邮件附件时,有哪些常见的编码问题和文件类型考虑?
在处理邮件附件时,编码和文件类型是两个绕不开的关键点,处理不好就可能导致附件乱码、无法打开或者被邮件系统误判。
1. 编码问题:
- Base64编码是核心:邮件协议最初是为传输ASCII文本设计的,直接发送二进制数据(如图片、PDF)会导致数据损坏。Base64编码就是为了解决这个问题而生。它将任意二进制数据转换为一套由64种ASCII字符组成的字符串。在Python中,
email.encoders.encode_base64()就是做这个事的。如果你忘记对附件进行Base64编码,接收方看到的附件很可能就是一堆乱码或者根本无法识别。 - 文本附件的字符编码:如果你的附件本身是文本文件(比如
.txt,.csv,.log),那么在将其作为附件发送时,需要确保它在本地保存时的字符编码(如UTF-8, GBK)与你在MIMEText或MIMEBase中声明的charset一致。虽然附件通常会经过Base64编码,但如果文件内容本身是文本,其内部的字符编码仍然重要,尤其是在接收方打开附件时。通常,使用UTF-8是一个比较稳妥的选择,因为它支持绝大多数字符。 - 邮件正文的编码:虽然和附件不是一回事,但邮件正文的编码也常出问题。我们通常会使用
MIMEText(body, 'plain', 'utf-8')来确保正文内容(尤其是包含中文等非ASCII字符时)不会乱码。
2. 文件类型(MIME类型)考虑:
Content-Type的重要性:这是告诉邮件客户端附件是什么类型文件的关键。MIMEBase在创建时需要两个参数:maintype和subtype,它们共同构成了MIME类型(例如,image/jpeg,application/pdf,text/plain)。- 如果你指定了正确的MIME类型,比如一个PDF文件设置为
application/pdf,那么接收方的邮件客户端就能识别它是一个PDF,并可能直接预览或用默认的PDF阅读器打开。 - 如果你使用了通用的
application/octet-stream,这意味着“我不知道这是什么类型,但它是一个二进制数据流”。客户端通常会提示用户保存或选择程序打开,这虽然通用,但用户体验可能不如直接识别。
- 如果你指定了正确的MIME类型,比如一个PDF文件设置为
- 如何确定MIME类型:
- 手动指定:如果你知道附件类型,可以直接写死,比如
MIMEBase('application', 'pdf')。 mimetypes模块:Python标准库中的mimetypes模块可以根据文件扩展名自动猜测MIME类型,这在处理多种未知类型附件时非常有用。import mimetypes # ... ctype, encoding = mimetypes.guess_type(attachment_path) if ctype is None or encoding is not None: ctype = 'application/octet-stream' # 无法猜测或有编码,回退到通用类型 maintype, subtype = ctype.split('/', 1) part = MIMEBase(maintype, subtype) # ...
- 手动指定:如果你知道附件类型,可以直接写死,比如
Content-Disposition:这个头部告诉邮件客户端如何“处置”这个附件。我们通常会设置attachment; filename="your_file_name.ext",这表示这是一个附件,并且建议客户端用指定的文件名保存。如果没有这个头部,或者设置不当,附件可能不会被识别为独立文件,而是直接显示在邮件正文里(如果客户端尝试解析的话),或者文件名乱码。
总而言之,处理附件时,你需要像个细心的邮递员,不仅要确保包裹内容完好无损(Base64编码),还要在包裹上贴好准确的标签(MIME类型和文件名),这样收件人才能顺利收到并正确使用你的“包裹”。
发送邮件时遇到认证失败或连接超时,该如何排查和解决?
在用Python脚本发送邮件时,认证失败和连接超时是两个非常常见的“拦路虎”。我遇到过太多次了,每次都得从头开始排查。这里我总结了一些经验,希望能帮到你。
1. 认证失败(smtplib.SMTPAuthenticationError):
这是最常见的问题,通常意味着你的脚本无法登录到SMTP服务器。
- 密码或授权码错误:
- 检查密码:最直接的原因就是密码输错了。请仔细核对你的发件邮箱地址和密码。
- 应用专用密码:这是很多邮件服务商(尤其是Gmail、Outlook、QQ邮箱等)在开启了两步验证(2FA)后强制要求的。你不能直接使用你的邮箱登录密码来登录SMTP服务器。你需要在邮箱设置中生成一个“应用专用密码”或“授权码”,然后用这个生成的密码来替代你的真实登录密码。这是我见过90%以上认证失败的原因。
- Gmail:访问Google账户安全设置,找到“应用密码”或“应用专用密码”选项。
- Outlook/Hotmail:在微软账户安全设置中,查找“应用密码”。
- QQ邮箱:在邮箱设置中,找到“账户”选项卡,开启SMTP服务并生成“授权码”。
- 旧版“允许不安全应用访问”:对于Gmail,以前有个选项叫“允许不安全应用访问”,现在基本已经停用或者不推荐使用了。如果你的代码依赖这个,现在很可能不再奏效。
- SMTP服务器设置问题:
- 发件人邮箱的SMTP服务未开启:有些邮箱默认关闭了SMTP服务,需要手动去邮箱设置里开启。
- 发件人邮箱的安全策略:有些企业邮箱或自定义域名邮箱会有更严格的安全策略,可能会限制外部IP访问或需要特定的认证方式。
- IP地址限制:极少数情况下,你的发件邮箱可能设置了IP地址白名单,只有特定IP才能登录。
排查方法:
- 手动登录:尝试用你的邮箱地址和密码(或授权码)在网页端或邮件客户端(如Outlook, Thunderbird)手动登录并发送一封邮件,确认邮箱本身工作正常。
- 生成应用专用密码:如果你的邮箱开启了两步验证,立即去生成一个应用专用密码,并用它替换代码中的
SENDER_PASSWORD。 - 检查邮箱设置:登录你的发件邮箱网页版,检查SMTP服务是否开启,是否有其他安全设置限制。
- 日志分析:如果服务器返回了更详细的错误信息,仔细阅读它们。
2. 连接超时(smtplib.SMTPConnectError 或其他网络错误):
连接超时通常与网络环境、服务器地址或端口配置有关。
- SMTP服务器地址或端口错误:
- 核对服务器地址:不同邮件服务商的SMTP服务器地址不同(例如
smtp.gmail.com,smtp.office365.com,smtp.qq.com)。确保你的代码中填写的地址是正确的。 - 核对端口号:
587:通常用于TLS加密连接(需要调用server.starttls())。465:通常用于SSL加密连接(需要使用smtplib.SMTP_SSL而不是smtplib.SMTP)。- 混淆端口和加密方式是常见错误。如果你用
smtplib.SMTP连接465端口,或用smtplib.SMTP_SSL连接587端口,都可能失败。
- 核对服务器地址:不同邮件服务商的SMTP服务器地址不同(例如
- 防火墙或网络限制:
- 本地防火墙:你的电脑或服务器上的防火墙可能阻止了Python脚本对外连接
587或465端口。检查防火墙设置,尝试临时关闭防火墙进行测试。 - 网络环境:你所在的网络(公司网络、公共Wi-Fi)可能对某些端口进行了限制或屏蔽。尝试更换网络环境(例如,从公司网络切换到手机热点)进行测试。
- 代理服务器:如果你在代理服务器后面,确保你的Python环境配置了正确的代理设置。
- 本地防火墙:你的电脑或服务器上的防火墙可能阻止了Python脚本对外连接
- DNS解析问题:你的系统可能无法正确解析SMTP服务器的域名。尝试
ping smtp.gmail.com等命令,看是否能解析并连通。 - SMTP服务器暂时故障:虽然不常见,但邮件服务商的SMTP服务器也可能偶尔出现短暂的故障或维护。等待一段时间再试。
排查方法:
- 双重检查配置:再次核对SMTP服务器地址和端口,确保与你的邮件服务商要求一致。
- 测试端口连通性:
- 在命令行使用
telnet smtp.gmail.com 587(或对应服务器和端口),如果能看到欢迎信息,说明端口是开放的。如果连接失败或超时,则可能是防火墙或网络问题。 - 对于Windows用户,可能需要先在“程序和功能”中开启Telnet客户端。
- 在命令行使用
- 更换网络环境:如上所述,切换网络可以快速判断是否是网络限制。
- 检查日志:Python的
smtplib模块在连接失败时会抛出异常,异常信息通常会给出一些线索。
遇到这些问题时,保持耐心,一步步排查,通常都能找到症结所在。记住,大多数时候问题出在配置上,而不是代码逻辑本身。
本篇关于《Python发邮件带附件教程详解》的介绍就到此结束啦,但是学无止境,想要了解学习更多关于文章的相关知识,请关注golang学习网公众号!
谷歌邮箱网页版登录官网地址
- 上一篇
- 谷歌邮箱网页版登录官网地址
- 下一篇
- 今日头条文件下载路径全解析
-
- 文章 · python教程 | 7小时前 |
- PandasDataFrame列赋值NaN方法解析
- 205浏览 收藏
-
- 文章 · python教程 | 7小时前 |
- Python元组括号用法与列表推导注意事项
- 143浏览 收藏
-
- 文章 · python教程 | 8小时前 |
- ib\_insync获取SPX历史数据教程
- 395浏览 收藏
-
- 文章 · python教程 | 8小时前 |
- GTK3Python动态CSS管理技巧分享
- 391浏览 收藏
-
- 文章 · python教程 | 8小时前 |
- Python微服务开发:Nameko框架全解析
- 269浏览 收藏
-
- 文章 · python教程 | 8小时前 |
- Xarray重采样技巧:解决维度冲突方法
- 410浏览 收藏
-
- 文章 · python教程 | 9小时前 | 多进程编程 进程间通信 进程池 process multiprocessing
- Python3多进程技巧与实战指南
- 131浏览 收藏
-
- 文章 · python教程 | 9小时前 |
- Python列表线程传递方法详解
- 382浏览 收藏
-
- 文章 · python教程 | 10小时前 |
- Python国内镜像源设置方法
- 154浏览 收藏
-
- 文章 · python教程 | 10小时前 |
- 数据库迁移步骤与实用技巧分享
- 251浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- ChatExcel酷表
- ChatExcel酷表是由北京大学团队打造的Excel聊天机器人,用自然语言操控表格,简化数据处理,告别繁琐操作,提升工作效率!适用于学生、上班族及政府人员。
- 3166次使用
-
- Any绘本
- 探索Any绘本(anypicturebook.com/zh),一款开源免费的AI绘本创作工具,基于Google Gemini与Flux AI模型,让您轻松创作个性化绘本。适用于家庭、教育、创作等多种场景,零门槛,高自由度,技术透明,本地可控。
- 3379次使用
-
- 可赞AI
- 可赞AI,AI驱动的办公可视化智能工具,助您轻松实现文本与可视化元素高效转化。无论是智能文档生成、多格式文本解析,还是一键生成专业图表、脑图、知识卡片,可赞AI都能让信息处理更清晰高效。覆盖数据汇报、会议纪要、内容营销等全场景,大幅提升办公效率,降低专业门槛,是您提升工作效率的得力助手。
- 3408次使用
-
- 星月写作
- 星月写作是国内首款聚焦中文网络小说创作的AI辅助工具,解决网文作者从构思到变现的全流程痛点。AI扫榜、专属模板、全链路适配,助力新人快速上手,资深作者效率倍增。
- 4512次使用
-
- MagicLight
- MagicLight.ai是全球首款叙事驱动型AI动画视频创作平台,专注于解决从故事想法到完整动画的全流程痛点。它通过自研AI模型,保障角色、风格、场景高度一致性,让零动画经验者也能高效产出专业级叙事内容。广泛适用于独立创作者、动画工作室、教育机构及企业营销,助您轻松实现创意落地与商业化。
- 3788次使用
-
- Flask框架安装技巧:让你的开发更高效
- 2024-01-03 501浏览
-
- Django框架中的并发处理技巧
- 2024-01-22 501浏览
-
- 提升Python包下载速度的方法——正确配置pip的国内源
- 2024-01-17 501浏览
-
- Python与C++:哪个编程语言更适合初学者?
- 2024-03-25 501浏览
-
- 品牌建设技巧
- 2024-04-06 501浏览

