≡ Menu
Deep in the Code

Sending an Email with Attachments using Python

Update: I have improved the code listed below by adding functionality that moves and renames the files, as well as improving some of the other syntax.  See revised code here.

Have you ever needed to create a job that would email multiple files on a schedule?  That was my problem.  I decided to use the Python scripting language for this one, specifically Python 2.7.3, which is the most recent production version of Python 2.

For this task, I needed to be able to attach at least two files to the message, and I didn’t know what the names of the files would be.  By combining a few scripts that I found and adding the looping functionality, I was able to make it such that any files dropped into the specified location would be picked up.  I will likely be modifying this script further to either move or delete the files once the email is sent.  The email system is Microsoft Exchange, currently configured not to require a username, password, or TLS.


# Adapted from https://gist.github.com/4009671 by David Young
# added directory searching functionality to add all files in folder
# disabled username / password logon for use in our Exchange environment
######### Setup your stuff here #######################################

path='C:/Python27/tmp' # location of files - no folders in this one, only files

host = 'smtp.whatever.com' # specify port, if required, using a colon and port number following the hostname

fromaddr = 'from@whatever.com' # must be a vaild 'from' address in your environment
toaddr = 'to@whatever.com'
replyto = fromaddr # unless you want a different reply-to

# username = 'username' # not used in our Exchange environment
# password = 'password' # not used in our Exchange environment

msgsubject = 'This is the email subject'

htmlmsgtext = """<h2>Email body here</h2>""" # text with appropriate HTML tags

######### In normal use nothing changes below this line ###############

import smtplib, os, sys, shutil
from email.MIMEMultipart import MIMEMultipart
from email.MIMEBase import MIMEBase
from email.MIMEText import MIMEText
from email import Encoders
from HTMLParser import HTMLParser

# A snippet - class to strip HTML tags for the text version of the email

class MLStripper(HTMLParser):
 def __init__(self):
 self.reset()
 self.fed = []
 def handle_data(self, d):
 self.fed.append(d)
 def get_data(self):
 return ''.join(self.fed)

def strip_tags(html):
 s = MLStripper()
 s.feed(html)
 return s.get_data()

########################################################################
try:
 # Make text version from HTML - First convert tags that produce a line break to carriage returns
 msgtext = htmlmsgtext.replace('</br>',"r").replace('<br />',"r").replace('</p>',"r")
 # Then strip all the other tags out
 msgtext = strip_tags(msgtext)

# necessary mimey stuff
 msg = MIMEMultipart()
 msg.preamble = 'This is a multi-part message in MIME format.n'
 msg.epilogue = ''

body = MIMEMultipart('alternative')
 body.attach(MIMEText(msgtext))
 body.attach(MIMEText(htmlmsgtext, 'html'))
 msg.attach(body)
 attachments = os.listdir(path)

if 'attachments' in globals() and len('attachments') > 0: # are there attachments?
 for filename in attachments:
 f = path + '/' + filename
 part = MIMEBase('application', "octet-stream")
 part.set_payload( open(f,"rb").read() )
 Encoders.encode_base64(part)
 part.add_header('Content-Disposition', 'attachment; filename="%s"' % os.path.basename(f))
 msg.attach(part)

msg.add_header('From', fromaddr)
 msg.add_header('To', toaddr)
 msg.add_header('Subject', msgsubject)
 msg.add_header('Reply-To', replyto)

# The actual email sendy bits
 server = smtplib.SMTP(host)
 server.set_debuglevel(False) # set to True for verbose output

 # Comment this and uncomment the try/except block if TLS or user/pass is required.
 server.sendmail(msg['From'], [msg['To']], msg.as_string())
 print 'Email sent'
 server.quit() # bye bye

 # try:
 # # If TLS is used
 # server.starttls()
 # server.login(username,password)
 # server.sendmail(msg['From'], [msg['To']], msg.as_string())
 # print 'Email sent'
 # server.quit() # bye bye
 # except:
 # # if tls is set for non-tls servers you would have raised an exception, so....
 # server.login(username,password)
 # server.sendmail(msg['From'], [msg['To']], msg.as_string())
 # print 'Email sent'
 # server.quit() # bye bye

 # if 'attachments' in globals() and len('attachments') > 0: # are there attachments?
 # for filename in attachments:
 # src = path + '/' + filename
 # dst = path + '/archive/' + filename
 # shutil.copy(src,dst)
except:
 print "Email NOT sent to %s successfully. ERR: %s %s %s " % (str(toaddr), str(sys.exc_info()[0]), str(sys.exc_info()[1]), str(sys.exc_info()[2]) )
 #just in case

This program can be run ad hoc, using Task Scheduler on Windows, cron on Linux / UNIX, or with whatever scheduling tool your environment uses.