《Python 编程快速上手 — 让繁琐工作自动化》读书笔记之【第16章 发送电子邮件和短信(2)】

时间:2022-04-25 22:49:39

1.  IMAP

IMAP:Internet Mail Access ProtocolInternet邮件访问协议。规定了如何与邮件提供商的服务器通信,取回发送到你的邮箱的的电子邮件。Python自带一个imaplib模块,但是第三方的imapclient更易用。然后使用pyzmail模块对邮件进行解析。

 2.  用 IMAP 获取和删除电子邮件

1)  连接到 IMAP 服务器

就像你需要一个 SMTP 对象连接到 SMTP 服务器并发送电子邮件一样,你需要一个 IMAPClient 对象,连接到 IMAP 服务器并接收电子邮件。使用imapclient.IMAPClient()函数,创建一个IMAPClient对象。同时,大多数电子邮件提供商要求 SSL 加密,传入 SSL= TRUE 关键字参数。示例:

>>>import imapclient

>>>imapObj = imapclient.IMAPClient('imap.qq.com', ssl=True)

2)  登录到 IMAP 服务器

取得IMAP对象后,调用它的login()方法,传入用户名和密码进行登陆。示例:

>>> imapObj.login('XXX@qq.com','QQ邮箱授权码')

b'Success login ok'

3)  搜索电子邮件

登录后,实际获取你感兴趣的电子邮件分为两步。首先,必须选择要搜索的文件夹。然后,必须调用 IMAPClient 对象的search()方法,传入 IMAP 搜索关键词字符串。

4)  选择文件夹

几乎每个账户默认都有一个INBOX 文件夹,但也可以调用 IMAPClient 对象的list_folders()方法,获取文件夹列表。这将返回一个元组的列表。每个元组包含一个文件夹的信息。

>>>import pprint

>>>pprint.pprint(imapObj.list_ _folders())

[((b'\\NoSelect',b'\\HasChildren'), b'/', '其他文件夹'),

 ((b'\\HasNoChildren',), b'/', 'INBOX'),

 ((b'\\HasNoChildren',), b'/', 'SentMessages'),

 ((b'\\HasNoChildren',), b'/', 'Drafts'),

 ((b'\\HasNoChildren',), b'/', 'DeletedMessages'),

 ((b'\\HasNoChildren',), b'/', 'Junk'),

 ((b'\\HasNoChildren',), b'/', '其他文件夹/QQ邮件订阅')]

要选择一个文件夹进行搜索,就调用 IMAPClient 对象的select_folder()方法,传入该文件夹的名称字符串。示例:

imapObj.select_folder('INBOX', readonly=True)

5)  执行搜索

文件夹选中后,就可以用IMAPClient 对象的 search()方法搜索电子邮件。search()的参数是一个字符串列表,每一个格式化为 IMAP 搜索键。示例:

 
>>>imapObj.search(['ALL'])
[380, 381, 382, 383, 384, 385, 400, 519,539, 540, 541, 542, 543, 544]

search()函数返回邮件的唯一ID(UID),然后,可以将这些 UID传入 fetch()方法,获得邮件内容。

6)  大小限制

如果你的搜索匹配大量的电子邮件,Python可能抛出异常 imaplib.error: got more than 10000 bytes。如果发生这种情况,必须断开并重连 IMAP 服务器,然后再试。这个限制是防止Python 程序消耗太多内存。遗憾的是,默认大小限制往往太小。可以执行下面的代码,将限制从 10000 字节改为 10000000 字节:

>>> import imaplib

>>> imaplib._ _MAXLINE = 10000000

这应该能避免该错误消息再次出现。也许要在你写的每一个 IMAP 程序中加上这两行。

7)  取邮件并标记为已读

得到UID的列表后,可以调用imapclient.fetch()方法获得实际的电子邮件内容。UID 列表是 fetch()的第一个参数。第二个参数应该是['BODY[]'],它告诉 fetch()下载 UID 列表中指定电子邮件的所有正文内容。示例:

>>>rawMessages = imapObj.fetch(UIDs, ['BODY[]'])

>>>import pprint

>>>pprint.pprint(rawMessages)

defaultdict(<class'dict'>,

    {519: {b'BODY[]': b'Received: frommail.paymentwall.com (unknown'

                      b'[216.127.71.105])\r\n\tby newmx1000.qq.com (Ne'

                        ......

                      b'olor:#333;">=E6=9C=8D=E5=8A=A1=\r\n=E6=9D=A1='

                     b'E6=AC=BE</a></td></tr></tbody></table></td><'

                     b'/tr></tbody></tabl=\r\ne></td></tr></tbody></t'

                     b'able></body></html>\r\n',

           b'SEQ': 1}})

如果希望在获取邮件的时候,就需要将 readonly=False 传入 select_folder()。示例:

>>>imapObj.select_folder(‘INBOX’,readonly= False)

8) 从原始消息中获取电子邮件地址

调用pyzmail.PeekMessage.factory()函数,传入原始邮件的'BODY[]'部分可以获得PyzMessage对象。示例:

>>>message.get_ _subject()

'Hello!'

>>>message.get_ _addresses('from')

[('EdwardSnowden', 'esnowden@nsa.gov')]

>>>message.get_ _addresses('to')

[(Jane Doe','my_email_address@gmail.com')]

>>>message.get_ _addresses('cc')

[]

>>>message.get_ _addresses('bcc')

[]

在这里,‘cc’表示抄送,‘bcc’表示密送。

9) 从原始消息中获取正文

如果电子邮件仅仅是纯文本,它的PyzMessage对象会将html_part 属性设为None。同样,如果电子邮件只是HTML,它的PyzMessage 对象会将text_part 属性设为None。

text_part 或 html_part 将有一个 get_payload()方法,将电子邮件的正文返回为 bytes 数据类型。对 get_payload()返回的 bytes 值调用 decode()方法。decode()方法接受一个参数:这条消息的字符编码,保存在 text_part.charset或 html_part.charset 属性中。最后,这返回了邮件正文的字符串。示例:

>>>message.text_ _part != None

True

>>>message.text_ _part.get_ _payload().decode(message.text_ _part.charset)

'So long, andthanks for all the fish!\r\n\r\n-Al\r\n'

>>>message.html_ _part != None

True

>>>message.html_ _part.get_ _payload().decode(message.html_ _part.charset)

'<divdir="ltr"><div>So long, and thanks for all thefish!<br><br></div>-Al

<br></div>\r\n'

10)删除电子邮件

向 IMAPClient.delete_messages()方法传入一个消息UID 的列表。这为电子邮件加上\Deleted 标志。调用 expunge()方法,将永久删除当前选中的文件夹中带\Deleted 标志的所有电子邮件。示例:

>>>imapObj.select_ _folder('INBOX', readonly=False)

>>>UIDs = imapObj.search(['ON 09-Jul-2015'])

>>>UIDs

[40066]

>>>imapObj.delete_ _messages(UIDs)

{40066:('\\Seen', '\\Deleted')}

>>>imapObj.expunge()

('Success',[(5452, 'EXISTS')])

11) 从 IMAP 服务器断开

如果程序已经完成了获取和删除电子邮件,就调用 IMAPClient 的 logout()方法,从 IMAP 服务器断开连接。示例:

>>> imapObj.logout()