我有问题理解如何使用Python电子邮件附件。我已经成功地通过smtplib电子邮件发送了简单的消息。有人能解释一下如何在电子邮件中发送附件吗?我知道网上还有其他的帖子,但作为一个Python初学者,我发现它们很难理解。


当前回答

python 3的另一种方法(如果有人正在搜索):

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 = "sender mail address"
toaddr = "receiver mail address"

msg = MIMEMultipart()

msg['From'] = fromaddr
msg['To'] = toaddr
msg['Subject'] = "SUBJECT OF THE EMAIL"

body = "TEXT YOU WANT TO SEND"

msg.attach(MIMEText(body, 'plain'))

filename = "fileName"
attachment = open("path of file", "rb")

part = MIMEBase('application', 'octet-stream')
part.set_payload((attachment).read())
encoders.encode_base64(part)
part.add_header('Content-Disposition', "attachment; filename= %s" % filename)

msg.attach(part)

server = smtplib.SMTP('smtp.gmail.com', 587)
server.starttls()
server.login(fromaddr, "sender mail password")
text = msg.as_string()
server.sendmail(fromaddr, toaddr, text)
server.quit()

确保在你的Gmail帐户上允许“不太安全的应用程序”

其他回答

下面是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()
from email.MIMEMultipart import MIMEMultipart
from email.MIMEText import MIMEText
from email.MIMEImage import MIMEImage
import smtplib

msg = MIMEMultipart()
msg.attach(MIMEText(file("text.txt").read()))
msg.attach(MIMEImage(file("image.png").read()))

# to send
mailer = smtplib.SMTP()
mailer.connect()
mailer.sendmail(from_, to, msg.as_string())
mailer.close()

从这里改编。

下面是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()

你可以在这里找到更多的例子。

你也可以在你的电子邮件中指定你想要的附件类型,例如我使用pdf:

def send_email_pdf_figs(path_to_pdf, subject, message, destination, password_path=None):
    ## credits: http://linuxcursor.com/python-programming/06-how-to-send-pdf-ppt-attachment-with-html-body-in-python-script
    from socket import gethostname
    #import email
    from email.mime.application import MIMEApplication
    from email.mime.multipart import MIMEMultipart
    from email.mime.text import MIMEText
    import smtplib
    import json

    server = smtplib.SMTP('smtp.gmail.com', 587)
    server.starttls()
    with open(password_path) as f:
        config = json.load(f)
        server.login('me@gmail.com', config['password'])
        # Craft message (obj)
        msg = MIMEMultipart()

        message = f'{message}\nSend from Hostname: {gethostname()}'
        msg['Subject'] = subject
        msg['From'] = 'me@gmail.com'
        msg['To'] = destination
        # Insert the text to the msg going by e-mail
        msg.attach(MIMEText(message, "plain"))
        # Attach the pdf to the msg going by e-mail
        with open(path_to_pdf, "rb") as f:
            #attach = email.mime.application.MIMEApplication(f.read(),_subtype="pdf")
            attach = MIMEApplication(f.read(),_subtype="pdf")
        attach.add_header('Content-Disposition','attachment',filename=str(path_to_pdf))
        msg.attach(attach)
        # send msg
        server.send_message(msg)

灵感/信用:http://linuxcursor.com/python-programming/06-how-to-send-pdf-ppt- attachment-html -body-in-python脚本

在让我的脚本发送通用附件时遇到了一点麻烦,但在做了一些研究和浏览了这篇文章后,我终于想出了以下几点

# 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)