1

I am working on assignment to extract emails from the mailbox.

Below are my codes, I am referencing from this case and combine with some other research online:

import win32com.client
import pandas as pd
import os

outlook = win32com.client.Dispatch("Outlook.Aplication").GetNamespace("MAPI")
inbox = outlook.GetDefaultFolder(6).Folders["Testmails"]
condition = pd.read_excel(r"C:\Users\Asus\Desktop\Python\Condition.xlsx", sheet_name = 'endword')
emails = condition.iloc[:,1].tolist()
done = outlook.GetDefaultFolder(6).Folders["Testmails"].Folders["Done"]

Item = inbox.Items.GetFirst()
add = Item.SenderEmailAddress

for attachment in Item.Attachments:
    if any([add.endswith(m) for m in condition]) and Item.Attachments.Count > 0:
       print(attachment.FileName)
       dir = "C:\\Users\\Asus\\Desktop\\Python\\Output\\"
       fname = attachment.FileName
       outpath = os.path.join(dir, fname)
       attachment.SaveAsFile(outpath)
Item.Move(done)

The code above is running, but it only saves the first email attachment, and the other email that matches the condition is not saving.

The condition file is like below, if is gmail to save in file A. But I am not sure if we can do by vlookup in loops.

        mail end  Directory
0      gmail.com  "C:\\Users\\Asus\\Desktop\\Output\\A\\"
1    outlook.com  "C:\\Users\\Asus\\Desktop\\Output\\A\\"
2  microsoft.com  "C:\\Users\\Asus\\Desktop\\Output\\B\\"

Thanks for all the gurus who is helping much. I have edited the codes above but now is facing other issues on looping.

mayliew
  • 35
  • 6
  • Your given code looks to me like it has some typos in (e.g. you're doing `Item = Item.GetFirst` where I imagine you are actually writing `Item = Items.GetFirst`) and you've got some syntax errors (in the form of unclosed strings). If you edit your post to correct to the code that's giving you the error I may be able to help you better – Minion3665 Dec 12 '22 at 12:11
  • 1
    hi @Minion3665, I am really sorry for the typo mistakes, i had edited the codes above – mayliew Dec 12 '22 at 12:27
  • 1
    https://learn.microsoft.com/en-us/office/vba/api/outlook.items.getfirst If your filter has no matches, the return from `GetFirst()` is Nothing (in VBA), which I am assuming `win32com` translates to the Python None. Check with `if Item is None:` before continuing. – DS_London Dec 12 '22 at 17:04
  • 1
    Additionally, the filter string is likely wrong. You can start reading here: https://learn.microsoft.com/en-us/office/vba/outlook/how-to/search-and-filter/filtering-items-using-a-string-comparison I am not sure the filter can deal with lists ie I haven't found a `in` keyword. This might be one of those occasions where you do actually have to iterate through all the Items, when your filter is too complex. Perhaps ask a separate question about how to filter for a list of matches. There are some Outlook gurus on SO who will likely be able to help. – DS_London Dec 13 '22 at 09:10
  • Hi @DS_London , i tried to change the filter to specific email address and now it works. I'll try check and ask how to deal with a list ;) – mayliew Dec 13 '22 at 13:28

2 Answers2

2

Fix Application on Dispatch("Outlook.Aplication") should be double p

On filter add single quotation mark round 'emails'

Example

Filter = "[SenderEmailAddress] = 'emails'"

for loop, you are using i but then you have print(attachment.FileName) / attachment.SaveAsFile

use i for all - print(i.FileName) / i.SaveAsFile or attachment


import win32com.client

Outlook = win32com.client.Dispatch("Outlook.Application")
olNs = Outlook.GetNamespace("MAPI")
Inbox = olNs.GetDefaultFolder(6)

Filter = "[SenderEmailAddress] = '0m3r@email.com'"

Items = Inbox.Items.Restrict(Filter)
Item = Items.GetFirst()

if Item.Attachments.Count > 0:
    for attachment in Item.Attachments:    
        print(Item.Attachments.Count)
        print(attachment.FileName)
        attachment.SaveAsFile(r"C:\path\to\my\folder\Attachment.xlsx")
0m3r
  • 12,286
  • 15
  • 35
  • 71
  • Hi @0m3r, thanks for the advice. I corrected the codes according to your suggestion, but I still face the AttributeError saying that no attribute 'Attachments'. May I know if I missed out any library packages? – mayliew Dec 12 '22 at 16:18
  • Can you run my code with out the line `attachment.SaveAsFile` and update email address, let me know please @mayliew – 0m3r Dec 12 '22 at 16:24
  • if you are still getting the same error, can you share what python version & pywin32 version you are running? – 0m3r Dec 12 '22 at 16:27
  • hi @0m3r, I had tried to run the code without the line attachment.SaveAsFile and change to a particular email address, and is working now. I amended a bit on the file path and is successfully saving the attachment now. May I know is there any ways to filter a list instead one particular email? – mayliew Dec 13 '22 at 13:26
  • the simplest way is to run for loop on your list , once you have the first object then run `Items = Inbox.Items.Restrict(Filter)` to .....`attachment.SaveAsFile` search loop on list python, if you get errors just post question and we will try to help you – 0m3r Dec 13 '22 at 17:07
  • another example filter with attachment https://stackoverflow.com/a/61091622/4539709 – 0m3r Dec 13 '22 at 17:20
  • Hi @0m3r, I have edited the codes to the latest version that I have been tested again. It is working but it only shows the first email after the loop. Can I seek your advice on this? Sorry if I misunderstand your comment on the for loop. – mayliew Dec 14 '22 at 16:42
1

The 'NoneType' object has no attribute 'Attachments' error means that you're trying to get attachments from something that is None.

You're getting attachments in only one place:

for i in Item.Attachments:
    ...

so we can conclude that the Item here is None.

By looking at Microsoft's documentation we can see that the method...

Returns Nothing if no first object exists, for example, if there are no objects in the collection

Therefore, I'd imagine there's an empty collection, or no emails matching your filter

To handle this you could use an if statement

if Item is not None:
    for i in Item.Attachments:
        ...
else:
    pass  # Do something here if there's nothing matching your filter
Minion3665
  • 879
  • 11
  • 25