在不下载附件的情况下获取Gmail附件文件名

13

我正在尝试获取来自Gmail帐户的所有消息,这些消息可能包含一些大附件(约30MB)。 我只需要名称,而不是整个文件。 我找到了一段代码来获取消息和附件的名称,但它会下载文件,然后读取其名称:

import imaplib, email

#log in and select the inbox
mail = imaplib.IMAP4_SSL('imap.gmail.com')
mail.login('username', 'password')
mail.select('inbox')

#get uids of all messages
result, data = mail.uid('search', None, 'ALL') 
uids = data[0].split()

#read the lastest message
result, data = mail.uid('fetch', uids[-1], '(RFC822)')
m = email.message_from_string(data[0][1])

if m.get_content_maintype() == 'multipart': #multipart messages only
    for part in m.walk():
        #find the attachment part
        if part.get_content_maintype() == 'multipart': continue
        if part.get('Content-Disposition') is None: continue

        #save the attachment in the program directory
        filename = part.get_filename()
        fp = open(filename, 'wb')
        fp.write(part.get_payload(decode=True))
        fp.close()
        print '%s saved!' % filename
我需要每分钟执行一次此操作,因此无法下载数百MB的数据。我是网页脚本的新手,是否有人可以帮助我?实际上,我不需要使用imaplib,任何Python库都可以满足我的需求。
此致敬礼。

你知道在 Gmail 中只能发送最多20MB的附件吗? - 0x90
我的意思是所有消息中的所有附件。 - mopsiok
4个回答

9

不必获取完整内容的RFC822,而是可以指定BODYSTRUCTURE

imaplib返回的数据结构相当令人困惑,但您应该能够在不下载整个邮件的情况下找到每个部分的文件名、内容类型和大小。


这就是我一直在寻找的...结果确实令人困惑,但它能够工作。非常感谢! - mopsiok
这正是我也在寻找的。但你有什么线索可以解析那个疯狂的结果字符串吗?@mopsiok,你是如何处理它的? - jjon
1
我已经对它进行了一些测试,但结果并不是很好。实际上,我发现获取附件列表对我的应用程序来说相当不足。最终,我通过传递整个邮件内容来获取文本和所有附件。我没有解析代码,就像我说的那样,它是无效的。抱歉... - mopsiok
对于新读者,请参考 https://dev59.com/aWYr5IYBdhLWcg3wdp4C 的 EDIT 部分。 - Dheemanth Bhat

3
如果您了解文件名的一些信息,您可以使用X-GM-RAW Gmail IMAP SEARCH 命令扩展。这些扩展允许您使用任何Gmail高级搜索查询来过滤邮件。这样,您可以将下载限制为匹配的邮件,或者排除一些您不需要的邮件。
mail.uid('search', None, 'X-GM-RAW', 
       'has:attachment filename:pdf in:inbox -label:parsed'))

在收件箱中搜索带有PDF附件但未被标记为“已解析”的消息。
一些专业提示: - 标记您已经解析过的消息,这样您就不需要再次获取它们(在上面的示例中使用“-label:parsed”筛选器) - 总是使用uid版本而不是标准的顺序ID(您已经在做这个) - 不幸的是MIME非常混乱:有许多客户端会做奇怪(或纯粹错误)的事情。您可以尝试仅下载并解析标题,但这值得花费这么多麻烦吗?
【编辑】
如果在解析后标记了邮件,则可以跳过已经解析过的消息。这应该足够合理地监视您的班级邮箱。
也许你生活在一个互联网带宽比程序员时间更昂贵的角落;在这种情况下,您可以仅获取标题并查找“Content-disposition”==“attachment;filename=somefilename.ext”。

很酷,但问题是我对附件一无所知。我正在编写一个脚本来“扫描”班级账户的所有Gmail收件箱,并告诉我是否有新邮件,包括附件信息(名称和大小)。 搜索未读邮件行不通,因为该帐户由30个人使用。 - mopsiok
你可以跳过没有附件以及已经解析过的邮件;请注意,你也可以按大小进行筛选。 - Paulo Scardine
当然可以,但跳过我已经解析的消息没有问题。问题是在一分钟内解析下一个带有20MB附件的20条消息。 - mopsiok
嗨,保罗,我使用了高级搜索。但我的问题是我想搜索xls文件,所以我使用了“filename:xls”,但最终我得到了xls文件和xlsx文件。你知道如何只搜索xls文件吗? - Cacheing
@Cacheing:也许这值得作为一个新问题来询问——评论系统正在破坏我的回答。 - Paulo Scardine

2
进行 RFC822 消息数据项的获取与使用 BODY[] 是等效的。IMAP4 还支持其他消息数据项,详见 RFC 3501 第6.4.5节
尝试请求不同的消息数据项,以获取所需信息。例如,您可以尝试使用 RFC822.HEADER 或者 BODY.PEEK[MIME]

1

虽然这是一个老问题,但我今天想分享一下我想出的解决方案。它会搜索所有带附件的电子邮件,并输出uid、发件人、主题以及格式化的附件列表。编辑相关代码以展示如何格式化BODYSTRUCTURE:

    data   = mailobj.uid('fetch', mail_uid, '(BODYSTRUCTURE)')[1]
    struct = data[0].split()        
    list   = []                     #holds list of attachment filenames

    for j, k in enumerate(struct):
        if k == '("FILENAME"':
            count = 1
            val = struct[j + count]
            while val[-3] != '"':
                count += 1
                val += " " + struct[j + count]
            list.append(val[1:-3])
        elif k == '"FILENAME"':
            count = 1
            val = struct[j + count]
            while val[-1] != '"':
                count += 1
                val += " " + struct[j + count]
            list.append(val[1:-1])

我也在GitHub上发布了它。 编辑 以上解决方案不错,但从有效载荷中提取附件文件名的逻辑并不健壮。当文件名包含带有仅两个字符的第一个单词的空格时,它会失败,
例如:“ad cde gh.png”。
尝试这个:
import re # Somewhere at the top

result, data = mailobj.uid("fetch", mail_uid, "BODYSTRUCTURE")

itr = re.finditer('("FILENAME" "([^\/:*?"<>|]+)")', data[0].decode("ascii"))

for match in itr:
    print(f"File name: {match.group(2)}")

测试正则表达式这里


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接