【问题标题】:How to send email attachments?如何发送电子邮件附件?
【发布时间】:2021-03-06 17:26:15
【问题描述】:

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

【问题讨论】:

  • 这是一个简单的实现,可以附加多个文件,甚至在要嵌入图像的情况下引用它们。 datamakessense.com/…
  • 我发现这个有用的drupal.org/project/mimemail/issues/911612 证明图像附件需要附加到相关类型的子部件。如果您将图像附加到根 MIME 部分,图像可以显示在附加项目列表中,并在 Outlook365 等客户端中预览。
  • @AdrianBR 如果我有一个 PDF 文件格式的图像怎么办。缩放时 PNG 有像素问题,所以 png 对我不利。

标签: python email


【解决方案1】:

我知道这是一个老问题,但我认为必须有比其他示例更简单的方法,因此我创建了一个库,可以干净地解决这个问题而不会污染您的代码库。包含附件非常简单:

from redmail import EmailSender
from pathlib import Path

# Configure an email sender
email = EmailSender(
    host="<SMTP HOST>", port=0,
    user_name="me@example.com", password="<PASSWORD>"
)

# Send an email
email.send(
    sender="me@example.com",
    receivers=["you@example.com"],
    subject="An example email"
    attachments={
        "myfile.txt": Path("path/to/a_file.txt"),
        "myfile.html": "<h1>Content of a HTML attachment</h1>"
    }
)

您也可以直接附加bytes、Pandas DataFrame(根据密钥中的文件扩展名转换为格式)、Matplotlib Figure 或 Pillow Image。该库很可能是电子邮件发件人所需的所有功能(不仅仅是附件)。

安装:

pip install redmail

以任何你喜欢的方式使用它。我还写了大量文档:https://red-mail.readthedocs.io/en/latest/

【讨论】:

    【解决方案2】:

    让我的脚本发送通用附件有点麻烦,但经过一些研究和浏览这篇文章的文章后,我终于想出了以下内容

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

    【讨论】:

      【解决方案3】:

      这里是 Oli 为 python 3 修改的版本

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

      【讨论】:

      • 谢谢,但最好也有基本的:单个附件的语法(使用它的路径)
      • send_to 应该是列表[str]
      • 对我来说最好的答案,但有一个小错误:将import pathlib替换为from pathlib import Path
      • 迄今为止的最佳答案。这应该是问题的解决方案。
      • @RubenFlam-Shepherd 谢谢。我更新了答案并删除了引号
      【解决方案4】:

      试试这个,希望对你有帮助

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

      【讨论】:

        【解决方案5】:

        此处当前给出的答案都不能与 GMail、Outlook 2016 和其他不支持 RFC 2231 的客户端(例如,请参阅 here)的文件名中的非 ASCII 符号一起正常工作。下面的 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())
        

        【讨论】:

        • 我决定使用这个版本。特别是因为文件名,在我的例子中,可以包含非 ASCII 字符。
        【解决方案6】:

        这是我最终使用的代码:

        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 的帖子大​​致相同。

        代码来自 Binary file email attachment problem 帖子。

        【讨论】:

        • 好答案。如果它还包含添加示例正文文本的代码,那就太好了。
        • 请注意,在电子邮件库的现代版本中 - 模块导入是不同的。例如:from email.mime.base import MIMEBase
        【解决方案7】:

        这是另一个:

        import smtplib
        from os.path import basename
        from email.mime.application import MIMEApplication
        from email.mime.multipart import MIMEMultipart
        from email.mime.text import MIMEText
        from email.utils import COMMASPACE, formatdate
        
        
        def send_mail(send_from, send_to, subject, text, files=None,
                      server="127.0.0.1"):
            assert isinstance(send_to, list)
        
            msg = MIMEMultipart()
            msg['From'] = send_from
            msg['To'] = COMMASPACE.join(send_to)
            msg['Date'] = formatdate(localtime=True)
            msg['Subject'] = subject
        
            msg.attach(MIMEText(text))
        
            for f in files or []:
                with open(f, "rb") as fil:
                    part = MIMEApplication(
                        fil.read(),
                        Name=basename(f)
                    )
                # After the file is closed
                part['Content-Disposition'] = 'attachment; filename="%s"' % basename(f)
                msg.attach(part)
        
        
            smtp = smtplib.SMTP(server)
            smtp.sendmail(send_from, send_to, msg.as_string())
            smtp.close()
        

        和第一个例子差不多……不过应该更容易上手。

        【讨论】:

        • @user589983 为什么不像这里的任何其他用户那样建议编辑?我已将对file 的剩余引用更改为f
        • Python3 开发者注意事项:模块“email.Utils”已重命名为“email.utils”
        • 对于 python2.5+,使用 MIMEApplication 更容易 - 将循环的前三行减少到:part = MIMEApplication(open(f, 'rb').read())
        • 发送的电子邮件中未显示主题。仅在将行更改为 msg['Subject']=subject 后才起作用。我使用 python 2.7。
        【解决方案8】:

        您还可以在电子邮件中指定您想要的附件类型,例如我使用 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-with-html-body-in-python-script

        【讨论】:

          【解决方案9】:

          使用我的代码,您可以使用 gmail 发送电子邮件附件,您需要:

          将您的 gmail 地址设置为“您的 SMTP 电子邮件在此处

          在“YOUR SMTP PASSWORD HERE_”处设置您的 gmail 帐户密码

          ___EMAIL TO RECEIVE THE MESSAGE_ 部分中,您需要设置目标电子邮件地址。

          报警通知是主题,

          有人进了房间,附图是尸体

          ["/home/pi/webcam.jpg"] 是图片附件。

          #!/usr/bin/env python
          import smtplib
          from email.MIMEMultipart import MIMEMultipart
          from email.MIMEBase import MIMEBase
          from email.MIMEText import MIMEText
          from email.Utils import COMMASPACE, formatdate
          from email import Encoders
          import os
          
          USERNAME = "___YOUR SMTP EMAIL HERE___"
          PASSWORD = "__YOUR SMTP PASSWORD HERE___"
          
          def sendMail(to, subject, text, files=[]):
              assert type(to)==list
              assert type(files)==list
          
              msg = MIMEMultipart()
              msg['From'] = USERNAME
              msg['To'] = COMMASPACE.join(to)
              msg['Date'] = formatdate(localtime=True)
              msg['Subject'] = subject
          
              msg.attach( MIMEText(text) )
          
              for file in files:
                  part = MIMEBase('application', "octet-stream")
                  part.set_payload( open(file,"rb").read() )
                  Encoders.encode_base64(part)
                  part.add_header('Content-Disposition', 'attachment; filename="%s"'
                                 % os.path.basename(file))
                  msg.attach(part)
          
              server = smtplib.SMTP('smtp.gmail.com:587')
              server.ehlo_or_helo_if_needed()
              server.starttls()
              server.ehlo_or_helo_if_needed()
              server.login(USERNAME,PASSWORD)
              server.sendmail(USERNAME, to, msg.as_string())
              server.quit()
          
          sendMail( ["___EMAIL TO RECEIVE THE MESSAGE__"],
                  "Alarm notification",
                  "Someone has entered the room, picture attached",
                  ["/home/pi/webcam.jpg"] )
          

          【讨论】:

          • 好久不见!很高兴看到您正确地归因于您的代码并将其直接包含在答案中。但是,通常不赞成在多个问题上复制粘贴相同的答案代码。如果他们真的可以用相同的解决方案解决,你应该flag the questions as duplicates代替。
          【解决方案10】:
          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()
          

          【讨论】:

          • 您好,欢迎您,在回答问题时,请务必对您的答案进行解释,以便更好地理解
          【解决方案11】:
          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

          【讨论】:

            【解决方案12】:

            以下是我从 SoccerPlayer 的帖子 Here 中找到的内容和以下链接的组合,该链接使我更容易附加 xlsx 文件。 Found Here

            file = 'File.xlsx'
            username=''
            password=''
            send_from = ''
            send_to = 'recipient1 , recipient2'
            Cc = 'recipient'
            msg = MIMEMultipart()
            msg['From'] = send_from
            msg['To'] = send_to
            msg['Cc'] = Cc
            msg['Date'] = formatdate(localtime = True)
            msg['Subject'] = ''
            server = smtplib.SMTP('smtp.gmail.com')
            port = '587'
            fp = open(file, 'rb')
            part = MIMEBase('application','vnd.ms-excel')
            part.set_payload(fp.read())
            fp.close()
            encoders.encode_base64(part)
            part.add_header('Content-Disposition', 'attachment', filename='Name File Here')
            msg.attach(part)
            smtp = smtplib.SMTP('smtp.gmail.com')
            smtp.ehlo()
            smtp.starttls()
            smtp.login(username,password)
            smtp.sendmail(send_from, send_to.split(',') + msg['Cc'].split(','), msg.as_string())
            smtp.quit()
            

            【讨论】:

              【解决方案13】:

              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 帐户中允许“less secure apps

              【讨论】:

                【解决方案14】:

                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 配置。

                【讨论】:

                  【解决方案15】:

                  其他答案非常好,但我仍然想分享一种不同的方法,以防有人正在寻找替代方案。

                  这里的主要区别在于,使用这种方法,您可以使用 HTML/CSS 来格式化您的邮件,这样您就可以发挥创意并为您的电子邮件添加一些样式。虽然没有强制您使用 HTML,但您仍然可以只使用纯文本。

                  请注意,此功能接受将电子邮件发送给多个收件人,还允许附加多个文件。

                  我只在 Python 2 上尝试过,但我认为它在 3 上也应该可以正常工作:

                  import os.path
                  import smtplib
                  from email.mime.multipart import MIMEMultipart
                  from email.mime.text import MIMEText
                  from email.mime.application import MIMEApplication
                  
                  def send_email(subject, message, from_email, to_email=[], attachment=[]):
                      """
                      :param subject: email subject
                      :param message: Body content of the email (string), can be HTML/CSS or plain text
                      :param from_email: Email address from where the email is sent
                      :param to_email: List of email recipients, example: ["a@a.com", "b@b.com"]
                      :param attachment: List of attachments, exmaple: ["file1.txt", "file2.txt"]
                      """
                      msg = MIMEMultipart()
                      msg['Subject'] = subject
                      msg['From'] = from_email
                      msg['To'] = ", ".join(to_email)
                      msg.attach(MIMEText(message, 'html'))
                  
                      for f in attachment:
                          with open(f, 'rb') as a_file:
                              basename = os.path.basename(f)
                              part = MIMEApplication(a_file.read(), Name=basename)
                  
                          part['Content-Disposition'] = 'attachment; filename="%s"' % basename
                          msg.attach(part)
                  
                      email = smtplib.SMTP('your-smtp-host-name.com')
                      email.sendmail(from_email, to_email, msg.as_string())
                  

                  我希望这会有所帮助! :-)

                  【讨论】:

                    【解决方案16】:

                    我能得到的最简单的代码是:

                    #for attachment email
                    from django.core.mail import EmailMessage
                    
                        def attachment_email(request):
                                email = EmailMessage(
                                'Hello', #subject
                                'Body goes here', #body
                                'MyEmail@MyEmail.com', #from
                                ['SendTo@SendTo.com'], #to
                                ['bcc@example.com'], #bcc
                                reply_to=['other@example.com'],
                                headers={'Message-ID': 'foo'},
                                )
                    
                                email.attach_file('/my/path/file')
                                email.send()
                    

                    基于官方Django documentation

                    【讨论】:

                    • 在您的情况下,您必须安装 django 才能发送电子邮件......它无法正确回复问题
                    • @comte '因为 python 只用于 Django,对吧?
                    • @Auspex 这就是我的观点 ;-) 这就像安装 LibreOffice 来编辑配置文件......
                    • 我觉得这很有帮助且内容丰富。只导入了一个模块,与其他人跳过的 MIME 箍相比,它的使用非常简单和优雅。相比之下,在您的示例中,LibreOffice 比记事本更难使用。
                    • 这太愚蠢了,这不是本地的做法。
                    【解决方案17】:
                    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()
                    

                    改编自here

                    【讨论】:

                    • 不是我想要的。该文件作为电子邮件正文发送。第 6 行和第 7 行也缺少括号。我觉得我们越来越近了
                    • 电子邮件是纯文本,这就是smtplib 所支持的。要发送附件,请将它们编码为 MIME 消息并以纯文本电子邮件的形式发送。不过有一个新的 python 电子邮件模块:docs.python.org/library/email.mime.html
                    • @katrienlalex 一个可行的例子对我的理解大有帮助
                    • 你确定上面的例子不起作用吗?我手边没有 SMTP 服务器,但我查看了msg.as_string(),它确实看起来像是 MIME 多部分电子邮件的正文。维基百科解释 MIME:en.wikipedia.org/wiki/MIME
                    • Line 6, in &lt;module&gt; msg.attach(MIMEText(file("text.txt").read())) NameError: name 'file' is not defined
                    猜你喜欢
                    • 1970-01-01
                    • 2016-11-30
                    • 2012-06-07
                    相关资源
                    最近更新 更多