我有问题理解如何使用Python电子邮件附件。我已经成功地通过smtplib电子邮件发送了简单的消息。有人能解释一下如何在电子邮件中发送附件吗?我知道网上还有其他的帖子,但作为一个Python初学者,我发现它们很难理解。
当前回答
这是我最终使用的代码:
import smtplib
from email.MIMEMultipart import MIMEMultipart
from email.MIMEBase import MIMEBase
from email import Encoders
SUBJECT = "Email Data"
msg = MIMEMultipart()
msg['Subject'] = SUBJECT
msg['From'] = self.EMAIL_FROM
msg['To'] = ', '.join(self.EMAIL_TO)
part = MIMEBase('application', "octet-stream")
part.set_payload(open("text.txt", "rb").read())
Encoders.encode_base64(part)
part.add_header('Content-Disposition', 'attachment; filename="text.txt"')
msg.attach(part)
server = smtplib.SMTP(self.EMAIL_SERVER)
server.sendmail(self.EMAIL_FROM, self.EMAIL_TO, msg.as_string())
代码与Oli的帖子大致相同。
代码基于二进制文件电子邮件附件问题的帖子。
其他回答
下面是Python 3.6及更新版本的更新版本,使用Python标准库中经过大修的电子邮件模块的EmailMessage类。
import mimetypes
import os
import smtplib
from email.message import EmailMessage
username = "user@example.com"
password = "password"
smtp_url = "smtp.example.com"
port = 587
def send_mail(subject: str, send_from: str, send_to: str, message: str, directory: str, filename: str):
# Create the email message
msg = EmailMessage()
msg['Subject'] = subject
msg['From'] = send_from
msg['To'] = send_to
# Set email content
msg.set_content(message)
path = directory + filename
if os.path.exists(path):
ctype, encoding = mimetypes.guess_type(path)
if ctype is None or encoding is not None:
# No guess could be made, or the file is encoded (compressed), so
# use a generic bag-of-bits type.
ctype = 'application/octet-stream'
maintype, subtype = ctype.split('/', 1)
# Add email attachment
with open(path, 'rb') as fp:
msg.add_attachment(fp.read(),
maintype=maintype,
subtype=subtype,
filename=filename)
smtp = smtplib.SMTP(smtp_url, port)
smtp.starttls() # for using port 587
smtp.login(username, password)
smtp.send_message(msg)
smtp.quit()
你可以在这里找到更多的例子。
这里目前给出的答案都不能正确地与GMail、Outlook 2016等客户机的文件名中的非ascii符号一起工作,以及其他不支持RFC 2231的客户机(例如,参见这里)。下面的Python 3代码改编自其他一些stackoverflow的答案(对不起,没有保存原始链接)和Python 2.7的odoo/openerp代码(参见ir_mail_server.py)。它可以正确地与GMail和其他设备一起工作,并且还使用SSL。
import smtplib, ssl
from os.path import basename
from email.mime.base import MIMEBase
from mimetypes import guess_type
from email.encoders import encode_base64
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.utils import COMMASPACE, formatdate
from email.charset import Charset
def try_coerce_ascii(string_utf8):
"""Attempts to decode the given utf8-encoded string
as ASCII after coercing it to UTF-8, then return
the confirmed 7-bit ASCII string.
If the process fails (because the string
contains non-ASCII characters) returns ``None``.
"""
try:
string_utf8.encode('ascii')
except UnicodeEncodeError:
return
return string_utf8
def encode_header_param(param_text):
"""Returns an appropriate RFC 2047 encoded representation of the given
header parameter value, suitable for direct assignation as the
param value (e.g. via Message.set_param() or Message.add_header())
RFC 2822 assumes that headers contain only 7-bit characters,
so we ensure it is the case, using RFC 2047 encoding when needed.
:param param_text: unicode or utf-8 encoded string with header value
:rtype: string
:return: if ``param_text`` represents a plain ASCII string,
return the same 7-bit string, otherwise returns an
ASCII string containing the RFC2047 encoded text.
"""
if not param_text: return ""
param_text_ascii = try_coerce_ascii(param_text)
return param_text_ascii if param_text_ascii\
else Charset('utf8').header_encode(param_text)
smtp_server = '<someserver.com>'
smtp_port = 465 # Default port for SSL
sender_email = '<sender_email@some.com>'
sender_password = '<PASSWORD>'
receiver_emails = ['<receiver_email_1@some.com>', '<receiver_email_2@some.com>']
subject = 'Test message'
message = """\
Hello! This is a test message with attachments.
This message is sent from Python."""
files = ['<path1>/файл1.pdf', '<path2>/файл2.png']
# Create a secure SSL context
context = ssl.create_default_context()
msg = MIMEMultipart()
msg['From'] = sender_email
msg['To'] = COMMASPACE.join(receiver_emails)
msg['Date'] = formatdate(localtime=True)
msg['Subject'] = subject
msg.attach(MIMEText(message))
for f in files:
mimetype, _ = guess_type(f)
mimetype = mimetype.split('/', 1)
with open(f, "rb") as fil:
part = MIMEBase(mimetype[0], mimetype[1])
part.set_payload(fil.read())
encode_base64(part)
filename_rfc2047 = encode_header_param(basename(f))
# The default RFC 2231 encoding of Message.add_header() works in Thunderbird but not GMail
# so we fix it by using RFC 2047 encoding for the filename instead.
part.set_param('name', filename_rfc2047)
part.add_header('Content-Disposition', 'attachment', filename=filename_rfc2047)
msg.attach(part)
with smtplib.SMTP_SSL(smtp_server, smtp_port, context=context) as server:
server.login(sender_email, sender_password)
server.sendmail(sender_email, receiver_emails, msg.as_string())
Gmail版本,使用Python 3.6(请注意,您需要更改Gmail设置,以便能够通过smtp从它发送电子邮件:
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.application import MIMEApplication
from os.path import basename
def send_mail(send_from: str, subject: str, text: str,
send_to: list, files= None):
send_to= default_address if not send_to else send_to
msg = MIMEMultipart()
msg['From'] = send_from
msg['To'] = ', '.join(send_to)
msg['Subject'] = subject
msg.attach(MIMEText(text))
for f in files or []:
with open(f, "rb") as fil:
ext = f.split('.')[-1:]
attachedfile = MIMEApplication(fil.read(), _subtype = ext)
attachedfile.add_header(
'content-disposition', 'attachment', filename=basename(f) )
msg.attach(attachedfile)
smtp = smtplib.SMTP(host="smtp.gmail.com", port= 587)
smtp.starttls()
smtp.login(username,password)
smtp.sendmail(send_from, send_to, msg.as_string())
smtp.close()
用法:
username = 'my-address@gmail.com'
password = 'top-secret'
default_address = ['my-address2@gmail.com']
send_mail(send_from= username,
subject="test",
text="text",
send_to= None,
files= # pass a list with the full filepaths here...
)
要与任何其他电子邮件提供商一起使用,只需更改smtp配置。
from email.mime.multipart import MIMEMultipart
from email.mime.image import MIMEImage
from email.mime.text import MIMEText
import smtplib
msg = MIMEMultipart()
password = "password"
msg['From'] = "from_address"
msg['To'] = "to_address"
msg['Subject'] = "Attached Photo"
msg.attach(MIMEImage(file("abc.jpg").read()))
file = "file path"
fp = open(file, 'rb')
img = MIMEImage(fp.read())
fp.close()
msg.attach(img)
server = smtplib.SMTP('smtp.gmail.com: 587')
server.starttls()
server.login(msg['From'], password)
server.sendmail(msg['From'], msg['To'], msg.as_string())
server.quit()
因为这里有很多关于Python 3的答案,但没有一个说明如何使用Python 3.6中经过彻底检查的电子邮件库,这里是从当前电子邮件示例文档中快速复制+粘贴。 (我略去了一些内容,以去除猜测正确MIME类型之类的多余内容。)
以Python >3.5为目标的现代代码不应该再使用email.message.Message API(包括各种MIMEText、MIMEMultipart、MIMEBase等类)或更老的mimetypes。
from email.message import EmailMessage
import smtplib
msg = EmailMessage()
msg["Subject"] = "Our family reunion"
msg["From"] = "me <sender@example.org>"
msg["To"] = "recipient <victim@example.net>"
# definitely don't mess with the .preamble
msg.set_content("Hello, victim! Look at these pictures")
with open("path/to/attachment.png", "rb") as fp:
msg.add_attachment(
fp.read(), maintype="image", subtype="png")
# Notice how smtplib now includes a send_message() method
with smtplib.SMTP("localhost") as s:
s.send_message(msg)
The modern email.message.EmailMessage API is now quite a bit more versatile and logical than the older version of the library. There are still a few kinks around the presentation in the documentation (it's not obvious how to change the Content-Disposition: of an attachment, for example; and the discussion of the policy module is probably too obscure for most newcomers) and fundamentally, you still need to have some sort of idea of what the MIME structure should look like (though the library now finally takes care of a lot of the nitty-gritty around that). Perhaps see What are the "parts" in a multipart email? for a brief introduction.
Using localhost as your SMTP server obviously only works if you actually have an SMTP server running on your local computer. Properly getting email off your system is a fairly complex separate question. For simple requirements, probably use your existing email account and your provider's email server (search for examples of using port 587 with Google, Yahoo, or whatever you have - what exactly works depends somewhat on the provider; some will only support port 465, or legacy port 25 which is however now by and large impossible to use on public-facing servers because of spam filtering).
推荐文章
- Visual Studio Code:如何调试Python脚本的参数
- 使用元组/列表等等。从输入vs直接引用类型如list/tuple/etc
- 结合conda环境。Yml和PIP requirements.txt
- 将命名元组转换为字典
- 如何使x轴和y轴的刻度相等呢?
- Numpy在这里函数多个条件
- 在Python中,使用argparse只允许正整数
- 如何从终端机发送电子邮件?
- 如何排序mongodb与pymongo
- 不可变与可变类型
- 列表是线程安全的吗?
- 操作系统。makdirs在我的路径上不理解“~”
- 如何在Django模板中获得我的网站的域名?
- 在django Forms中定义css类
- 如何在Python中scp ?