13

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

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 库对我来说都可以。

此致

4

4 回答 4

9

而不是 fetch RFC822,这是完整的内容,你可以指定BODYSTRUCTURE.

由此产生的数据结构imaplib相当混乱,但您应该能够找到消息每个部分的文件名、内容类型和大小,而无需下载整个内容。

于 2012-12-01T21:31:00.543 回答
3

如果您对文件名有所了解,可以使用imap SEARCH 命令的 X-GM-RAW gmail 扩展名。这些扩展允许您使用任何gmail 高级搜索查询来过滤邮件。这样,您可以将下载限制为匹配的消息,或排除一些您不想要的消息。

mail.uid('search', None, 'X-GM-RAW', 
       'has:attachment filename:pdf in:inbox -label:parsed'))

上述搜索收件箱中未标记为“已解析”的带有 PDF 附件的邮件。

一些专业提示:

  • 标记您已经解析的消息,因此您不需要再次获取它们(上例中的 -label:parsed 过滤器)
  • 总是使用 uid 版本而不是标准的顺序 ids(你已经这样做了)
  • 不幸的是,MIME 很混乱:有很多客户做奇怪(或完全错误)的事情。您可以尝试仅下载和解析标头,但这值得吗?

[编辑]

如果在解析后标记消息,则可以跳过已解析的消息。这应该足够合理以监控您的班级邮箱。

也许您生活在互联网带宽比程序员时间更昂贵的世界角落;在这种情况下,您可以仅获取标题并查找“Content-disposition”==“attachment;filename=somefilename.ext”。

于 2012-12-01T21:01:33.357 回答
2

消息数据项的 FETCH 在RFC822功能上等同于BODY[]。IMAP4 支持其他消息数据项,列在RFC 3501 的 6.4.5 节中

尝试请求一组不同的消息数据项以获取您需要的信息。例如,您可以尝试RFC822.HEADER或可能BODY.PEEK[MIME]

于 2012-12-01T21:16:05.590 回答
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)}")

在这里测试正则表达式。

于 2017-06-27T20:26:00.740 回答