Download Email Attachment from Microsoft Exchange Web Services Automatically | by Joshua Yeung | Towards Data Science

Did you need to download email attachments regularly? Do you want to automate this boring process? I know that feel bro. When I first come to my job, I was assigned a daily task: download the attached report from the email sent to our team every day. It’s not a difficult task, but it’s extremely boring and I often forget to do so.
How can I get rid of this dummy task: Downloading email attachments regularly.
After researching the internet I discovered that a little Python script could take over my work. Let’s explore what Python can help us.
Exchangelib, a Python library that provides a simple interface that allows Python scripts to interact with Microsoft Exchange or Exchange Web Services (EWS)
github.com
You can install this package from PyPI:
pip install exchangelib
And then you import package following the instruction of the official site. You may not use all of them,
#import pytzfrom exchangelib import DELEGATE, IMPERSONATION, Account, Credentials, ServiceAccount, EWSDateTime, EWSTimeZone, Configuration, NTLM, GSSAPI, CalendarItem, Message, Mailbox, Attendee, Q, ExtendedProperty, FileAttachment, ItemAttachment, HTMLBody, Build, Version, FolderCollection
The next step is to specify your credentials, that is the login username and the password
credentials = Credentials(username='john@example.com', password='topsecret')
You have to config the mail server of course. Exchangelib should be able to identify the authentication type used by your email server, but in my case, it failed and I specified the authentication type to NTLM.
ews_url = 'mail.example.com'ews_auth_type = 'NTLM'primary_smtp_address = 'john@example.com'config = Configuration(service_endpoint=ews_url, credentials=credentials, auth_type=ews_auth_type)# An Account is the account on the Exchange server that you want to connect to.account = Account(primary_smtp_address=primary_smtp_address,config=config, autodiscover=False,access_type=DELEGATE)
Now you have an Account object and you can navigator through it. It just follows your email account folder structure. For example, if you want to operate on the inbox folder, just use this simple syntax: account.inbox. To operate a folder inside the inbox, just put a backslash and single quote like this: account.inbox / 'some_folder'.
If you just want to download all the attachments in a folder, this shortcode would help. You may require os package to process local directory.
some_folder = account.inbox / 'some_folder'for item in some_folder.all():    for attachment in item.attachments:        if isinstance(attachment, FileAttachment):            local_path = os.path.join(local_path, attachment.name)            with open(local_path, 'wb') as f:                f.write(attachment.content)
That’s it!
If I just want to download the files that I haven’t downloaded before, what can I do further? I need a mechanism to check if the attachment exists locally.
Our team uses an FTP server to store all the attached reports. Thus in my Python script, first of all, I log in to the FTP server and list all files with the specified prefix. A checking is added to the script to determine whether this file exists in the current file list or not.
To do so, I used ftplib, a library of FTP protocol client.
docs.python.org
The following code logins to the FTP server (ftp.login()), navigate the target folder (ftp.cwd()) and list all files (ftp.nlst()) that matched the specified prefix (file.startswith())
from ftplib import FTPftp_server_ip = FTP_SERVER_IPusername = 'username'password = 'password'remote_path = 'remote_path'local_path = 'local_path'with FTP(ftp_server_ip) as ftp:    ftp.login(user=username, passwd=password)    ftp.cwd(remote_path + '/copied data')    filelist = [file for file in ftp.nlst() if file.startswith('YOUR_FILE_PREFIX')]
After downloading the attachments locally, I upload the files to the FTP server. I use storbinary to send STOR command to upload the attachments.
if attachment.name not in filelist:# Check if the attachment downloaded before    local_path = os.path.join(local_path, attachment.name)    with open(local_path, 'wb') as f:        f.write(attachment.content)with FTP(ftp_server_ip) as ftp:    ftp.login(user=username, passwd=password)    ftp.cwd(remote_path)    file = open(local_path, 'rb')    ftp.storbinary('STOR {}'.format(attachment.name), file)    file.close()
I automated this daily routine using a simple Python script that can be scheduled to run regularly. The script automates the workflow that downloads missing attachments from the mail server and uploads them to the FTP server.