Sending e-mails
There will be occasions when you may need to send an e-mail from a Python script. An example might be an alert for the successful completion or error in a long-running geoprocessing operation. On these and other occasions, sending an e-mail can be helpful.
Getting ready
Sending an e-mail through a
Python script will require that you have access to a mail server. This can be a public e-mail service, such as Yahoo, Gmail, or others. It can also use outgoing mail servers configured with applications, such as Microsoft Outlook. In either case, you'll need to know the host name and port of the e-mail server. The Python smtplib
module is used to create connections to the mail server and to send e-mails.
The Python email
module contains a Message
class that represents e-mail messages. Each message contains both headers and a body. This class can't be used to send e-mails; it just handles its object representation. In this recipe, you'll learn how to use the smtp
class to send e-mails containing an attachment through your script. The Message
class can parse a stream of characters or a file containing an e-mail using either the message_from_file()
or message_from_string()
functions. Both will create a new Message
object. The body of the mail can be obtained by calling Message.getpayload()
.
Note
We are using the Google Mail service for this exercise. If you already have a Gmail account, then simply provide your username and password as the values for these variables. If you don't have a Gmail account, you'll need to create one or use a different mail service to complete this exercise; Gmail accounts are free.
How to do it…
Follow these steps to create a script that can send emails:
Open IDLE and create a file called
c:\ArcpyBook\Appendix2\SendEmail.py
.In order to send e-mails with attachments, you're going to need to import the
smtplib
module along with theos
module, and several classes from the e-mail module. Add the followingimport
statements to your script:import smtplib from email.MIMEMultipart import MIMEMultipart from email.MIMEBase import MIMEBase from email.MIMEText import MIMEText from email import Encoders import os
Create the following variables and assign your Gmail username and password as the values. Do keep in mind that this method of e-mailing from your Python script can invite problems, as it requires that you include your username and password:
gmail_user = "<username>" gmail_pwd = "<password>"
Create a new Python function called
mail()
. This function will accept four parameters:to
,subject
,text
, andattach
. Each of these parameters should be self-explanatory. Create a newMIMEMultipart
object and assign thefrom
,to
, andsubject
keys. You can also attach the text of the e-mail to this newmsg
object usingMIMEMultipart.attach()
:def mail(to, subject, text, attach): msg = MIMEMultipart() msg['From'] = gmail_user msg['To'] = to msg['Subject'] = subject msg.attach(MIMEText(text))
Attach the file to the e-mail:
part = MIMEBase('application', 'octet-stream') part.set_payload(open(attach, 'rb').read()) Encoders.encode_base64(part) part.add_header('Content-Disposition', 'attachment; filename="%s"' % os.path.basename(attach)) msg.attach(part)
Create a new SMTP object that references the Google Mail service, passes in the username and password to connect to the mail services, sends the e-mail, and closes the connection:
mailServer = smtplib.SMTP("smtp.gmail.com", 587) mailServer.ehlo() mailServer.starttls() mailServer.ehlo() mailServer.login(gmail_user, gmail_pwd) mailServer.sendmail(gmail_user, to, msg.as_string()) mailServer.close()
Call the
mail()
function, passing in the recipient of the e-mail, a subject for the e-mail, the text of the e-mail, and the attachment:mail("<email to send to>", "Hello from python!", "This is an email sent with python", "c:/ArcpyBook/data/bc_pop1996.csv")
The entire script should appear as follows:
import smtplib from email.MIMEMultipart import MIMEMultipart from email.MIMEBase import MIMEBase from email.MIMEText import MIMEText from email import Encoders import os gmail_user = "<username>" gmail_pwd = "<password>" def mail(to, subject, text, attach): msg = MIMEMultipart() msg['From'] = gmail_user msg['To'] = to msg['Subject'] = subject msg.attach(MIMEText(text)) part = MIMEBase('application', 'octet-stream') part.set_payload(open(attach, 'rb').read()) Encoders.encode_base64(part) part.add_header('Content-Disposition', 'attachment; filename="%s"' % os.path.basename(attach)) msg.attach(part) mailServer = smtplib.SMTP("smtp.gmail.com", 587) mailServer.ehlo() mailServer.starttls() mailServer.ehlo() mailServer.login(gmail_user, gmail_pwd) mailServer.sendmail(gmail_user, to, msg.as_string()) mailServer.close() mail("<email to send to>", "Hello from python!", "This is an email sent with python", "bc_pop1996.csv")
Save and run the script. For testing, I used my personal Yahoo account as the recipient. You'll notice that my inbox has a new message from my Gmail account; also notice the attachment:
How it works…
The first parameter passed into the mail()
function is the e-mail address that will receive the e-mail. This can be any valid e-mail address, but you'll want to supply a mail account that you can actually check, so that you can make sure your script runs correctly. The second parameter is just the subject line of the e-mail. The third parameter is the text of the e-mail. The final parameter is the name of a file that will be attached to the e-mail. Here, I've simply defined that the bc_pop1996.csv
file should be attached. You can use any file you have access to, but you may want to just use this file for testing.
We then create a new MIMEMultipart
object inside the mail()
function, and assign the from
, to
, and subject
keys. You can also attach the text of the e-mail to this new msg
object using MIMEMultipart.attach()
. The bc_pop1996.csv
file is then attached to the e-mail using a MIMEBase
object and attached to the e-mail using msg.attach(part)
.
At this point, we've
examined how a basic text e-mail can be sent. However, we want to send a more complex e-mail message that contains text and an attachment. This requires the use of MIME messages, which provides the functionality to handle multi-part e-mails. MIME messages need boundaries between the multiple parts, along with extra headers to specify the content being sent. The MIMEBase
class is an abstract subclass of Message
and enables this type of an e-mail to be sent. Because it is an abstract class, you can't create actual instances of this class. Instead, you use one of the subclasses, such as MIMEText
. The last step of the mail()
function is to create a new SMTP object that references the Google Mail service, passes in the username and password to connect to the mail services, sends the e-mail, and closes the connection.