Reading Outlook Emails and Saving Attachments with Python


Hello, tech enthusiasts! ๐Ÿ‘‹

Every morning, the same kind of email arrives: “Daily Sales Report”, with an Excel file attached. You open the email, click the file, choose Save As, find the right folder and click Save. It takes a minute. But a minute every day, plus the days you forget, adds up.

Today we will write a Python script that reads your Outlook Inbox, finds the right emails and saves the attachments for you. We will use pywin32. It is a library that lets Python talk to Windows programs like Outlook. Think of it like a remote control: Python presses the buttons inside Outlook for you.

What you need

  • Windows with the desktop Outlook app, already signed in to your email
  • Python 3 from https://www.python.org
  • The pywin32 library
  • A folder for the files, for example C:\Reports\Attachments

Install pywin32 in the Command Prompt:

pip install pywin32

Note: this method works with the classic desktop Outlook only. It does not work with Outlook in the browser.

Step 1: Connect to Outlook and open the Inbox

Create a file called save_attachments.py:

import win32com.client

# Connect to the Outlook app that is installed on this computer
outlook = win32com.client.Dispatch("Outlook.Application")
namespace = outlook.GetNamespace("MAPI")

# 6 is the number Outlook uses for the Inbox folder
inbox = namespace.GetDefaultFolder(6)
print("Emails in Inbox:", inbox.Items.Count)

Run it with python save_attachments.py. If you see a number, Python is talking to Outlook.

MAPI is the system Outlook uses to reach your mailbox. You do not need to understand it, just use the name.

Step 2: Loop through the emails

Now we look at each email. We sort them so the newest come first:

items = inbox.Items
items.Sort("[ReceivedTime]", True)   # True = newest first

for item in items:
    # 43 means "mail item". Skip meeting invites and other items.
    if item.Class != 43:
        continue
    print(item.ReceivedTime, "|", item.Subject)

Your Inbox can also hold calendar invites and delivery reports. They do not always have the same fields, so we skip anything that is not a normal email.

Step 3: The full script with filters

Now let us filter by subject and date, and save the files. Replace the code with this full version:

import datetime
import re
from pathlib import Path

import win32com.client

SUBJECT_WORD = "Daily Sales Report"          # text to look for in the subject
DAYS_BACK = 7                                # only check the last 7 days
SAVE_FOLDER = Path(r"C:\Reports\Attachments")

SAVE_FOLDER.mkdir(parents=True, exist_ok=True)
since = datetime.date.today() - datetime.timedelta(days=DAYS_BACK)

outlook = win32com.client.Dispatch("Outlook.Application")
inbox = outlook.GetNamespace("MAPI").GetDefaultFolder(6)

items = inbox.Items
items.Sort("[ReceivedTime]", True)  # newest first

saved = 0
for item in items:
    if item.Class != 43:             # skip non-email items
        continue

    received = item.ReceivedTime.date()
    if received < since:
        break                        # sorted newest first, so we can stop here

    if SUBJECT_WORD.lower() not in item.Subject.lower():
        continue

    for att in item.Attachments:
        # Remove characters that Windows does not allow in file names
        clean_name = re.sub(r'[\\/:*?"<>|]', "_", att.FileName)
        # Add the date so files with the same name do not overwrite each other
        target = SAVE_FOLDER / f"{received:%Y-%m-%d}_{clean_name}"
        att.SaveAsFile(str(target))  # Outlook needs a full path as text
        saved += 1
        print("Saved:", target)

print(f"Done. {saved} file(s) saved.")

Step 4: Understand the important parts

  • ReceivedTime gives the date and time the email arrived. We use .date() to compare only the day.
  • Because the list is sorted newest first, we can break when we reach an old email. This makes the script fast, even with a big Inbox.
  • att.SaveAsFile() saves one attachment. It needs a full path, like C:\Reports\Attachments\file.xlsx, as text. That is why we use str(target).
  • We add the date to the file name. Daily reports often have the same name every day, and we do not want to overwrite yesterday’s file.

Step 5: Run and check

Run python save_attachments.py. Open C:\Reports\Attachments and check the files. ๐ŸŽ‰

This script only reads emails and saves files. It does not delete or move anything in Outlook, so it is safe to run many times.

Common problems

  • pywintypes.com_error when connecting. โ†’ Open Outlook once by hand and make sure it is signed in. Also check you are using desktop Outlook.
  • Logos and signature images are saved too. โ†’ Skip small files: add if att.Size < 10000: continue, or only keep names ending with .xlsx.
  • No emails found, but you can see them. โ†’ Maybe a rule moves them to another folder. Use inbox.Folders["Reports"] to open a subfolder of the Inbox.
  • Outlook shows a security prompt. โ†’ This protects you from unknown programs. Keep your antivirus active and allow access only for scripts you wrote yourself.

Wrap-up

You now have a script that collects your daily files without a single click. Next, combine it with the pandas cleaning from an earlier post: save the attachment, clean it, and build your report, all in one run. Happy automating!