You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python中使用EmailMessage和smtplib无法同时发送文本与HTML邮件

问题

使用EmailMessage和smtplib发送邮件时,单独发送纯文本邮件正常,但添加带嵌入图片的HTML内容(通过msg.add_alternative(html))后,打开邮件仅能看到HTML部分,纯文本内容仅在收件箱列表可见,打开邮件后不展示。

原代码片段:

import smtplib
from email.message import EmailMessage
from email.header import Header
from email.utils import formataddr, make_msgid

SENDER_MAIL = "sender@email.com"
SENDER_PASSWORD = "sender password"
SENDER_TITLE = "sender title"
recipient = "recipient@email.com"

subject = "This is the subject."
body = "This is the body."
html_string = """
                <!DOCTYPE html>
                <html lang="en">
                <head>
                    <meta charset="UTF-8">
                    <meta http-equiv="X-UA-Compatible" content="IE-edge">
                    <meta name="viewport" content="width=device-width, initial-scale=1.0">
                    <title>email</title>
                </head>
                <body>
                    <table>
                        <tbody>
                            <tr>
                                <td>
                                    <td>
                                        <img width="138" height="138" src="cid:{dp_cid}" alt="">
                                    </td>
                                </td>
                            </tr>
                        <tbody>
                    <table>
                <body>
            """

def compose_mail(subject, body, html):
    msg = EmailMessage()
    msg['Subject'] = subject
    msg['From'] = formataddr((str(Header(SENDER_TITLE, 'utf-8')), SENDER_MAIL))
    msg['To'] = recipient

    # ================= Attach plain text as body
    msg.set_content(body, subtype="plain", charset='utf-8')

    # ================= Load images to the html
    dp_cid = make_msgid()

    msg.add_alternative(html.format(dp_cid=dp_cid[1:-1]), subtype='html')

    with open("Images/dp.png", "rb") as img:
        msg.get_payload()[1].add_related(img.read(), "image", "png", cid=dp_cid)

    # ================= Send mail
    server = smtplib.SMTP_SSL('server', port)
    server.login(SENDER_MAIL, SENDER_PASSWORD)
    server.send_message(msg)
    server.quit()
    print("Mail sent.")

compose_mail(subject, body, html_string)
原因分析

问题核心在于直接通过msg.get_payload()[1]操作HTML部分添加关联资源,这种方式会破坏邮件的MIME结构,导致邮件客户端无法正确识别纯文本与HTML的替代关系,最终只渲染HTML内容。此外,原HTML代码存在标签错误(未闭合的<tbody>、<table>,多余嵌套的<td>),虽不直接影响纯文本显示,但会干扰HTML渲染效果。

解决方法

不要直接操作get_payload(),而是保存msg.add_alternative()返回的HTML部分对象,通过该对象添加嵌入资源;同时修正HTML标签错误。

修正后的代码:

import smtplib
from email.message import EmailMessage
from email.header import Header
from email.utils import formataddr, make_msgid

SENDER_MAIL = "sender@email.com"
SENDER_PASSWORD = "sender password"
SENDER_TITLE = "sender title"
recipient = "recipient@email.com"

subject = "This is the subject."
body = "This is the body."
html_string = """
                <!DOCTYPE html>
                <html lang="en">
                <head>
                    <meta charset="UTF-8">
                    <meta http-equiv="X-UA-Compatible" content="IE=edge">
                    <meta name="viewport" content="width=device-width, initial-scale=1.0">
                    <title>email</title>
                </head>
                <body>
                    <table>
                        <tbody>
                            <tr>
                                <td>
                                    <img width="138" height="138" src="cid:{dp_cid}" alt="">
                                </td>
                            </tr>
                        </tbody>
                    </table>
                </body>
            </html>
            """

def compose_mail(subject, body, html):
    msg = EmailMessage()
    msg['Subject'] = subject
    msg['From'] = formataddr((str(Header(SENDER_TITLE, 'utf-8')), SENDER_MAIL))
    msg['To'] = recipient

    # 设置纯文本内容
    msg.set_content(body, subtype="plain", charset='utf-8')

    # 添加HTML替代内容并获取对应对象
    dp_cid = make_msgid()
    html_part = msg.add_alternative(html.format(dp_cid=dp_cid[1:-1]), subtype='html')

    # 向HTML部分添加嵌入图片
    with open("Images/dp.png", "rb") as img:
        html_part.add_related(img.read(), "image", "png", cid=dp_cid)

    # 发送邮件
    server = smtplib.SMTP_SSL('server', port)
    server.login(SENDER_MAIL, SENDER_PASSWORD)
    server.send_message(msg)
    server.quit()
    print("Mail sent.")

compose_mail(subject, body, html_string)
关键修改点
  • 保存msg.add_alternative()返回的html_part对象,通过它添加关联资源,避免破坏MIME结构。
  • 修正HTML标签错误:闭合</tbody>、</table>、</html>,移除多余嵌套的<td>,修正IE-edge为IE=edge。

内容的提问来源于stack exchange,提问作者Milind Khobragade

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 19:36:32