Python 和 imaplib:无需下载完整电子邮件即可获取附件名称或正文

Posted

技术标签:

【中文标题】Python 和 imaplib:无需下载完整电子邮件即可获取附件名称或正文【英文标题】:Python and imaplib: Obtain attachment names or body without downloading full email 【发布时间】:2012-11-30 15:11:35 【问题描述】:

我在 Django 中有一个电子邮件客户端。目前支持使用 imaplib 的 GMail 帐户。

我的问题是:我想获得附件名称而无需下载完整的电子邮件。目前,为了获取附件名称或邮件正文,我需要使用带有参数(RFC822)的 fetch 函数下载整个邮件。

我知道我只能使用 HEADER.FIELDS 获取特定字段,例如主题,来自,cc。但是有没有办法在不下载整个电子邮件的情况下获取附件名称或电子邮件正文?

我的具体意思是:假设我有一封 30Mb 的电子邮件,正文中有一行文本和两个 15Mb 的附件。我想在不下载完整的 30Mb 正文的情况下获取附件名称和那一行文本。

谢谢

【问题讨论】:

"...在不下载整个电子邮件的情况下获取 [] 电子邮件正文的方法" -> 没有您的意思的附件? 你知道BODYSTRUCTURE(BODY ENVELOPE)吗? @JonClements:不,IMAP 服务器可以为您解析正文并返回您想要的部分。您必须能够解析 BODYSTRUCTURE 才能知道要请求什么,然后您就可以做到。 @abarnert 是的,我发布了然后意识到可以做一些事情,大约在你发布的同一时间 - 所以我想我会删除它:) 编辑了问题以更好地详细说明问题。将调查 BODYSTRUCTURE 和 (BODY ENVELOPE)。 【参考方案1】:

假设你在问我认为你在问什么,这就是该怎么做:

首先,获取BODYSTRUCTURE。假设 gmail 的 IMAP 服务器支持这个,你会得到这样的结果:

(("TEXT" "PLAIN" ("CHARSET" "UTF-8") NIL NIL "QUOTED-PRINTABLE" 56 1 NIL NIL NIL NIL)
 ("TEXT" "html" ("CHARSET" "UTF-8") (NAME "") NIL NIL "BASE64" 12345 NIL 
  ("attachment" ("FILENAME" "")) NIL NIL) 
 ("IMG" "JPEG" (NAME "funny picture") NIL NIL "BASE64" 56789 NIL
  ("attachment" ("FILENAME" "image.jpg")) NIL NIL))
 "MIXED" ("BOUNDARY" "----_=_NextPart_001_1234ABCD.56789EF0") NIL NIL NIL)

然后获取(BODY ENVELOPE)是结构有一个。

如果您查看RFC3501 7.4.2,它会解释如何处理这些问题。

一旦您确定(BODY[1])(BODY[2]) 是主要内容的纯文本和HTML 版本,并且(BODY[3]) 是第一个真正的附件,您可以通过获取@ 下载纯文本正文987654328@,并且您已经从结构中获得了附件的名称。

抱歉,这里没有代码。我认为imaplib 或任何与stdlib MIME 和邮件相关的模块都不会为您完成困难的部分(解释结构),但我还没有真正检查过,所以我先看看那里,如果没有,去 PyPI 看看有没有其他人已经写过代码。

嗯,实际上,首先我会获取 BODYSTRUCTURE(BODY ENVELOPE)(BODY[3]) 以获得特定消息,以确保在编写一大堆代码之前,gmail 得到了完全的支持……

PS,如果最坏的情况出现在最坏的情况下,如果您的用例像您描述的那样简单和严格,您可以随时获取 BODYSTRUCTURE(BODY[1]),如果失败则回退到 RFC822,然后获取通过在结构上运行一个 hacky 正则表达式而不是真正的解析来命名附件名称。除了一次性脚本或快速而肮脏的原型来了解 gmail 之外,我不会写任何东西,但对于那些情况,我可能会。

【讨论】:

谢谢,这似乎正是我想要的。将在接下来的 5 分钟内进行测试,如果有效,则标记为正确答案。 已接受。谢谢你的努力【参考方案2】:

[编辑]

好的,我们开始 =)

>>> import imaplib, email
>>> mail = imaplib.IMAP4_SSL('imap.gmail.com')
>>> mail.login('emailaddr@gmail.com', 'password')
('OK', ['emailaddr@gmail.com Inget Namn authenticated (Success)'])
>>> mail.select('inbox')
('OK', ['14'])
>>> result, data = mail.uid('search', None, 'ALL')
>>> uids=data[0].split()
>>> result, data = mail.uid('fetch', uids[-1], 'BODYSTRUCTURE')
>>> print data
['14 (UID 340 BODYSTRUCTURE ((("TEXT" "PLAIN" ("CHARSET" "ISO-8859-1") NIL NIL "7BIT" 17 1 NIL NIL NIL)("TEXT" "HTML" ("CHARSET" "ISO-8859-1") NIL NIL "7BIT" 17 1 NIL NIL NIL) "ALTERNATIVE" ("BOUNDARY" "20cf3071d16a5a877b04d0adcc43") NIL NIL)("APPLICATION" "PDF" ("NAME" "attiny40.pdf") NIL NIL "BASE64" 8429956 NIL ("ATTACHMENT" ("FILENAME" "attiny40.pdf")) NIL) "MIXED" ("BOUNDARY" "20cf3071d16a5a878104d0adcc45") NIL NIL))']
>>>

此消息的附件名为“attiny40.pdf”,您可以在 BODYSTRUCTURE 中清楚地看到该名称。剩下的就是解析 BODYSTRUCTURE。

代码几乎直接取自下面的最后一个链接。

[/编辑]

您需要将 fetch 的参数从 RFC822 更改为 BODYSTRUCTURE。

然后以here 为例。

例如,由两部分组成的消息,一个文本和一个 BASE64 编码的文本附件的正文结构可以是: (("TEXT" "PLAIN" ("CHARSET" "US-ASCII") NIL NIL "7BIT" 1152 23)("TEXT" "PLAIN" ("CHARSET" "US-ASCII" "NAME" "cc.diff") "960723163407.20117h@cac.washington.edu" "编译器差异" "BASE64" 4554 73) "混合")

另见this post 和this one。最后一个链接看起来就像你正在尝试做的一样。

【讨论】:

非常好的和完整的答案,但必须接受另一位发帖人的答案,因为它早了大约 10 分钟并且同样完整。但感谢您的广泛努力。这也是我想要的 100%。

以上是关于Python 和 imaplib:无需下载完整电子邮件即可获取附件名称或正文的主要内容,如果未能解决你的问题,请参考以下文章

imaplib/gmail 如何在不标记为已读的情况下下载完整消息(所有部分)[重复]

只获取新的电子邮件 imaplib 和 python

使用 Python 和 imaplib 在 GMail 中移动电子邮件

Python imaplib 使用日期和时间搜索电子邮件

使用imaplib下载多个附件

具有多个条件的python imaplib搜索