Sign In
Cold Outreach

5 Smart Ways How to automate email sending easily

How to Automate Email Sending with Python and Gmail

Automating email sending can save you countless hours and streamline your workflows, whether you need to send personalized newsletters, automated reports, or simple notifications. This article will guide you through the process of automating email sending using Python and Gmail. We’ll cover setting up your Gmail account, writing the Python code, scheduling your emails, and addressing security considerations to ensure your automation is reliable and secure. Get ready to unlock the power of email automation!

Setting Up Your Gmail Account for Automation

How to automate email sending - Setting up Gmail's

Before diving into the code, you need to prepare your Gmail account to allow Python to send emails. Google prioritizes security, so you’ll need to configure your account accordingly. There are two primary methods: enabling “Less secure app access” (discouraged) or, the recommended approach, using “App Passwords” with 2-Step Verification enabled.

Enabling “Less Secure App Access” (Not Recommended)

While this method is simpler, Google strongly discourages it because it reduces your account’s security. This option is being phased out completely by Google. If you still find it available, proceed with caution and only if you understand the risks. To enable it:

  • Go to your Google Account settings.
  • Navigate to the “Security” section.
  • Scroll down to “Less secure app access” and turn it ON.

Remember that enabling this option makes your account more vulnerable. It’s highly recommended to use App Passwords instead.

Enabling 2-Step Verification and Generating an App Password (Recommended)

This is the secure and recommended method. You need to enable 2-Step Verification first, then generate an App Password specifically for your Python script. Here’s how:

  • Enable 2-Step Verification:
    • Go to your Google Account settings.
    • Navigate to the “Security” section.
    • Under “Signing in to Google,” select “2-Step Verification” and follow the on-screen instructions to set it up. You’ll need a phone number for verification.
  • Generate an App Password:
    • Once 2-Step Verification is enabled, return to the “Security” section.
    • Scroll down to “App passwords” (you might need to re-enter your password).
    • In the “Select app” dropdown, choose “Mail.”
    • In the “Select device” dropdown, choose “Other (Custom name)” and enter a name like “Python Script.”
    • Click “Generate.” Google will display a 16-character App Password. Copy this password carefully! You’ll only see it once.

Important: The App Password is like a regular password, but only for your specific script. If the script is compromised, you can revoke the App Password without affecting your primary Gmail password.

Example: After following the steps above, you might receive an App Password like this: `abcd efgh ijkl mnop`. You will use this password in your Python script instead of your regular Gmail password.

Expert Tip: Store your App Password securely. Never commit it to your code repository directly. Use environment variables or a secure configuration file to keep it safe.

Security Note: Using App Passwords significantly enhances the security of your Gmail account when automating email sending. It isolates the script’s access, minimizing the risk of a compromised main account password.

Writing the Python Email Script

How to automate email sending - A code editor showing a Python script with the necessary imports, email construction, and SMTP connection details.

Now that your Gmail account is configured, let’s create the Python script to send emails. We’ll use the `smtplib` and `email` modules, which are part of Python’s standard library.

Basic Email Sending Script

This script demonstrates sending a simple text-based email.

import smtplib
from email.mime.text import MIMEText

# Email details
sender_email = "your_email@gmail.com"
receiver_email = "recipient_email@example.com"
password = "your_app_password"  # Use the App Password you generated

# Message details
subject = "Test Email from Python"
body = "This is a test email sent from a Python script."

# Create MIMEText object
msg = MIMEText(body)
msg['Subject'] = subject
msg['From'] = sender_email
msg['To'] = receiver_email

# SMTP server details (Gmail's SMTP server)
smtp_server = "smtp.gmail.com"
port = 587  # TLS port

# Send the email
try:
    server = smtplib.SMTP(smtp_server, port)
    server.starttls()  # Upgrade connection to secure TLS
    server.login(sender_email, password)
    server.sendmail(sender_email, receiver_email, msg.as_string())
    print("Email sent successfully!")
except Exception as e:
    print(f"Error sending email: {e}")
finally:
    server.quit()

Explanation:

  • `import smtplib` and `from email.mime.text import MIMEText`: Import the necessary modules. `smtplib` handles the SMTP connection, and `email.mime.text` is used to create the email message.
  • `sender_email`, `receiver_email`, `password`: Replace these with your Gmail address, the recipient’s email address, and the App Password you generated.
  • `subject`, `body`: Set the subject and body of the email.
  • `MIMEText(body)`: Creates a MIMEText object to represent the email body. This is essential for formatting the email correctly.
  • `msg[‘Subject’]`, `msg[‘From’]`, `msg[‘To’]`: Sets the email headers.
  • `smtp_server`, `port`: Specifies Gmail’s SMTP server and the TLS port (587).
  • `server = smtplib.SMTP(smtp_server, port)`: Creates an SMTP connection to Gmail.
  • `server.starttls()`: Upgrades the connection to a secure TLS connection. This is crucial for security.
  • `server.login(sender_email, password)`: Logs in to your Gmail account using the App Password.
  • `server.sendmail(sender_email, receiver_email, msg.as_string())`: Sends the email. `msg.as_string()` converts the MIMEText object to a string suitable for sending.
  • `server.quit()`: Closes the connection to the SMTP server.
  • `try…except…finally`: Handles potential errors and ensures the connection is closed even if an error occurs.

Example: Replace `your_email@gmail.com` with your actual Gmail address and `recipient_email@example.com` with a valid recipient address to test the script.

Sending HTML Emails

To send HTML emails, you need to use `MIMEText` with the `’html’` subtype.

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

# Email details
sender_email = "your_email@gmail.com"
receiver_email = "recipient_email@example.com"
password = "your_app_password"

# Message details
subject = "HTML Email from Python"
body = """
<html>
  <body>
    <h1>Hello, World!</h1>
    <p>This is an HTML email sent from a Python script.</p>
    <a href="https://www.example.com">Visit Example.com</a>
  </body>
</html>
"""

# Create a multipart message object
msg = MIMEMultipart('alternative')
msg['Subject'] = subject
msg['From'] = sender_email
msg['To'] = receiver_email

# Create the HTML part
html_part = MIMEText(body, 'html')

# Attach the HTML part to the message
msg.attach(html_part)

# SMTP server details (Gmail's SMTP server)
smtp_server = "smtp.gmail.com"
port = 587  # TLS port

# Send the email
try:
    server = smtplib.SMTP(smtp_server, port)
    server.starttls()  # Upgrade connection to secure TLS
    server.login(sender_email, password)
    server.sendmail(sender_email, receiver_email, msg.as_string())
    print("HTML Email sent successfully!")
except Exception as e:
    print(f"Error sending email: {e}")
finally:
    server.quit()

Explanation:

  • `from email.mime.multipart import MIMEMultipart`: Imports the `MIMEMultipart` class, which is used to create emails with multiple parts (e.g., both plain text and HTML versions).
  • `msg = MIMEMultipart(‘alternative’)`: Creates a `MIMEMultipart` object with the ‘alternative’ subtype. This indicates that the email contains multiple versions of the same content (e.g., plain text and HTML), and the email client should choose the best one to display.
  • `html_part = MIMEText(body, ‘html’)`: Creates a `MIMEText` object with the `’html’` subtype, indicating that the content is HTML.
  • `msg.attach(html_part)`: Attaches the HTML part to the `MIMEMultipart` message.

Example: Modify the HTML within the `body` variable to customize the email’s appearance. You can include CSS styling, images (referenced by URL), and links.

Sending Emails with Attachments

To send emails with attachments, you’ll need to use the `email.mime.base`, `email.mime.multipart`, and `email.mime.text` modules. This involves creating a multipart message and attaching the file as a separate part.

import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
from email import encoders

# Email details
sender_email = "your_email@gmail.com"
receiver_email = "recipient_email@example.com"
password = "your_app_password"

# Message details
subject = "Email with Attachment from Python"
body = "This is an email with an attachment sent from a Python script."

# Create a multipart message object
msg = MIMEMultipart()
msg['Subject'] = subject
msg['From'] = sender_email
msg['To'] = receiver_email

# Attach the body to the email
msg.attach(MIMEText(body, 'plain'))

# Attachment details
filename = "example.txt"  # Replace with your file name
filepath = "/path/to/your/file/example.txt"  # Replace with the full path to your file

# Open the file in binary read mode
with open(filepath, "rb") as attachment:
    # Create a MIMEBase object
    part = MIMEBase('application', 'octet-stream')
    part.set_payload(attachment.read())

# Encode the attachment using Base64
encoders.encode_base64(part)

# Add a header with the filename
part.add_header('Content-Disposition', f"attachment; filename= {filename}")

# Attach the part to the message
msg.attach(part)

# SMTP server details (Gmail's SMTP server)
smtp_server = "smtp.gmail.com"
port = 587  # TLS port

# Send the email
try:
    server = smtplib.SMTP(smtp_server, port)
    server.starttls()  # Upgrade connection to secure TLS
    server.login(sender_email, password)
    server.sendmail(sender_email, receiver_email, msg.as_string())
    print("Email with attachment sent successfully!")
except Exception as e:
    print(f"Error sending email: {e}")
finally:
    server.quit()

Explanation:

  • `from email.mime.base import MIMEBase`: Imports the `MIMEBase` class, which is used to represent the attachment.
  • `from email import encoders`: Imports the `encoders` module, which is used to encode the attachment (typically using Base64 encoding).
  • `filename = “example.txt”` and `filepath = “/path/to/your/file/example.txt”`: Defines the name of the attachment file and the full path to the file on your system. Important: Replace these with the actual file name and path.
  • `with open(filepath, “rb”) as attachment:`: Opens the attachment file in binary read mode (`”rb”`). This is important for handling various file types correctly.
  • `part = MIMEBase(‘application’, ‘octet-stream’)`: Creates a `MIMEBase` object with the `’application’` main type and the `’octet-stream’` subtype. This is a generic MIME type for binary data.
  • `part.set_payload(attachment.read())`: Reads the contents of the attachment file and sets it as the payload of the `MIMEBase` object.
  • `encoders.encode_base64(part)`: Encodes the attachment data using Base64 encoding. This ensures that the attachment can be transmitted safely over email.
  • `part.add_header(‘Content-Disposition’, f”attachment; filename= {filename}”)`: Adds a `Content-Disposition` header to the attachment part. This header tells the email client how to handle the attachment (in this case, as an attachment) and specifies the filename that should be used when saving the attachment.
  • The attachment part is then attached to the main `msg` object.

Example: Change the `filename` and `filepath` variables to point to a different file on your system to send that file as an attachment.

Scheduling Emails

Once you have a working email script, you’ll likely want to schedule it to run automatically at specific times. There are several ways to accomplish this, depending on your operating system and requirements.

Using Cron (Linux/macOS)

Cron is a time-based job scheduler in Unix-like operating systems. It allows you to schedule commands to run automatically at specific times or intervals.

Steps:

  • Open the crontab file: In your terminal, type `crontab -e`. This will open the crontab file in a text editor.
  • Add a cron job: Add a line to the crontab file with the following syntax:
    minute hour day_of_month month day_of_week command
    For example, to run your Python script every day at 8:00 AM, you would add the following line:
    0 8 * * * /usr/bin/python3 /path/to/your/script.py
    • `0`: Minute (0-59)
    • `8`: Hour (0-23)
    • `*`: Day of the month (1-31)
    • `*`: Month (1-12)
    • `*`: Day of the week (0-6, where 0 is Sunday)
    • `/usr/bin/python3`: The path to your Python 3 interpreter. Use `which python3` to find the correct path.
    • `/path/to/your/script.py`: The full path to your Python script.
  • Save the crontab file: Save the file and exit the editor. Cron will automatically detect the changes and start scheduling your job.

Example: To run the script `/home/user/email_script.py` every Monday at 9:00 AM, the cron entry would be:

0 9 * * 1 /usr/bin/python3 /home/user/email_script.py

Important Considerations for Cron:

  • Full Paths: Always use full paths to both the Python interpreter and your script. Cron’s environment is different from your interactive shell, so relying on relative paths or environment variables might lead to errors.
  • Logging: Cron doesn’t provide direct output to the terminal. Redirect the output of your script to a log file to troubleshoot any issues. You can do this by adding `>> /path/to/your/log_file.log 2>&1` to the end of your cron entry:
    0 8 * * * /usr/bin/python3 /path/to/your/script.py >> /path/to/your/log_file.log 2>&1
    This will append both standard output (stdout) and standard error (stderr) to the log file.
  • Environment Variables: If your script relies on environment variables, you need to define them in the crontab file. You can do this by adding lines like `VARIABLE=value` before the cron entry:
    APP_PASSWORD=your_app_password
    0 8 * * * /usr/bin/python3 /path/to/your/script.py
    However, it’s generally recommended to avoid storing sensitive information like passwords directly in the crontab file. Use a more secure method like storing the password in a separate file and reading it from the script.

Using Task Scheduler (Windows)

Task Scheduler is the built-in task scheduling component in Windows. It allows you to schedule programs or scripts to run automatically at specified times or when specific events occur.

Steps:

  • Open Task Scheduler: Search for “Task Scheduler” in the Start menu and open it.
  • Create a Basic Task: In the right pane, click “Create Basic Task…”.
  • Name and Description: Enter a name and description for the task.
  • Trigger: Choose when you want the task to start (e.g., Daily, Weekly, Monthly, One time).
  • Action: Choose “Start a program”.
  • Program/script: Enter the path to your Python interpreter (e.g., `C:\Python39\python.exe`).
  • Add arguments (optional): Enter the full path to your Python script (e.g., `C:\path\to\your\script.py`).
  • Finish: Review the task details and click “Finish”.

Example: To run the script `C:\scripts\email_script.py` daily, you would set the “Program/script” to `C:\Python39\python.exe` and the “Add arguments (optional)” to `C:\scripts\email_script.py`.

Important Considerations for Task Scheduler:

  • User Account: Configure the task to run under a specific user account. This is important if the script needs access to specific files or resources. You may need to provide the user’s password.
  • Working Directory: Set the working directory for the task. This is the directory from which the script will be executed. If your script uses relative paths, make sure the working directory is set correctly. You can find this option in the task’s “Properties” under the “Actions” tab, then edit the action.
  • Hidden Execution: To prevent a console window from appearing when the script runs, you can create a VBScript wrapper that executes the Python script silently. Save the following code as a `.vbs` file (e.g., `run_script.vbs`):
    CreateObject("WScript.Shell").Run "C:\Python39\python.exe C:\scripts\email_script.py", 0, True
    Then, in Task Scheduler, set the “Program/script” to `C:\Windows\System32\wscript.exe` and the “Add arguments (optional)” to `C:\path\to\run_script.vbs`. The `0` in the VBScript code hides the window, and `True` waits for the script to finish.

Using Python Scheduler Libraries (Advanced)

For more complex scheduling needs, you can use Python scheduler libraries like `schedule` or `APScheduler`. These libraries allow you to define schedules directly within your Python code.

Example using `schedule`:

import schedule
import time
import smtplib
from email.mime.text import MIMEText

def send_email():
    # Email sending code (same as in previous examples)
    sender_email = "your_email@gmail.com"
    receiver_email = "recipient_email@example.com"
    password = "your_app_password"
    subject = "Scheduled Email from Python"
    body = "This email was sent by a scheduled task."

    msg = MIMEText(body)
    msg['Subject'] = subject
    msg['From'] = sender_email
    msg['To'] = receiver_email

    smtp_server = "smtp.gmail.com"
    port = 587

    try:
        server = smtplib.SMTP(smtp_server, port)
        server.starttls()
        server.login(sender_email, password)
        server.sendmail(sender_email, receiver_email, msg.as_string())
        print("Scheduled email sent successfully!")
    except Exception as e:
        print(f"Error sending email: {e}")
    finally:
        if 'server' in locals():  # Check if server was defined
            server.quit()

# Schedule the email to be sent every day at 10:30 AM
schedule.every().day.at("10:30").do(send_email)

# Keep the script running to execute the schedule
while True:
    schedule.run_pending()
    time.sleep(60)  # Check every minute

Explanation:

  • `import schedule`: Imports the `schedule` library.
  • `def send_email():`: Defines a function that contains the email sending code (from previous examples). This function will be executed by the scheduler.
  • `schedule.every().day.at(“10:30”).do(send_email)`: Schedules the `send_email` function to be executed every day at 10:30 AM.
  • `while True:`: Creates an infinite loop that keeps the script running.
  • `schedule.run_pending()`: Checks if any scheduled tasks are due to run and executes them.
  • `time.sleep(60)`: Pauses the script for 60 seconds. This prevents the script from consuming too much CPU.

Before running this script, you’ll need to install the `schedule` library:

pip install schedule

Note: When using Python scheduler libraries, the script needs to be continuously running for the scheduling to work. You can achieve this by running the script in the background (e.g., using `nohup` on Linux or by creating a service on Windows). It’s important to include error handling and logging to monitor the script’s execution.

Comparison of Scheduling Methods:

MethodOperating SystemComplexityFlexibilityRequires Script Running
CronLinux/macOSMediumGoodNo
Task SchedulerWindowsMediumGoodNo
Python Scheduler LibrariesCross-PlatformHighExcellentYes

Security Best Practices for Automated Email Sending

Automating email sending involves handling sensitive information, such as your Gmail credentials and potentially user data. Therefore, it’s crucial to implement security best practices to protect your account and the privacy of your recipients.

Securely Storing Credentials

Never hardcode your Gmail password or App Password directly into your Python script. This is a major security risk. Instead, use environment variables or a secure configuration file.

Using Environment Variables:

  • Set the environment variable: On Linux/macOS, you can set an environment variable using the `export` command:
    export GMAIL_APP_PASSWORD="your_app_password"
    On Windows, you can set environment variables through the System Properties dialog.
  • Access the environment variable in your script:
    import os
    
    password = os.environ.get("GMAIL_APP_PASSWORD")
    
    if not password:
        print("Error: GMAIL_APP_PASSWORD environment variable not set.")
        exit()
    

Using a Secure Configuration File:

  • Create a configuration file (e.g., `config.ini`):
    [Gmail]
    app_password = your_app_password
    
  • Read the configuration file in your script:
    import configparser
    
    config = configparser.ConfigParser()
    config.read('config.ini')
    
    password = config['Gmail']['app_password']
    

Important: Make sure to add your configuration file to your `.gitignore` file to prevent it from being committed to your code repository.

Rate Limiting and Avoiding Spam Filters

Sending too many emails in a short period can trigger spam filters or cause Gmail to block your account. Implement rate limiting to avoid this.

Adding a Delay:

  • Use the `time.sleep()` function to add a delay between sending emails:
    import time
    
    # ... (email sending code) ...
    server.sendmail(sender_email, receiver_email, msg.as_string())
    time.sleep(5)  # Wait 5 seconds before sending the next email
    

Using a Queue:

  • For more sophisticated rate limiting, use a queue to manage the emails to be sent. This allows you to control the sending rate more precisely. Libraries like `redis` or `celery` can be used to implement a queue. This example uses `time.sleep()` for simplicity.

Avoiding Spam Filters:

  • Use a reputable sending domain: If possible, send emails from a domain you own and have properly configured with SPF, DKIM, and DMARC records.
  • Personalize your emails: Avoid sending generic, mass emails. Personalize the subject line and body of the email as much as possible.
  • Include an unsubscribe link: Provide a clear and easy way for recipients to unsubscribe from your emails. This is legally required in many jurisdictions.
  • Monitor your sender reputation: Use tools like Google Postmaster Tools to monitor your sender reputation and identify any issues that might be causing your emails to be marked as spam.

Expert Quote: “Email deliverability is a marathon, not a sprint. Consistent sender behavior and a focus on recipient engagement are key to maintaining a good sender reputation.” – hubspot-email-marketing-tactics-to-boost-roi/" class="internal-link" title="3 Hubspot Email Marketing Tactics to Boost ROI">Email Marketing Expert

Handling Errors and Logging

Proper error handling and logging are essential for debugging and monitoring your email automation script.

Error Handling:

  • Use `try…except` blocks to catch potential exceptions, such as network errors, authentication failures, and invalid email addresses.
  • Provide informative error messages that help you identify the cause of the error.
  • Implement retry logic to automatically retry sending emails that failed due to temporary errors.

Logging:

  • Use the `logging` module to log important events, such as successful email sends, errors, and warnings.
  • Configure the logging level to control the amount of information that is logged (e.g., DEBUG, INFO, WARNING, ERROR, CRITICAL).
  • Write logs to a file or a dedicated logging server for analysis.

Example:

import logging
import smtplib
from email.mime.text import MIMEText

# Configure logging
logging.basicConfig(filename='email_automation.log', level=logging.INFO,
                    format='%(asctime)s - %(levelname)s - %(message)s')

def send_email(sender_email, receiver_email, password, subject, body):
    try:
        msg = MIMEText(body)
        msg['Subject'] = subject
        msg['From'] = sender_email
        msg['To'] = receiver_email

        smtp_server = "smtp.gmail.com"
        port = 587

        server = smtplib.SMTP(smtp_server, port)
        server.starttls                    

Share this article