Home > Mobile >  Python: Save all attachments from unread Outlook email
Python: Save all attachments from unread Outlook email

Time:03-28

I have a subfolder in Outlook. My objective is to go through all unread emails or the ones I received today in that folder and download all existing attachments in those emails on my desktop. So far, I've the following code:

def saveattachments(messages,today,path): 
for message in messages:
if message.Unread or message.Senton.date() == today:

        attachments = message.Attachments
        attachment = attachments.Item(1)

        for attachment in message.Attachments:
            attachment.SaveAsFile(os.path.join(path, str(attachment)))
            if message.Unread:
                message.Unread = False
            break



def main():
  path = '\\Desktop\Test Python Save Attachments Outlook'
  today = datetime.today().date()
  outlook = win32com.client.Dispatch("Outlook.Application").GetNamespace("MAPI")
  inbox = outlook.GetDefaultFolder(6)
  folder = inbox

  folderMessages = folder.Items  
  messages = folderMessages
  saveattachments(messages,today,path)

  print ("Downloading Files successful.") 

if __name__=="__main__":
    main()

The problem with the above code is that it downloads only one attachment from the email at the time. Also, it seems that it does favor PDF documents over Excel files, as it always first saves the former ones. Any ideas or suggestions on how the code might be corrected accordingly? Many thanks in advance!

CodePudding user response:

You should never loop through all items in a folder - it is like a SELECT query without a WHERE clause. Inefficient to put it mildly.

Use Items.Restrict or Items.Find/FindNext with a query on Unread and SentOn property being in the range. You can also add a condition on the PR_HASATTACH MAPI property (DASL name "http://schemas.microsoft.com/mapi/proptag/0x0E1B000B")

CodePudding user response:

To make sure that all attached files are saved correctly you need to be sure that a unique name is passed to the SaveAsFile method. For example, the following code doesn't check whether such file already exists in the target folder:

for attachment in message.Attachments:
            attachment.SaveAsFile(os.path.join(path, str(attachment)))

I'd suggest using the FileName property of the Attachment class and also add a unique ID to the filename. Note, you need to also make sure that only allowed symbols are used for the filename. See What characters are forbidden in Windows and Linux directory names? for more information.

My objective is to go through all unread emails or the ones I received today in that folder

As Dmitry noted, there is no need to iterate over all items in the folder. Instead, you need to find out only items that correspond to your conditions and only then iterate over them and save attached files.

To find all unread items from the Inbox folder you can use the following code (C#, I am not familiar with a python syntax, but the Outlook object model is common for all kind of applications):

using System.Text;
using System.Diagnostics;
// ...
private void RestrictUnreadItems(Outlook.MAPIFolder folder)
{
    string restrictCriteria = "[UnRead] = true";
    StringBuilder strBuilder = null;
    Outlook.Items folderItems = null;
    Outlook.Items resultItems = null;
    Outlook._MailItem mail = null;
    int counter = default(int);
    object item = null;
    try
    {
        strBuilder = new StringBuilder();
        folderItems = folder.Items;
        resultItems = folderItems.Restrict(restrictCriteria);
        item = resultItems.GetFirst();
        while (item != null)
        {
            if (item is Outlook._MailItem)
            {
                counter  ;
                mail = item as Outlook._MailItem;
                strBuilder.AppendLine("#"   counter.ToString()  
                                   "\tSubject: "   mail.Subject);
            }
            Marshal.ReleaseComObject(item);
            item = resultItems.GetNext();
        }
        if (strBuilder.Length > 0)
            Debug.WriteLine(strBuilder.ToString());
        else
            Debug.WriteLine("There is no match in the "
                               folder.Name   " folder.");
    }
    catch (Exception ex)
    {
        System.Windows.Forms.MessageBox.Show(ex.Message);
    }
}

The Find/FindNext or Restrict methods of the Items class can be used for that. Read more about them in the following articles:

  • Related