Python - 如何发送 html 电子邮件并附上带有 Python 的图片?
Python - How to send an html email and attach an image with Python?
我收到一个 "error: ValueError: Cannot convert mixed to alternative."
当我插入打开的图像和 msg.add_attachment 块时出现此错误(在 btw #### #### 中突出显示)。没有它,代码运行良好。我需要将电子邮件作为 html 和图片附件发送。
import os
import imghdr
from email.message import EmailMessage
import smtplib
EMAIL_ADDRESS = os.environ.get('EMAIL-USER')
EMAIL_PASSWORD = os.environ.get('EMAIL-PASS')
Message0 = "HelloWorld1"
Message1 = "HelloWorld2"
msg = EmailMessage()
msg['Subject'] = 'Hello WORLD'
msg['From'] = EMAIL_ADDRESS
msg['To'] = EMAIL_ADDRESS
msg.set_content('This is a plain text email, see HTML format')
########################################################################
with open('screenshot.png', 'rb') as f:
file_data = f.read()
file_type = imghdr.what(f.name)
file_name = f.name
msg.add_attachment(file_data, maintype='image', subtype=file_type, filename=file_name)
#########################################################################
msg.add_alternative("""\
<!DOCTYPE html>
<html>
<body>
<h1 style="color:Blue;">Hello World</h1>
{Message0}
{Message1}
</body>
</html>
""".format(**locals()), subtype='html')
with smtplib.SMTP_SSL('smtp.gmail.com', 465) as smtp:
smtp.login(EMAIL_ADDRESS, EMAIL_PASSWORD)
smtp.send_message(msg)
print("email sent")
对于最终结果,我需要能够通过 Python 发送电子邮件并附加图像。
电子邮件可以由单个部分组成,也可以是多部分邮件。
如果是多部分消息,通常是 multipart/alternative
或 multipart/mixed
.
multipart/alternative
表示同一内容有 2 个或更多版本(例如纯文本和 html)
multipart/mixed
用于需要将多个不同的内容打包在一起(例如一封邮件和一个附件)
当使用 multipart
时实际发生的是电子邮件由一个 "multipart" 容器组成,其中包含其他部分,例如对于 text+html 是这样的:
multipart/alternative
部分
text/plain
部分
text/html
部分
如果是带附件的邮件,你可以这样写:
multipart/mixed
部分
text/plain
部分
image/png
部分
因此,容器是 mixed
或 alternative
,但不能同时是两者。那么,如何兼得呢?您可以嵌套它们,例如:
multipart/mixed
部分
multipart/alternative
部分
text/plain
部分
text/html
部分
image/png
部分
所以,现在您有一封包含消息和附件的电子邮件,并且该消息既有纯文本也有 html。
现在,在代码中,这是基本思想:
msg = EmailMessage()
msg['Subject'] = 'Subject'
msg['From'] = 'from@email'
msg['To'] = 'to@email'
msg.set_content('This is a plain text')
msg.add_attachment(b'xxxxxx', maintype='image', subtype='png', filename='image.png')
# Now there are plain text and attachment.
# HTML should be added as alternative to the plain text part:
text_part, attachment_part = msg.iter_parts()
text_part.add_alternative("<p>html contents</p>", subtype='html')
顺便说一句,您可以这样查看每个部分的内容:
>>> plain_text_part, html_text_part = text_part.iter_parts()
>>> print(plain_text_part)
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: 7bit
This is a plain text
>>> print(html_text_part)
Content-Type: text/html; charset="utf-8"
Content-Transfer-Encoding: 7bit
MIME-Version: 1.0
<p>html contents</p>
>>> print(attachment_part)
Content-Type: image/png
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="image.png"
MIME-Version: 1.0
eHh4eHh4
>>> print(msg)
Subject: Subject
From: from@email
To: to@email
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="===============2219697219721248811=="
--===============2219697219721248811==
Content-Type: multipart/alternative;
boundary="===============5680305804901241482=="
--===============5680305804901241482==
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: 7bit
This is a plain text
--===============5680305804901241482==
Content-Type: text/html; charset="utf-8"
Content-Transfer-Encoding: 7bit
MIME-Version: 1.0
<p>html contents</p>
--===============5680305804901241482==--
--===============2219697219721248811==
Content-Type: image/png
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="image.png"
MIME-Version: 1.0
eHh4eHh4
--===============2219697219721248811==--
这是@zvone 提供的示例的补充示例。这里的区别是我想要一封有 2 个版本的邮件。 plain-text 中的一个带有图像作为附件。另一部分是带有嵌入图像的 html。
所以邮件的布局是这样的:
multipart/mixed part
multipart/alternative part
multipart/related part
(html 与 img)
text/html part
image/png part
text/plain part
(纯文本)
image/png part
(附件)
plain-text版本:textfile.txt
Hello World
html版本:mail_content.html
<html>
<head></head>
<body>
<h1>Hello World</h1>
<img src="cid:{graph_example_img_cid}" />
</body>
</html>
当然还有 Python 代码(针对 Python 3.10
进行了测试)。图像的嵌入遵循最新 Python Standard Library of email
.
中的示例
Python代码:main.py
# var
mail_server: str = "****smtp****"
user: str = "****@****.com"
me = user
you = user
# imports
import smtplib # mail actual sending function
import imghdr # And imghdr to find the types of our images
from email.message import EmailMessage
from email.utils import make_msgid
import datetime
# Open a plain text file for reading. For this example, assume that
# the text file contains only ASCII characters.
textfile_filepath: str = "textfile.txt"
with open(textfile_filepath) as fp:
# Create a text/plain message
msg = EmailMessage()
msg.set_content(fp.read())
# me == the sender's email address
# you == the recipient's email address
msg['Subject'] = 'Test Email with 2 versions'
msg['From'] = me
msg['To'] = you
# attachements
# Open the files in binary mode. Use imghdr to figure out the
# MIME subtype for each specific image.
image_filepath: str = "test_image.png"
with open(image_filepath, 'rb') as fp:
img_data = fp.read()
msg.add_attachment(img_data, maintype='image', subtype=imghdr.what(None, img_data))
text_part, attachment_part = msg.iter_parts()
## HTML alternative using Content ID
html_content_filepath: str = "mail_content.html"
graph_cid = make_msgid()
with open(html_content_filepath) as fp:
# Create a text/plain message
#msg = MIMEText(fp.read())
raw_html: str = fp.read()
# note that we needed to peel the <> off the msgid for use in the html.
html_content: str = raw_html.format(graph_example_img_cid=graph_cid[1:-1])
# add to email message
text_part.add_alternative(html_content,subtype='html')
# Now add the related image to the html part.
with open(image_filepath, 'rb') as img:
text_part, html_part = text_part.iter_parts()
html_part.add_related(img.read(), 'image', 'jpeg', cid=graph_cid)
# Send the message via our own SMTP server, but don't include the envelope header.
s = smtplib.SMTP(mail_server)
s.sendmail(me, [you], msg.as_string())
s.quit()
# log
current_time = datetime.datetime.now()
print("{} >>> Email sent (From: {}, To: {})".format(
current_time.isoformat(), me, you
))
我收到一个 "error: ValueError: Cannot convert mixed to alternative."
当我插入打开的图像和 msg.add_attachment 块时出现此错误(在 btw #### #### 中突出显示)。没有它,代码运行良好。我需要将电子邮件作为 html 和图片附件发送。
import os
import imghdr
from email.message import EmailMessage
import smtplib
EMAIL_ADDRESS = os.environ.get('EMAIL-USER')
EMAIL_PASSWORD = os.environ.get('EMAIL-PASS')
Message0 = "HelloWorld1"
Message1 = "HelloWorld2"
msg = EmailMessage()
msg['Subject'] = 'Hello WORLD'
msg['From'] = EMAIL_ADDRESS
msg['To'] = EMAIL_ADDRESS
msg.set_content('This is a plain text email, see HTML format')
########################################################################
with open('screenshot.png', 'rb') as f:
file_data = f.read()
file_type = imghdr.what(f.name)
file_name = f.name
msg.add_attachment(file_data, maintype='image', subtype=file_type, filename=file_name)
#########################################################################
msg.add_alternative("""\
<!DOCTYPE html>
<html>
<body>
<h1 style="color:Blue;">Hello World</h1>
{Message0}
{Message1}
</body>
</html>
""".format(**locals()), subtype='html')
with smtplib.SMTP_SSL('smtp.gmail.com', 465) as smtp:
smtp.login(EMAIL_ADDRESS, EMAIL_PASSWORD)
smtp.send_message(msg)
print("email sent")
对于最终结果,我需要能够通过 Python 发送电子邮件并附加图像。
电子邮件可以由单个部分组成,也可以是多部分邮件。
如果是多部分消息,通常是 multipart/alternative
或 multipart/mixed
.
multipart/alternative
表示同一内容有 2 个或更多版本(例如纯文本和 html)multipart/mixed
用于需要将多个不同的内容打包在一起(例如一封邮件和一个附件)
当使用 multipart
时实际发生的是电子邮件由一个 "multipart" 容器组成,其中包含其他部分,例如对于 text+html 是这样的:
multipart/alternative
部分text/plain
部分text/html
部分
如果是带附件的邮件,你可以这样写:
multipart/mixed
部分text/plain
部分image/png
部分
因此,容器是 mixed
或 alternative
,但不能同时是两者。那么,如何兼得呢?您可以嵌套它们,例如:
multipart/mixed
部分multipart/alternative
部分text/plain
部分text/html
部分
image/png
部分
所以,现在您有一封包含消息和附件的电子邮件,并且该消息既有纯文本也有 html。
现在,在代码中,这是基本思想:
msg = EmailMessage()
msg['Subject'] = 'Subject'
msg['From'] = 'from@email'
msg['To'] = 'to@email'
msg.set_content('This is a plain text')
msg.add_attachment(b'xxxxxx', maintype='image', subtype='png', filename='image.png')
# Now there are plain text and attachment.
# HTML should be added as alternative to the plain text part:
text_part, attachment_part = msg.iter_parts()
text_part.add_alternative("<p>html contents</p>", subtype='html')
顺便说一句,您可以这样查看每个部分的内容:
>>> plain_text_part, html_text_part = text_part.iter_parts()
>>> print(plain_text_part)
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: 7bit
This is a plain text
>>> print(html_text_part)
Content-Type: text/html; charset="utf-8"
Content-Transfer-Encoding: 7bit
MIME-Version: 1.0
<p>html contents</p>
>>> print(attachment_part)
Content-Type: image/png
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="image.png"
MIME-Version: 1.0
eHh4eHh4
>>> print(msg)
Subject: Subject
From: from@email
To: to@email
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="===============2219697219721248811=="
--===============2219697219721248811==
Content-Type: multipart/alternative;
boundary="===============5680305804901241482=="
--===============5680305804901241482==
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: 7bit
This is a plain text
--===============5680305804901241482==
Content-Type: text/html; charset="utf-8"
Content-Transfer-Encoding: 7bit
MIME-Version: 1.0
<p>html contents</p>
--===============5680305804901241482==--
--===============2219697219721248811==
Content-Type: image/png
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="image.png"
MIME-Version: 1.0
eHh4eHh4
--===============2219697219721248811==--
这是@zvone 提供的示例的补充示例。这里的区别是我想要一封有 2 个版本的邮件。 plain-text 中的一个带有图像作为附件。另一部分是带有嵌入图像的 html。
所以邮件的布局是这样的:
multipart/mixed part
multipart/alternative part
multipart/related part
(html 与 img)text/html part
image/png part
text/plain part
(纯文本)
image/png part
(附件)
plain-text版本:textfile.txt
Hello World
html版本:mail_content.html
<html>
<head></head>
<body>
<h1>Hello World</h1>
<img src="cid:{graph_example_img_cid}" />
</body>
</html>
当然还有 Python 代码(针对 Python 3.10
进行了测试)。图像的嵌入遵循最新 Python Standard Library of email
.
Python代码:main.py
# var
mail_server: str = "****smtp****"
user: str = "****@****.com"
me = user
you = user
# imports
import smtplib # mail actual sending function
import imghdr # And imghdr to find the types of our images
from email.message import EmailMessage
from email.utils import make_msgid
import datetime
# Open a plain text file for reading. For this example, assume that
# the text file contains only ASCII characters.
textfile_filepath: str = "textfile.txt"
with open(textfile_filepath) as fp:
# Create a text/plain message
msg = EmailMessage()
msg.set_content(fp.read())
# me == the sender's email address
# you == the recipient's email address
msg['Subject'] = 'Test Email with 2 versions'
msg['From'] = me
msg['To'] = you
# attachements
# Open the files in binary mode. Use imghdr to figure out the
# MIME subtype for each specific image.
image_filepath: str = "test_image.png"
with open(image_filepath, 'rb') as fp:
img_data = fp.read()
msg.add_attachment(img_data, maintype='image', subtype=imghdr.what(None, img_data))
text_part, attachment_part = msg.iter_parts()
## HTML alternative using Content ID
html_content_filepath: str = "mail_content.html"
graph_cid = make_msgid()
with open(html_content_filepath) as fp:
# Create a text/plain message
#msg = MIMEText(fp.read())
raw_html: str = fp.read()
# note that we needed to peel the <> off the msgid for use in the html.
html_content: str = raw_html.format(graph_example_img_cid=graph_cid[1:-1])
# add to email message
text_part.add_alternative(html_content,subtype='html')
# Now add the related image to the html part.
with open(image_filepath, 'rb') as img:
text_part, html_part = text_part.iter_parts()
html_part.add_related(img.read(), 'image', 'jpeg', cid=graph_cid)
# Send the message via our own SMTP server, but don't include the envelope header.
s = smtplib.SMTP(mail_server)
s.sendmail(me, [you], msg.as_string())
s.quit()
# log
current_time = datetime.datetime.now()
print("{} >>> Email sent (From: {}, To: {})".format(
current_time.isoformat(), me, you
))