Breadcrumbs:

Table of contents

  1. Table of contents
  2. Introduction
  3. Preparation:
  4. Explanation:
    1. Import package
  5. Editing Email Info
  6. Instantiate MIMEText Wrapper
    1. Sending Email
  7. Completed Code:
  8. Reference:

Introduction

This article is for solving questions like: 1) How to send bulks of email in less than a minute? 2) How to automate the process of sending email to multiple user? And doing this regularlly? So here is sample sample data and the result we will obtained at the end.

Sample Data:

image-20210817024452834

Result:

image-20210817024412267

In this tutorial, we are going to write a program that will read some information from Excel and print a summarized report to a designated email address.

Preparation:

First, you need to have a Gmail account. To set up a Gmail address for testing your code, do the following:

If you don’t want to lower the security settings of your Gmail account, check out Google’s documentation on how to gain access credentials for your Python script, using the OAuth2 authorization framework.

Explanation:

Import package

# Type: Data Analysis, Python
import pandas as pd
import smtplib, ssl
# 用于构造可视化的content
from email.mime.text import MIMEText
from email.header import Header

pandas: use for reading data from excel, with “read_excel()” method, read more about its usage and example, see here

smtplib: Use Simple Mail Transfer Protocol (SMTP) for sending email. It’s an internet standard communication protocol for electronic mail transmission. Mail servers and other message transfer agents use SMTP to send and receive mail messages.

ssl: Use to make a secure connection. As per Python’s Security considerations, it is highly recommended that you use create_default_context() from the ssl module. This will load the system’s trusted CA certificates, enable host name checking and certificate validation, and try to choose reasonably secure protocol and cipher settings.

email: use for constructing emall messages

Editing Email Info

# Editing Email Info
host = "smtp.gmail.com"     # Using Gmail server to send the email
subject = "[AUTO EMAIL] Student Grader Report"
body = f"""<h1> Student Grade Report </h1> {df.describe().to_html()}"""
from_addr, to_addr = "dongzq760@gmail.com", "dongzq760@gmail.com"

host: For the rest of the tutorial, I’ll assume you’re using a Gmail account, but if you’re using a local debugging server, just make sure to use localhost as your SMTP server and use port 1025 rather than port 465 or 587. Besides this, you won’t need to use login() or encrypt the communication using SSL/TLS.

subject: or email title

body: The Email Content. If you use want some simple plain text email, you can do something like:

Instantiate MIMEText Wrapper

# Getting Email server password or token_id
import getpass
password = getpass.getpass() 

It’s not safe practice to store your email password in your code, especially if you intend to share it with others. Instead, use input() to let the user type in their password when running the script, as in the example above. If you don’t want your password to show on your screen when you type it, you can import the getpass module and use .getpass() instead for blind input of your password.

# Instantiate MIMEText object
message = MIMEText(body, 'html', 'utf-8')
message['From'], message['To'] = from_addr, to_addr
message['Subject'] = Header(subject, "utf-8")
message = message.as_string()   # Because SMTP.sendmail, required bytes-like string. Otherwise you will see error like: Error: expected string or bytes-like object
"""
SMTP.sendmail(from_addr, to_addrs, msg, mail_options=(), rcpt_options=())
@msg: may be a string containing characters in the ASCII range, or a byte string. A string is encoded to bytes using the ascii codec, and lone `\r` and `\n` characters are converted to `\r\n` characters. A byte string is not modified. Read more in here, https://docs.python.org/3/library/smtplib.html#smtplib.SMTP.sendmail
"""

Note: If you don’t want an MEMEText object as a wrapper, you can write those protocal body as Plain-text, but by yourself. For example:

message = f'''From: From Person <{from_addr}>
To: To Person <{to_addr}
MIME-Version: 1.0
Content-type: text/html
Subject:{subject}
{body}'''

Sending Email

try:
    import ssl
    context = ssl.create_default_context()
    server = smtplib.SMTP_SSL(host, 465, context=context)
    server.login(from_addr, password)
    server.sendmail(from_addr, [to_addr], message)
except Exception as e:
    print(f"Error: {e}")
finally:
    server.quit()
    print("Email Sent!")

Starting a Secure SMTP Connection

When you send emails through Python, you should make sure that your SMTP connection is encrypted, so that your message and login credentials are not easily accessed by others. SSL (Secure Sockets Layer) and TLS (Transport Layer Security) are two protocols that can be used to encrypt an SMTP connection. It’s not necessary to use either of these when using a local debugging server.

There are two ways to start a secure connection with your email server:

  • Start an SMTP connection that is secured from the beginning using SMTP_SSL().
  • Start an unsecured SMTP connection that can then be encrypted using .starttls().

In both instances, Gmail will encrypt emails using TLS, as this is the more secure successor of SSL. As per Python’s Security considerations, it is highly recommended that you use create_default_context() from the ssl module. This will load the system’s trusted CA certificates, enable host name checking and certificate validation, and try to choose reasonably secure protocol and cipher settings.

Completed Code:

# Author: Zhengqi Dong
# Date: 08/17/2021
# Type: Data Analysis, Python
import pandas as pd
import smtplib  
# 用于构造可视化的content
from email.mime.text import MIMEText
from email.header import Header

# No attachements, just simple plain-text or html content
def send_simple_email(host, subject, from_addr, to_addr, body, password):
    # Instantiate MIMEText object
    message = MIMEText(body, 'html', 'utf-8')
    message['From'], message['To'] = from_addr, to_addr
    message['Subject'] = Header(subject, "utf-8")
    message = message.as_string()   # Because SMTP.sendmail, required bytes-like string. Otherwise you will see error like: Error: expected string or bytes-like object

    try:
        import ssl
        context = ssl.create_default_context()
        server = smtplib.SMTP_SSL(host, 465, context=context)
        server.login(from_addr, password)
        server.sendmail(from_addr, [to_addr], message)
    except Exception as e:
        print(f"Error: {e}")
    finally:
        server.quit()
        print("Email Sent!")


if __name__ == "__main__":
    # Reading excel content
    df = pd.read_excel("Student_Grade.xlsx")

    # Editing Email Info
    host = "smtp.gmail.com"     # Using Gmail server to send the email
    subject = "[AUTO EMAIL] Student Grader Report"
    html_body = f"""<h1> Student Grade Report </h1> {df.describe().to_html()}"""
    from_addr, to_addr = "dongzq760@gmail.com", "dongzq760@gmail.com"

    # Get Passwork from user
    import getpass
    password = getpass.getpass()

    # Sending Email
    send_simple_email(host, subject, from_addr, to_addr, html_body, password)


# Debug:
# 1) xlrd.biffh.XLRDError: Excel xlsx file; not supported ===》 Because, the latest version of xlrd (2.0.1) only supports .xls files. so try this `pip install xlrd==1.2.0`, refers to https://stackoverflow.com/questions/65254535/xlrd-biffh-xlrderror-excel-xlsx-file-not-supported
# 2) smtplib.SMTPAuthenticationError: (535, b'5.7.8 Username and Password not accepted. Learn more at\n5.7.8  https://support.google.com/mail/?p=BadCredentials j18sm735815qke.75 - gsmtp') ==> Mean wrong username or password.
# 3) smtplib.SMTPServerDisconnected: Connection unexpectedly closed ==> This means you need do something on your Gmail Setting: Turn Allow less secure apps to ON(https://myaccount.google.com/lesssecureapps). Be aware that this makes it easier for others to gain access to your account.

Reference: