我有问题理解如何使用Python电子邮件附件。我已经成功地通过smtplib电子邮件发送了简单的消息。有人能解释一下如何在电子邮件中发送附件吗?我知道网上还有其他的帖子,但作为一个Python初学者,我发现它们很难理解。
当前回答
下面是Oli为python3编写的修改版本
import smtplib
from pathlib import Path
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email.mime.text import MIMEText
from email.utils import COMMASPACE, formatdate
from email import encoders
def send_mail(send_from, send_to, subject, message, files=[],
server="localhost", port=587, username='', password='',
use_tls=True):
"""Compose and send email with provided info and attachments.
Args:
send_from (str): from name
send_to (list[str]): to name(s)
subject (str): message title
message (str): message body
files (list[str]): list of file paths to be attached to email
server (str): mail server host name
port (int): port number
username (str): server auth username
password (str): server auth password
use_tls (bool): use TLS mode
"""
msg = MIMEMultipart()
msg['From'] = send_from
msg['To'] = COMMASPACE.join(send_to)
msg['Date'] = formatdate(localtime=True)
msg['Subject'] = subject
msg.attach(MIMEText(message))
for path in files:
part = MIMEBase('application', "octet-stream")
with open(path, 'rb') as file:
part.set_payload(file.read())
encoders.encode_base64(part)
part.add_header('Content-Disposition',
'attachment; filename={}'.format(Path(path).name))
msg.attach(part)
smtp = smtplib.SMTP(server, port)
if use_tls:
smtp.starttls()
smtp.login(username, password)
smtp.sendmail(send_from, send_to, msg.as_string())
smtp.quit()
其他回答
在让我的脚本发送通用附件时遇到了一点麻烦,但在做了一些研究和浏览了这篇文章后,我终于想出了以下几点
# to query:
import sys
import ast
from datetime import datetime
import smtplib
import mimetypes
from email.mime.application import MIMEApplication
from email.mime.multipart import MIMEMultipart
from email import encoders
from email.message import Message
from email.mime.audio import MIMEAudio
from email.mime.base import MIMEBase
from email.mime.image import MIMEImage
from email.mime.text import MIMEText
from dotenv import load_dotenv, dotenv_values
load_dotenv() # load environment variables from .env
'''
sample .env file
# .env file
SECRET_KEY="gnhfpsjxxxxxxxx"
DOMAIN="GMAIL"
TOP_LEVEL_DOMAIN="COM"
EMAIL="CHESERExxxxxx@${DOMAIN}.${TOP_LEVEL_DOMAIN}"
TO_ADDRESS = ("cheseremxxxxx@gmail.com","cheserek@gmail.com")#didn't use this in the code but you can load recipients from here
'''
import smtplib
tls_port = 587
ssl_port = 465
smtp_server_domain_names = {'GMAIL': ('smtp.gmail.com', tls_port, ssl_port),
'OUTLOOK': ('smtp-mail.outlook.com', tls_port, ssl_port),
'YAHOO': ('smtp.mail.yahoo.com', tls_port, ssl_port),
'AT&T': ('smtp.mail.att.net', tls_port, ssl_port),
}
# todo: Ability to choose mail server provider
# auto read in from the dictionary the respective mail server address and the tls and ssl ports
class Bimail:
def __init__(self, subject, recipients):
self.subject = subject
self.recipients = recipients
self.htmlbody = ''
self.mail_username = 'will be loaded from .env file'
self.mail_password = 'loaded from .env file as well'
self.attachments = []
# Creating an smtp object
# todo: if gmail passed in use gmail's dictionary values
def setup_mail_client(self, domain_key_to_use="GMAIL",
email_servers_domains_dict=smtp_server_domain_names):
"""
:param report_pdf:
:type to_address: str
"""
smtpObj = None
encryption_status = True
config = dotenv_values(".env")
# check if the domain_key exists from within the available email-servers-domains dict file passed in
# else throw an error
# read environment file to get the Domain to be used
if f"{domain_key_to_use}" in email_servers_domains_dict.keys():
# if the key is found do the following
# 1.extract the domain,tls,ssl ports from email_servers dict for use in program
try:
values_tuple = email_servers_domains_dict.get(f"{domain_key_to_use}")
ssl_port = values_tuple[2]
tls_port = values_tuple[1]
smtp_server = values_tuple[0]
smtpObj = smtplib.SMTP(smtp_server, tls_port)
print(f"Success connect with tls on {tls_port}")
print('Awaiting for connection encryption via startttls()')
encryption_status = False
except:
print(f"Failed connection via tls on port {tls_port}")
try:
smtpObj = smtplib.SMTP_SSL(smtp_server, ssl_port)
print(f"Success connect with ssl on {ssl_port}")
encryption_status = True
except:
print(f"Failed connection via ssl on port {ssl_port}")
finally:
print("Within Finally block")
if not smtpObj:
print("Failed!!! no Internet connection")
else:
# if connection channel is unencrypted via the use of tls encrypt it
if not encryption_status:
status = smtpObj.starttls()
if status[0] == 220:
print("Successfully Encrypted tls channel")
print("Successfully Connected!!!! Requesting Login")
# Loading .env file values to config variable
#load Login Creds from ENV File
self.mail_username = f'{config.get("EMAIL")}'
self.mail_password = f'{cofig.get("SECRET_KEY")}'
status = smtpObj.login(self.mail_usernam,self.mail_password)
if status[0] == 235:
print("Successfully Authenticated User to xxx account")
success = self.send(smtpObj, f'{config.get("EMAIL")}')
if not bool(success):
print(f"Success in Sending Mail to {success}")
print("Disconnecting from Server INstance")
quit_result = smtpObj.quit()
else:
print(f"Failed to Post {success}!!!")
print(f"Quiting anyway !!!")
quit_result = smtpObj.quit()
else:
print("Application Specific Password is Required")
else:
print("World")
def send(self,smtpObj,from_address):
msg = MIMEMultipart('alternative')
msg['From'] = from_address
msg['Subject'] = self.subject
msg['To'] = ", ".join(self.recipients) # to must be array of the form ['mailsender135@gmail.com']
msg.preamble = "preamble goes here"
# check if there are attachments if yes, add them
if self.attachments:
self.attach(msg)
# add html body after attachments
msg.attach(MIMEText(self.htmlbody, 'html'))
# send
print(f"Attempting Email send to the following addresses {self.recipients}")
result = smtpObj.sendmail(from_address, self.recipients,msg.as_string())
return result
def htmladd(self, html):
self.htmlbody = self.htmlbody + '<p></p>' + html
def attach(self, msg):
for f in self.attachments:
ctype, encoding = mimetypes.guess_type(f)
if ctype is None or encoding is not None:
ctype = "application/octet-stream"
maintype, subtype = ctype.split("/", 1)
if maintype == "text":
fp = open(f)
# Note: we should handle calculating the charset
attachment = MIMEText(fp.read(), _subtype=subtype)
fp.close()
elif maintype == "image":
fp = open(f, "rb")
attachment = MIMEImage(fp.read(), _subtype=subtype)
fp.close()
elif maintype == "ppt":
fp = open(f, "rb")
attachment = MIMEApplication(fp.read(), _subtype=subtype)
fp.close()
elif maintype == "audio":
fp = open(f, "rb")
attachment = MIMEAudio(fp.read(), _subtype=subtype)
fp.close()
else:
fp = open(f, "rb")
attachment = MIMEBase(maintype, subtype)
attachment.set_payload(fp.read())
fp.close()
encoders.encode_base64(attachment)
attachment.add_header("Content-Disposition", "attachment", filename=f)
attachment.add_header('Content-ID', '<{}>'.format(f))
msg.attach(attachment)
def addattach(self, files):
self.attachments = self.attachments + files
# example below
if __name__ == '__main__':
# subject and recipients
mymail = Bimail('Sales email ' + datetime.now().strftime('%Y/%m/%d'),
['cheseremxx@gmail.com', 'tkemboxxx@gmail.com'])
# start html body. Here we add a greeting.
mymail.htmladd('Good morning, find the daily summary below.')
# Further things added to body are separated by a paragraph, so you do not need to worry about newlines for new sentences
# here we add a line of text and an html table previously stored in the variable
mymail.htmladd('Daily sales')
mymail.addattach(['htmlsalestable.xlsx'])
# another table name + table
mymail.htmladd('Daily bestsellers')
mymail.addattach(['htmlbestsellertable.xlsx'])
# add image chart title
mymail.htmladd('Weekly sales chart')
# attach image chart
mymail.addattach(['saleschartweekly.png'])
# refer to image chart in html
mymail.htmladd('<img src="cid:saleschartweekly.png"/>')
# attach another file
mymail.addattach(['MailSend.py'])
# send!
mymail.setup_mail_client( domain_key_to_use="GMAIL",email_servers_domains_dict=smtp_server_domain_names)
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
import smtplib
import mimetypes
import email.mime.application
smtp_ssl_host = 'smtp.gmail.com' # smtp.mail.yahoo.com
smtp_ssl_port = 465
s = smtplib.SMTP_SSL(smtp_ssl_host, smtp_ssl_port)
s.login(email_user, email_pass)
msg = MIMEMultipart()
msg['Subject'] = 'I have a picture'
msg['From'] = email_user
msg['To'] = email_user
txt = MIMEText('I just bought a new camera.')
msg.attach(txt)
filename = 'introduction-to-algorithms-3rd-edition-sep-2010.pdf' #path to file
fo=open(filename,'rb')
attach = email.mime.application.MIMEApplication(fo.read(),_subtype="pdf")
fo.close()
attach.add_header('Content-Disposition','attachment',filename=filename)
msg.attach(attach)
s.send_message(msg)
s.quit()
为了解释,你可以使用这个链接,它解释得很好 https://medium.com/@sdoshi579/to-send-an-email-along-with-attachment-using-smtp-7852e77623
这是我最终使用的代码:
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的帖子大致相同。
代码基于二进制文件电子邮件附件问题的帖子。
试试这个我希望这可能会有帮助
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
from email import encoders
fromaddr = "youremailhere"
toaddr = input("Enter The Email Adress You want to send to: ")
# instance of MIMEMultipart
msg = MIMEMultipart()
# storing the senders email address
msg['From'] = fromaddr
# storing the receivers email address
msg['To'] = toaddr
# storing the subject
msg['Subject'] = input("What is the Subject:\t")
# string to store the body of the mail
body = input("What is the body:\t")
# attach the body with the msg instance
msg.attach(MIMEText(body, 'plain'))
# open the file to be sent
filename = input("filename:")
attachment = open(filename, "rb")
# instance of MIMEBase and named as p
p = MIMEBase('application', 'octet-stream')
# To change the payload into encoded form
p.set_payload((attachment).read())
# encode into base64
encoders.encode_base64(p)
p.add_header('Content-Disposition', "attachment; filename= %s" % filename)
# attach the instance 'p' to instance 'msg'
msg.attach(p)
# creates SMTP session
s = smtplib.SMTP('smtp.gmail.com', 587)
# start TLS for security
s.starttls()
# Authentication
s.login(fromaddr, "yourpaswordhere)
# Converts the Multipart msg into a string
text = msg.as_string()
# sending the mail
s.sendmail(fromaddr, toaddr, text)
# terminating the session
s.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())
推荐文章
- 如何禁用标准错误流的日志记录?
- 用Matplotlib在Python中绘制时间
- 类中的Python装饰器
- 在Python中锁定文件
- 得到熊猫栏目的总数
- 从pandas DataFrame中删除名称包含特定字符串的列
- Mock vs MagicMock
- 如何阅读一个。xlsx文件使用熊猫库在iPython?
- 如何访问熊猫组由数据帧按键
- Pandas和NumPy+SciPy在Python中的区别是什么?
- 将列表转换为集合会改变元素的顺序
- 如何在matplotlib更新一个情节
- TypeError: ` NoneType `对象在Python中不可迭代
- 如何在Vim注释掉一个Python代码块
- python标准库中的装饰符(特别是@deprecated)