2009-12-27 17 views
19

Voglio scrivere un programma che manda email usando Python smtplib. Ho cercato attraverso il documento e le RFC, ma non ho trovato nulla relativo agli allegati. Quindi, sono sicuro che ci sia un concetto di livello superiore che mi sto perdendo. Qualcuno può indovinarmi su come funzionano gli allegati in SMTP?Come si inviano allegati tramite SMTP?

+3

Giusto per essere chiari, non c'è nulla in SMTP per gestire questa situazione, è interamente gestita da strutturare il documento viene inviato come documento MIME. L'articolo su MIME su wikipedia sembra coprire abbastanza bene la base. – jcoder

+3

Includere un collegamento direttamente nella sezione "esempi email" dei documenti Python renderebbe qualsiasi risposta completa: http://docs.python.org/library/email-examples.html –

risposta

10

Quello che si desidera controllare è il modulo email. Ti consente di creare i messaggi conformi a MIME che poi invii con smtplib.

+1

Grazie per la modifica, eri un po 'più veloce di me ;-) –

3

Beh, gli allegati non sono trattati in alcun modo speciale, sono "solo" foglie dell'albero Message-oggetto. È possibile trovare le risposte a qualsiasi domanda riguardante i mesasges conformi a MIME nella sezione this della documentazione sul pacchetto python email.

In generale, qualsiasi tipo di allegato (leggi: dati binari non elaborati) può essere rappresentato utilizzando base64 (o simile) Content-Transfer-Encoding.

18

Ecco un esempio che ho ritagliato da un'applicazione di lavoro che abbiamo creato. Crea un'e-mail HTML con un allegato Excel.

import smtplib,email,email.encoders,email.mime.text,email.mime.base 

    smtpserver = 'localhost' 
    to = ['[email protected]'] 
    fromAddr = '[email protected]' 
    subject = "my subject" 

    # create html email 
    html = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" ' 
    html +='"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml">' 
    html +='<body style="font-size:12px;font-family:Verdana"><p>...</p>' 
    html += "</body></html>" 
    emailMsg = email.MIMEMultipart.MIMEMultipart('alternative') 
    emailMsg['Subject'] = subject 
    emailMsg['From'] = fromAddr 
    emailMsg['To'] = ', '.join(to) 
    emailMsg['Cc'] = ", ".join(cc) 
    emailMsg.attach(email.mime.text.MIMEText(html,'html')) 

    # now attach the file 
    fileMsg = email.mime.base.MIMEBase('application','vnd.ms-excel') 
    fileMsg.set_payload(file('exelFile.xls').read()) 
    email.encoders.encode_base64(fileMsg) 
    fileMsg.add_header('Content-Disposition','attachment;filename=anExcelFile.xls') 
    emailMsg.attach(fileMsg) 

    # send email 
    server = smtplib.SMTP(smtpserver) 
    server.sendmail(fromAddr,to,emailMsg.as_string()) 
    server.quit() 
+6

Il sottotipo multipart deve essere "misto" anziché "alternativo" altrimenti non vedrai il file allegato in alcuni client di posta elettronica. – rhyek

3

Ecco come inviare e-mail con allegati di file ZIP e utf-8 soggetti codificato + corpo.

Non era semplice per calcolare questo fuori, per mancanza di documentazione e campioni per questo caso particolare.

caratteri non ASCII in replyto deve essere codificato con, per esempio, ISO-8859-1. Probabilmente esiste una funzione che può farlo.

Suggerimento:
Invia anche tu una e-mail, salvarla ed esaminare il contenuto di capire come fare la stessa cosa in Python.

Ecco il codice, per Python 3:

#!/usr/bin/env python3 
# -*- coding: utf-8 -*- 
# vim:set ts=4 sw=4 et: 

from os.path import basename 
from smtplib import SMTP 
from email.mime.text import MIMEText 
from email.mime.base import MIMEBase 
from email.mime.multipart import MIMEMultipart 
from email.header import Header 
from email.utils import parseaddr, formataddr 
from base64 import encodebytes 

def send_email(recipients=["[email protected]"], 
     subject="Test subject æøå", 
     body="Test body æøå", 
     zipfiles=[], 
     server="smtp.somewhere.xyz", 
     username="bob", 
     password="password123", 
     sender="Bob <[email protected]>", 
     replyto="=?ISO-8859-1?Q?M=F8=F8=F8?= <[email protected]>"): #: bool 
    """Sends an e-mail""" 
    to = ",".join(recipients) 
    charset = "utf-8" 
    # Testing if body can be encoded with the charset 
    try: 
     body.encode(charset) 
    except UnicodeEncodeError: 
     print("Could not encode " + body + " as " + charset + ".") 
     return False 

    # Split real name (which is optional) and email address parts 
    sender_name, sender_addr = parseaddr(sender) 
    replyto_name, replyto_addr = parseaddr(replyto) 

    sender_name = str(Header(sender_name, charset)) 
    replyto_name = str(Header(replyto_name, charset)) 

    # Create the message ('plain' stands for Content-Type: text/plain) 
    try: 
     msgtext = MIMEText(body.encode(charset), 'plain', charset) 
    except TypeError: 
     print("MIMEText fail") 
     return False 

    msg = MIMEMultipart() 

    msg['From'] = formataddr((sender_name, sender_addr)) 
    msg['To'] = to #formataddr((recipient_name, recipient_addr)) 
    msg['Reply-to'] = formataddr((replyto_name, replyto_addr)) 
    msg['Subject'] = Header(subject, charset) 

    msg.attach(msgtext) 

    for zipfile in zipfiles: 
     part = MIMEBase('application', "zip") 
     b = open(zipfile, "rb").read() 
     # Convert from bytes to a base64-encoded ascii string 
     bs = encodebytes(b).decode() 
     # Add the ascii-string to the payload 
     part.set_payload(bs) 
     # Tell the e-mail client that we're using base 64 
     part.add_header('Content-Transfer-Encoding', 'base64') 
     part.add_header('Content-Disposition', 'attachment; filename="%s"' % 
         os.path.basename(zipfile)) 
     msg.attach(part) 

    s = SMTP() 
    try: 
     s.connect(server) 
    except: 
     print("Could not connect to smtp server: " + server) 
     return False 

    if username: 
     s.login(username, password) 
    print("Sending the e-mail") 
    s.sendmail(sender, recipients, msg.as_string()) 
    s.quit() 
    return True 

def main(): 
    send_email() 

if __name__ == "__main__": 
    main() 
28

Ecco un esempio di un messaggio con un allegato PDF, un testo "corpo" e l'invio tramite Gmail.

# Import smtplib for the actual sending function 
import smtplib 

# For guessing MIME type 
import mimetypes 

# Import the email modules we'll need 
import email 
import email.mime.application 

# Create a text/plain message 
msg = email.mime.Multipart.MIMEMultipart() 
msg['Subject'] = 'Greetings' 
msg['From'] = '[email protected]' 
msg['To'] = '[email protected]' 

# The main body is just another attachment 
body = email.mime.Text.MIMEText("""Hello, how are you? I am fine. 
This is a rather nice letter, don't you think?""") 
msg.attach(body) 

# PDF attachment 
filename='simple-table.pdf' 
fp=open(filename,'rb') 
att = email.mime.application.MIMEApplication(fp.read(),_subtype="pdf") 
fp.close() 
att.add_header('Content-Disposition','attachment',filename=filename) 
msg.attach(att) 

# send via Gmail server 
# NOTE: my ISP, Centurylink, seems to be automatically rewriting 
# port 25 packets to be port 587 and it is trashing port 587 packets. 
# So, I use the default port 25, but I authenticate. 
s = smtplib.SMTP('smtp.gmail.com') 
s.starttls() 
s.login('[email protected]','xyzpassword') 
s.sendmail('[email protected]',['[email protected]'], msg.as_string()) 
s.quit() 
+2

Questo ha risolto il mio problema per l'invio tramite e-mail di file excel, il che è stato fantastico perché mi ha tenuto fuori da os.system chiamando Ruby! Grazie Kevin! – Benjooster

+0

Questa soluzione ha funzionato anche per me, dopo aver creato un file .xls usando il modulo xlwt Python. Invece di inviare Gmail ho utilizzato il server di posta della mia azienda. Grazie, e +1 –

+0

questo metodo ha funzionato davvero ed è molto più pulito! –

1
# -*- coding: utf-8 -*- 

""" 
Mail sender 
""" 

from email.mime.multipart import MIMEMultipart 
from email.mime.text import MIMEText 

import smtplib 
import pystache 
import codecs 
import time 

import sys 
reload(sys) 
sys.setdefaultencoding('utf-8') 


HOST = 'smtp.exmail.qq.com' 
PORT = 587 
USER = '[email protected]' 
PASS = 'yourpass' 
FROM = '[email protected]' 

SUBJECT = 'subject' 
HTML_NAME = 'tpl.html' 
CSV_NAME = 'list.txt' 
FAILED_LIST = [] 


def send(mail_receiver, mail_to): 
    # text = mail_text 
    html = render(mail_receiver) 

    # msg = MIMEMultipart('alternative') 
    msg = MIMEMultipart('mixed') 
    msg['From'] = FROM 
    msg['To'] = mail_to.encode() 
    msg['Subject'] = SUBJECT.encode() 

    # msg.attach(MIMEText(text, 'plain', 'utf-8')) 
    msg.attach(MIMEText(html, 'html', 'utf-8')) 

    try: 
     _sender = smtplib.SMTP(
      HOST, 
      PORT 
     ) 
     _sender.starttls() 
     _sender.login(USER, PASS) 
     _sender.sendmail(FROM, mail_to, msg.as_string()) 
     _sender.quit() 
     print "Success" 
    except smtplib.SMTPException, e: 
     print e 
     FAILED_LIST.append(mail_receiver + ',' + mail_to) 


def render(name): 
    _tpl = codecs.open(
     './html/' + HTML_NAME, 
     'r', 
     'utf-8' 
    ) 
    _html_string = _tpl.read() 
    return pystache.render(_html_string, { 
     'receiver': name 
    }) 


def main(): 
    ls = open('./csv/' + CSV_NAME, 'r') 
    mail_list = ls.read().split('\r') 

    for _receiver in mail_list: 
     _tmp = _receiver.split(',') 
     print 'Mail: ' + _tmp[0] + ',' + _tmp[1] 
     time.sleep(20) 
     send(_tmp[0], _tmp[1]) 

    print FAILED_LIST 


main() 
Problemi correlati