Purpose: Client library for IMAP4 communication.

imaplib implements clients for communicating with Internet Message Access Protocol (IMAP) version 4 servers. The IMAP protocol defines a set of commands for sending to the server and for passing responses back to the client. Most commands are available as methods of the IMAP4 object used to communicate with the server.

These examples discuss parts of the IMAP protocol, but are by no means complete. See RFC 3501 for complete details.

Variety

There are three client classes for communicating with the server using various mechanisms. The first IMAP4 uses plaintext sockets; IMAP4_SSL uses encrypted communication over SSL sockets; and IMAP4_stream uses standard input and standard output of external commands. All examples here will use IMAP4_SSL, but the API for other classes is similar.

connect to the server

There are two steps to establishing a connection with an IMAP server. First, set up the socket connection itself. Second, authenticate as the user with an account on the server. The following sample code will read server and user information from a configuration file.

imaplib_connect.py

import imaplib
import configparser
import os

def open_connection(verbose=False):
     # Read the config file
     config = configparser. ConfigParser()
     config.read([os.path.expanduser('~/.pymotw')])

     # Connect to the server
     hostname = config.get('server', 'hostname')
     if verbose:
         print('Connecting to', hostname)
     connection = imaplib.IMAP4_SSL(hostname)

     # Login to our account
     username = config. get('account', 'username')
     password = config. get('account', 'password')
     if verbose:
         print('Logging in as', username)
     connection. login(username, password)
     return connection

if __name__ == '__main__':
     with open_connection(verbose=True) as c:
         print(c)

When run, open_connection() reads configuration information from a file in the user's home directory, then opens an IMAP4_SSL connection and authenticates it.

$ python3 imaplib_connect.py

Connecting to pymotw.hellfly.net
Logging in as an example
<imaplib.IMAP4_SSL object at 0x10421e320>

Other examples in this section reuse this module to avoid duplicating code.

verification failed

An exception is thrown if a connection is established but authentication fails.

imaplib_connect_fail.py

import imaplib
import configparser
import os

# Read the config file
config = configparser. ConfigParser()
config.read([os.path.expanduser('~/.pymotw')])

# Connect to the server
hostname = config.get('server', 'hostname')
print('Connecting to', hostname)
connection = imaplib.IMAP4_SSL(hostname)

# Login to our account
username = config. get('account', 'username')
password = 'this_is_the_wrong_password'
print('Logging in as', username)
try:
     connection. login(username, password)
except Exception as err:
     print('ERROR:', err)

This example intentionally uses a wrong password to trigger an exception.

$ python3 imaplib_connect_fail.py

Connecting to pymotw.hellfly.net
Logging in as an example
ERROR: b'[AUTHENTICATIONFAILED] Authentication failed.'

Example configuration

This example account has multiple mailboxes in the hierarchy:

  • INBOX inbox
  • Deleted Messages Deleted messages
  • Archive
  • Example example
    • 2016

There is one unread message in INBOX folder and one unread message in Example/2016.

list emails

To retrieve the mailboxes available for an account, use the list() method.

imaplib_list.py

import imaplib
from pprint import pprint
from imaplib_connect import open_connection

with open_connection() as c:
     typ, data = c.list()
     print('Response code:', typ)
     print('Response:')
     pprint(data)

The return value is a tuple containing the response code and the data returned by the server. The response code is OK unless an error occurred. The data for list() is a sequence of strings containing the flag, hierarchy separator and mailbox name* for each mailbox.

$ python3 imaplib_list.py

Response code: OK
Response:
[b'(.HasChildren) "." Example',
  b'(.HasNoChildren) "." Example.2016',
  b'(.HasNoChildren) "." Archive',
  b'(.HasNoChildren) "." "Deleted Messages"',
  b'(.HasNoChildren) "." INBOX']

Each response string can use re or [csv (see IMAP backup script in reference at end of this section , for an example using csv).

imaplib_list_parse.py

import imaplib
import re

from imaplib_connect import open_connection

list_response_pattern = re.compile(
     r'.(?P<flags>.*?). "(?P<delimiter>.*)" (?P<name>.*)'
)

def parse_list_response(line):
     match = list_response_pattern.match(line.decode('utf-8'))
     flags, delimiter, mailbox_name = match.groups()
     mailbox_name = mailbox_name. strip('"')
     return (flags, delimiter, mailbox_name)

with open_connection() as c:
     typ, data = c.list()
print('Response code:', typ)

for line in data:
     print('Server response:', line)
     flags, delimiter, mailbox_name = parse_list_response(line)
     print('Parsed response:', (flags, delimiter, mailbox_name))

If the server name contains spaces, the server quotes the mailbox name, but later on, those quotes need to be removed so that the mailbox name can be used in other calls back to the server.

$ python3 imaplib_list_parse.py

Response code: OK
Server response: b'(.HasChildren) "." Example'
Parsed response: ('.HasChildren', '.', 'Example')
Server response: b'(.HasNoChildren) "." Example.2016'
Parsed response: ('.HasNoChildren', '.', 'Example.2016')
Server response: b'(.HasNoChildren) "." Archive'
Parsed response: ('.HasNoChildren', '.', 'Archive')
Server response: b'(.HasNoChildren) "." "Deleted Messages"'
Parsed response: ('.HasNoChildren', '.', 'Deleted Messages')
Server response: b'(.HasNoChildren) "." INBOX'
Parsed response: ('.HasNoChildren', '.', 'INBOX')

list() takes an argument to specify the mailboxes in the partial hierarchy. For example, to list the subfolders of Example, pass "Example" as the directory parameter.

imaplib_list_subfolders.py

import imaplib

from imaplib_connect import open_connection

with open_connection() as c:
     typ, data = c. list(directory='Example')

print('Response code:', typ)

for line in data:
     print('Server response:', line)

Return parent and subfolders.

$ pythonn3 imaplib_list_subfolders.py

Response code: OK
Server response: b'(.HasChildren) "." Example'
Server response: b'(.HasNoChildren) "." Example.2016'

Alternatively, to list folders matching a pattern, pass the pattern argument.

imaplib_list_pattern.py

import imaplib

from imaplib_connect import open_connection

with open_connection() as c:
     typ, data = c.list(pattern='*Example*')

print('Response code:', typ)

for line in data:
     print('Server response:', line)

In this case, both Example and Example.2016 are included in the response.

$ python3 imaplib_list_pattern.py

Response code: OK
Server response: b'(.HasChildren) "." Example'
Server response: b'(.HasNoChildren) "." Example.2016'

Mailbox Status

Use status() to ask for aggregated information about the content. The following table lists the status conditions defined by the standard.

IMAP 4 Mailbox Status Conditions

condition meaning
MESSAGES The number of messages in the mailbox.
RECENT The number of messages with the .ecent flag set.
UIDNEXT The next unique identifier value for the mailbox.
UIDVALIDITY The unique identifier validity value for the mailbox.
UNSEEN The number of messages without the .een flag set.

The format of the status condition must be a space-separated string within parentheses, which is the encoding for a "list" in the IMAP4 specification. Mailbox names are wrapped in " in case any names contain spaces or other characters that might throw off the parser.

imaplib_status.py

import imaplib
import re

from imaplib_connect import open_connection
from imaplib_list_parse import parse_list_response

with open_connection() as c:
     typ, data = c.list()
     for line in data:
         flags, delimiter, mailbox = parse_list_response(line)
         print('Mailbox:', mailbox)
         status = c. status(
             '"{}"'. format(mailbox),
             '(MESSAGES RECENT UIDNEXT UIDVALIDITY UNSEEN)',
         )
         print(status)

The return value is the usual tuple containing the response code and a list of messages from the server. In this case, the list consists of a single string formatted as the mailbox name in quotation marks, followed by the status condition and value in parentheses.

$ python3 imaplib_status.py

Response code: OK
Server response: b'(.HasChildren) "." Example'
Parsed response: ('.HasChildren', '.', 'Example')
Server response: b'(.HasNoChildren) "." Example.2016'
Parsed response: ('.HasNoChildren', '.', 'Example.2016')
Server response: b'(.HasNoChildren) "." Archive'
Parsed response: ('.HasNoChildren', '.', 'Archive')
Server response: b'(.HasNoChildren) "." "Deleted Messages"'
Parsed response: ('.HasNoChildren', '.', 'Deleted Messages')
Server response: b'(.HasNoChildren) "." INBOX'
Parsed response: ('.HasNoChildren', '.', 'INBOX')
Mailbox: Example
('OK', [b'Example (MESSAGES 0 RECENT 0 UIDNEXT 2 UIDVALIDITY 145
7297771 UNSEEN 0)'])
Mailbox: Example.2016
('OK', [b'Example.2016 (MESSAGES 1 RECENT 0 UIDNEXT 3 UIDVALIDIT
Y 1457297772 UNSEEN 0)'])
Mailbox: Archive
('OK', [b'Archive (MESSAGES 0 RECENT 0 UIDNEXT 1 UIDVALIDITY 145
7297770 UNSEEN 0)'])
Mailbox: Deleted Messages
('OK', [b'"Deleted Messages" (MESSAGES 3 RECENT 0 UIDNEXT 4 UIDV
ALIDITY 1457297773 UNSEEN 0)'])
Mailbox: INBOX
('OK', [b'INBOX (MESSAGES 2 RECENT 0 UIDNEXT 6 UIDVALIDITY 14572
97769 UNSEEN 1)'])

Select a mailbox

Once the client is authenticated, the basic mode of operation is to select a mailbox and then ask the server for information about the messages in the mailbox. Connections are stateful, so after a mailbox is selected, all commands operate on messages in that mailbox until a new mailbox is selected.

imaplib_select.py

import imaplib
import imaplib_connect

with imaplib_connect.open_connection() as c:
     typ, data = c. select('INBOX')
     print(typ, data)
     num_msgs = int(data[0])
     print('There are {} messages in INBOX'. format(num_msgs))

The response data contains the total number of messages in the mailbox.

$ python3 imaplib_select.py

OK [b'1']
There are 1 messages in INBOX

If an invalid mailbox is specified, the response code is NO.

imaplib_select_invalid.py

import imaplib
import imaplib_connect

with imaplib_connect.open_connection() as c:
     typ, data = c. select('Does-Not-Exist')
     print(typ, data)

The data contains an error message describing the problem.

$ python3 imaplib_select_invalid.py

NO [b"Mailbox doesn't exist: Does-Not-Exist"]

Search messages

After selecting a mailbox, use search() to retrieve the message IDs in the mailbox.

imaplib_search_all.py

import imaplib
import imaplib_connect
from imaplib_list_parse import parse_list_response

with imaplib_connect.open_connection() as c:
     typ, mbox_data = c.list()
     for line in mbox_data:
         flags, delimiter, mbox_name = parse_list_response(line)
         c.select('"{}"'.format(mbox_name), readonly=True)
         typ, msg_ids = c. search(None, 'ALL')
         print(mbox_name, typ, msg_ids)

Message IDs are assigned by the server and are implementation dependent. The IMAP4 protocol distinguishes between a message sequence ID and a message's UID identifier at a given point in time during a transaction, but not all servers implement both.

$ python3 imaplib_search_all.py

Response code: OK
Server response: b'(.HasChildren) "." Example'
Parsed response: ('.HasChildren', '.', 'Example')
Server response: b'(.HasNoChildren) "." Example.2016'
Parsed response: ('.HasNoChildren', '.', 'Example.2016')
Server response: b'(.HasNoChildren) "." Archive'
Parsed response: ('.HasNoChildren', '.', 'Archive')
Server response: b'(.HasNoChildren) "." "Deleted Messages"'
Parsed response: ('.HasNoChildren', '.', 'Deleted Messages')
Server response: b'(.HasNoChildren) "." INBOX'
Parsed response: ('.HasNoChildren', '.', 'INBOX')
Example OK [b'']
Example.2016 OK [b'1']
Archive OK [b'']
Deleted Messages OK [b'']
INBOX OK [b'1']

In this case, INBOX and Example.2016 each have a different message with ID 1. Other mailboxes are empty.

search condition

Various other search criteria can be used, including the date the message was viewed, flags and other titles. See RFC 3501 section 6.4.4 for full details.

To find messages with 'Example message 2' in a topic, the search criteria should be structured as:

(SUBJECT "Example message 2")

This example finds all messages titled 'Example message 2' in all mailboxes:

imaplib_search_subject.py

import imaplib
import imaplib_connect
from imaplib_list_parse import parse_list_response

with imaplib_connect.open_connection() as c:
    typ, mbox_data = c.list()
    for line in mbox_data:
        flags, delimiter, mbox_name = parse_list_response(line)
        c.select('"{}"'.format(mbox_name), readonly=True)
        typ, msg_ids = c.search(
            None,
            '(SUBJECT "Example message 2")',
        )
        print(mbox_name, typ, msg_ids)

帐户中只有一条这样的消息,它在 INBOX 中。

$ python3 imaplib_search_subject.py

Response code: OK
Server response: b'(.HasChildren) "." Example'
Parsed response: ('.HasChildren', '.', 'Example')
Server response: b'(.HasNoChildren) "." Example.2016'
Parsed response: ('.HasNoChildren', '.', 'Example.2016')
Server response: b'(.HasNoChildren) "." Archive'
Parsed response: ('.HasNoChildren', '.', 'Archive')
Server response: b'(.HasNoChildren) "." "Deleted Messages"'
Parsed response: ('.HasNoChildren', '.', 'Deleted Messages')
Server response: b'(.HasNoChildren) "." INBOX'
Parsed response: ('.HasNoChildren', '.', 'INBOX')
Example OK [b'']
Example.2016 OK [b'']
Archive OK [b'']
Deleted Messages OK [b'']
INBOX OK [b'1']

搜索条件也可以组合。

imaplib_search_from.py

import imaplib
import imaplib_connect
from imaplib_list_parse import parse_list_response

with imaplib_connect.open_connection() as c:
    typ, mbox_data = c.list()
    for line in mbox_data:
        flags, delimiter, mbox_name = parse_list_response(line)
        c.select('"{}"'.format(mbox_name), readonly=True)
        typ, msg_ids = c.search(
            None,
            '(FROM "Doug" SUBJECT "Example message 2")',
        )
        print(mbox_name, typ, msg_ids)

这些标准与逻辑  and  运算结合在一起。

$ python3 imaplib_search_from.py

Response code: OK
Server response: b'(.HasChildren) "." Example'
Parsed response: ('.HasChildren', '.', 'Example')
Server response: b'(.HasNoChildren) "." Example.2016'
Parsed response: ('.HasNoChildren', '.', 'Example.2016')
Server response: b'(.HasNoChildren) "." Archive'
Parsed response: ('.HasNoChildren', '.', 'Archive')
Server response: b'(.HasNoChildren) "." "Deleted Messages"'
Parsed response: ('.HasNoChildren', '.', 'Deleted Messages')
Server response: b'(.HasNoChildren) "." INBOX'
Parsed response: ('.HasNoChildren', '.', 'INBOX')
Example OK [b'']
Example.2016 OK [b'']
Archive OK [b'']
Deleted Messages OK [b'']
INBOX OK [b'1']

获取消息

search() 返回的标识符用于检索消息的内容或部分内容,以便使用 fetch() 方法进行进一步处理。它有两个参数,要获取的消息I D 和要检索的消息部分。

“ message_ids” 参数是逗号分隔的 ID(例如“ 1”,“ 1,2””或 ID 范围(例如“ 1:2”)列表。 message_parts 参数是消息段名称的IMAP列表。如同search()的搜索条件一样,IMAP 协议指定了命名的消息段,因此客户端可以有效地仅检索他们实际需要的消息部分。例如,要检索邮箱中邮件的标题,请使用带有参数BODY.PEEK [HEADER] 的 fetch()

注意

另一种获取标头的方法是 BODY [HEADERS] ,但是这种形式具有将消息隐式标记为已读的副作用,这在许多情况下是不希望的。

imaplib_fetch_raw.py

import imaplib
import pprint
import imaplib_connect

imaplib.Debug = 4
with imaplib_connect.open_connection() as c:
    c.select('INBOX', readonly=True)
    typ, msg_data = c.fetch('1', '(BODY.PEEK[HEADER] FLAGS)')
    pprint.pprint(msg_data)

fetch() 的返回值已经部分解析,因此使用起来比  list() 的返回值难一些。打开调试将显示客户端和服务器之间的完整交互,以了解为什么会这样。

$ python3 imaplib_fetch_raw.py

  19:40.68 imaplib version 2.58
  19:40.68 new IMAP4 connection, tag=b'IIEN'
  19:40.70 < b'* OK [CAPABILITY IMAP4rev1 LITERAL+ SASL-IR LOGIN
-REFERRALS ID ENABLE IDLE AUTH=PLAIN] Dovecot (Ubuntu) ready.'
  19:40.70 > b'IIEN0 CAPABILITY'
  19:40.73 < b'* CAPABILITY IMAP4rev1 LITERAL+ SASL-IR LOGIN-REF
ERRALS ID ENABLE IDLE AUTH=PLAIN'
  19:40.73 < b'IIEN0 OK Pre-login capabilities listed, post-logi
n capabilities have more.'
  19:40.73 CAPABILITIES: ('IMAP4REV1', 'LITERAL+', 'SASL-IR', 'L
OGIN-REFERRALS', 'ID', 'ENABLE', 'IDLE', 'AUTH=PLAIN')
  19:40.73 > b'IIEN1 LOGIN example "TMFw00fpymotw"'
  19:40.79 < b'* CAPABILITY IMAP4rev1 LITERAL+ SASL-IR LOGIN-REF
ERRALS ID ENABLE IDLE SORT SORT=DISPLAY THREAD=REFERENCES THREAD
=REFS THREAD=ORDEREDSUBJECT MULTIAPPEND URL-PARTIAL CATENATE UNS
ELECT CHILDREN NAMESPACE UIDPLUS LIST-EXTENDED I18NLEVEL=1 CONDS
TORE QRESYNC ESEARCH ESORT SEARCHRES WITHIN CONTEXT=SEARCH LIST-
STATUS SPECIAL-USE BINARY MOVE'
  19:40.79 < b'IIEN1 OK Logged in'
  19:40.79 > b'IIEN2 EXAMINE INBOX'
  19:40.82 < b'* FLAGS (.Answered .Flagged .Deleted .Seen .
Draft)'
  19:40.82 < b'* OK [PERMANENTFLAGS ()] Read-only mailbox.'
  19:40.82 < b'* 2 EXISTS'
  19:40.82 < b'* 0 RECENT'
  19:40.82 < b'* OK [UNSEEN 1] First unseen.'
  19:40.82 < b'* OK [UIDVALIDITY 1457297769] UIDs valid'
  19:40.82 < b'* OK [UIDNEXT 6] Predicted next UID'
  19:40.82 < b'* OK [HIGHESTMODSEQ 20] Highest'
  19:40.82 < b'IIEN2 OK [READ-ONLY] Examine completed (0.000 sec
s).'
  19:40.82 > b'IIEN3 FETCH 1 (BODY.PEEK[HEADER] FLAGS)'
  19:40.86 < b'* 1 FETCH (FLAGS () BODY[HEADER] {3108}'
  19:40.86 read literal size 3108
  19:40.86 < b')'
  19:40.89 < b'IIEN3 OK Fetch completed.'
  19:40.89 > b'IIEN4 LOGOUT'
  19:40.93 < b'* BYE Logging out'
  19:40.93 BYE response: b'Logging out'
[(b'1 (FLAGS () BODY[HEADER] {3108}',
  b'Return-Path: <doug@doughellmann.com>..Received: from compu
te4.internal ('
  b'compute4.nyi.internal [10.202.2.44])... by sloti26t01 (Cy
rus 3.0.0-beta1'
  b'-git-fastmail-12410) with LMTPA;... Sun, 06 Mar 2016 16:1
6:03 -0500.'
  b'.X-Sieve: CMU Sieve 2.4..X-Spam-known-sender: yes, fadd1c
f2-dc3a-4984-a0'
  b'8b-02cef3cf1221="doug",..  ea349ad0-9299-47b5-b632-6ff1e39
4cc7d="both he'
  b'llfly"..X-Spam-score: 0.0..X-Spam-hits: ALL_TRUSTED -1,
BAYES_00 -1.'
  b'9, LANGUAGES unknown, BAYES_USED global,..  SA_VERSION 3.3
.2..X-Spam'
  b"-source: IP='127.0.0.1', Host='unk', Country='unk', FromHead
er='com',.. "
  b" MailFrom='com'..X-Spam-charsets: plain='us-ascii'..X-Re
solved-to: d"
  b'oughellmann@fastmail.fm..X-Delivered-to: doug@doughellmann
.com..X-Ma'
  b'il-from: doug@doughellmann.com..Received: from mx5 ([10.20
2.2.204]).'
  b'.  by compute4.internal (LMTPProxy); Sun, 06 Mar 2016 16:16
:03 -0500..Re'
  b'ceived: from mx5.nyi.internal (localhost [127.0.0.1])...b
y mx5.nyi.inter'
  b'nal (Postfix) with ESMTP id 47CBA280DB3...for <doug@dough
ellmann.com>; S'
  b'un,  6 Mar 2016 16:16:03 -0500 (EST)..Received: from mx5.n
yi.internal (l'
  b'ocalhost [127.0.0.1])..    by mx5.nyi.internal (Authentica
tion Milter) w'
  b'ith ESMTP..    id A717886846E.30BA4280D81;..    Sun, 6 M
ar 2016 16:1'
  b'6:03 -0500..Authentication-Results: mx5.nyi.internal;..
   dkim=pass'
  b' (1024-bit rsa key) header.d=messagingengine.com header.i=@m
essagingengi'
  b'ne.com header.b=Jrsm+pCo;..    x-local-ip=pass..Received
: from mailo'
  b'ut.nyi.internal (gateway1.nyi.internal [10.202.2.221])...
(using TLSv1.2 '
  b'with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits))..
t(No client cer'
  b'tificate requested)...by mx5.nyi.internal (Postfix) with
ESMTPS id 30BA4'
  b'280D81...for <doug@doughellmann.com>; Sun,  6 Mar 2016 16
:16:03 -0500 (E'
  b'ST)..Received: from compute2.internal (compute2.nyi.intern
al [10.202.2.4'
  b'2])...by mailout.nyi.internal (Postfix) with ESMTP id 174
0420D0A...f'
  b'or <doug@doughellmann.com>; Sun,  6 Mar 2016 16:16:03 -0500
(EST)..Recei'
  b'ved: from frontend2 ([10.202.2.161])..  by compute2.intern
al (MEProxy); '
  b'Sun, 06 Mar 2016 16:16:03 -0500..DKIM-Signature: v=1; a=rs
a-sha1; c=rela'
  b'xed/relaxed; d=...messagingengine.com; h=content-transfer
-encoding:conte'
  b'nt-type...:date:from:message-id:mime-version:subject:to:x
-sasl-enc..'
  b'.:x-sasl-enc; s=smtpout; bh=P98NTsEo015suwJ4gk71knAWLa4=; b
=Jrsm+...'
  b'pCovRIoQIRyp8Fl0L6JHOI8sbZy2obx7O28JF2iTlTWmX33Rhlq9403XRklw
N3JA...7KSPq'
  b'MTp30Qdx6yIUaADwQqlO+QMuQq/QxBHdjeebmdhgVfjhqxrzTbSMww/ZNhL
r..Ywv/QM/oDH'
  b'bXiLSUlB3Qrg+9wsE/0jU/EOisiU=..X-Sasl-enc: 8ZJ+4ZRE8AGPzdL
RWQFivGymJb8pa'
  b'4G9JGcb7k4xKn+I 1457298962..Received: from [192.168.1.14]
(75-137-1-34.d'
  b'hcp.nwnn.ga.charter.com [75.137.1.34])...by mail.messagin
gengine.com (Po'
  b'stfix) with ESMTPA id C0B366801CD...for <doug@doughellman
n.com>; Sun,  6'
  b' Mar 2016 16:16:02 -0500 (EST)..From: Doug Hellmann <doug@
doughellmann.c'
  b'om>..Content-Type: text/plain; charset=us-ascii..Content
-Transfer-En'
  b'coding: 7bit..Subject: PyMOTW Example message 2..Message
-Id: <00ABCD'
  b'46-DADA-4912-A451-D27165BC3A2F@doughellmann.com>..Date: Su
n, 6 Mar 2016 '
  b'16:16:02 -0500..To: Doug Hellmann <doug@doughellmann.com>
r.Mime-Vers'
  b'ion: 1.0 (Mac OS X Mail 9.2 .(3112.))..X-Mailer: Apple M
ail (2.3112)'
  b'....'),
 b')']

 FETCH 命令的响应从标志开始,然后指示头数据有595字节。 客户端使用消息的响应构造一个元组,然后用包含右括号 (“)”) 的单个字符串关闭该序列,服务器在获取响应的末尾发送该字符串。 由于采用这种格式,可能更容易分别获取不同的信息,或者重新组合响应并在客户端中对其进行解析。

imaplib_fetch_separately.py

import imaplib
import pprint
import imaplib_connect

with imaplib_connect.open_connection() as c:
    c.select('INBOX', readonly=True)

    print('HEADER:')
    typ, msg_data = c.fetch('1', '(BODY.PEEK[HEADER])')
    for response_part in msg_data:
        if isinstance(response_part, tuple):
            print(response_part[1])

    print('.BODY TEXT:')
    typ, msg_data = c.fetch('1', '(BODY.PEEK[TEXT])')
    for response_part in msg_data:
        if isinstance(response_part, tuple):
            print(response_part[1])

    print('.FLAGS:')
    typ, msg_data = c.fetch('1', '(FLAGS)')
    for response_part in msg_data:
        print(response_part)
        print(imaplib.ParseFlags(response_part))

分别获取值还有一个额外的好处,就是易于使用  ParseFlags() 从响应中解析标志。

$ python3 imaplib_fetch_separately.py

HEADER:
b'Return-Path: <doug@doughellmann.com>..Received: from compute
4.internal (compute4.nyi.internal [10.202.2.44])... by sloti2
6t01 (Cyrus 3.0.0-beta1-git-fastmail-12410) with LMTPA;... Su
n, 06 Mar 2016 16:16:03 -0500..X-Sieve: CMU Sieve 2.4..X-Spa
m-known-sender: yes, fadd1cf2-dc3a-4984-a08b-02cef3cf1221="doug"
,..  ea349ad0-9299-47b5-b632-6ff1e394cc7d="both hellfly"..X-
Spam-score: 0.0..X-Spam-hits: ALL_TRUSTED -1, BAYES_00 -1.9, L
ANGUAGES unknown, BAYES_USED global,..  SA_VERSION 3.3.2..X-
Spam-source: IP=.127.0.0.1., Host=.unk., Country=.unk., Fr
omHeader=.com.,..  MailFrom=.com...X-Spam-charsets: plai
n=.us-ascii...X-Resolved-to: doughellmann@fastmail.fm..X-D
elivered-to: doug@doughellmann.com..X-Mail-from: doug@doughell
mann.com..Received: from mx5 ([10.202.2.204])..  by compute4
.internal (LMTPProxy); Sun, 06 Mar 2016 16:16:03 -0500..Receiv
ed: from mx5.nyi.internal (localhost [127.0.0.1])...by mx5.ny
i.internal (Postfix) with ESMTP id 47CBA280DB3...for <doug@do
ughellmann.com>; Sun,  6 Mar 2016 16:16:03 -0500 (EST)..Receiv
ed: from mx5.nyi.internal (localhost [127.0.0.1])..    by mx5.
nyi.internal (Authentication Milter) with ESMTP..    id A71788
6846E.30BA4280D81;..    Sun, 6 Mar 2016 16:16:03 -0500..Auth
entication-Results: mx5.nyi.internal;..    dkim=pass (1024-bit
 rsa key) header.d=messagingengine.com header.i=@messagingengine
.com header.b=Jrsm+pCo;..    x-local-ip=pass..Received: from
 mailout.nyi.internal (gateway1.nyi.internal [10.202.2.221])..
.(using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/25
6 bits))...(No client certificate requested)...by mx5.nyi.
internal (Postfix) with ESMTPS id 30BA4280D81...for <doug@dou
ghellmann.com>; Sun,  6 Mar 2016 16:16:03 -0500 (EST)..Receive
d: from compute2.internal (compute2.nyi.internal [10.202.2.42])
r..by mailout.nyi.internal (Postfix) with ESMTP id 1740420D0A
r..for <doug@doughellmann.com>; Sun,  6 Mar 2016 16:16:03 -050
0 (EST)..Received: from frontend2 ([10.202.2.161])..  by com
pute2.internal (MEProxy); Sun, 06 Mar 2016 16:16:03 -0500..DKI
M-Signature: v=1; a=rsa-sha1; c=relaxed/relaxed; d=...messagi
ngengine.com; h=content-transfer-encoding:content-type...:dat
e:from:message-id:mime-version:subject:to:x-sasl-enc...:x-sas
l-enc; s=smtpout; bh=P98NTsEo015suwJ4gk71knAWLa4=; b=Jrsm+...
pCovRIoQIRyp8Fl0L6JHOI8sbZy2obx7O28JF2iTlTWmX33Rhlq9403XRklwN3JA
...7KSPqMTp30Qdx6yIUaADwQqlO+QMuQq/QxBHdjeebmdhgVfjhqxrzTbSMw
w/ZNhL...Ywv/QM/oDHbXiLSUlB3Qrg+9wsE/0jU/EOisiU=..X-Sasl-en
c: 8ZJ+4ZRE8AGPzdLRWQFivGymJb8pa4G9JGcb7k4xKn+I 1457298962..Re
ceived: from [192.168.1.14] (75-137-1-34.dhcp.nwnn.ga.charter.co
m [75.137.1.34])...by mail.messagingengine.com (Postfix) with
 ESMTPA id C0B366801CD...for <doug@doughellmann.com>; Sun,  6
 Mar 2016 16:16:02 -0500 (EST)..From: Doug Hellmann <doug@doug
hellmann.com>..Content-Type: text/plain; charset=us-ascii..C
ontent-Transfer-Encoding: 7bit..Subject: PyMOTW Example messag
e 2..Message-Id: <00ABCD46-DADA-4912-A451-D27165BC3A2F@doughel
lmann.com>..Date: Sun, 6 Mar 2016 16:16:02 -0500..To: Doug H
ellmann <doug@doughellmann.com>..Mime-Version: 1.0 (Mac OS X M
ail 9.2 .(3112.))..X-Mailer: Apple Mail (2.3112)....'

BODY TEXT:
b'This is the second example message...'

FLAGS:
b'1 (FLAGS ())'
()

全部消息

如前所述,客户端可以分别向服务器询问消息的各个部分。还可以将整个消息作为 RFC 822 格式的邮件消息进行检索,并使用email模块中的类对其进行解析。

imaplib_fetch_rfc822.py

import imaplib
import email
import email.parser

import imaplib_connect

with imaplib_connect.open_connection() as c:
    c.select('INBOX', readonly=True)

    typ, msg_data = c.fetch('1', '(RFC822)')
    for response_part in msg_data:
        if isinstance(response_part, tuple):
            email_parser = email.parser.BytesFeedParser()
            email_parser.feed(response_part[1])
            msg = email_parser.close()
            for header in ['subject', 'to', 'from']:
                print('{:^8}: {}'.format(
                    header.upper(), msg[header]))

email 模块中的解析器使访问和处理消息变得非常容易。本示例仅为每个消息打印一些标题。

$ python3 imaplib_fetch_rfc822.py

SUBJECT : PyMOTW Example message 2
   TO   : Doug Hellmann <doug@doughellmann.com>
  FROM  : Doug Hellmann <doug@doughellmann.com>

上传消息

要将新邮件添加到邮箱,请构造一个Message实例,并将其以及该邮件的时间戳传递给append() 方法。

imaplib_append.py

import imaplib
import time
import email.message
import imaplib_connect

new_message = email.message.Message()
new_message.set_unixfrom('pymotw')
new_message['Subject'] = 'subject goes here'
new_message['From'] = 'pymotw@example.com'
new_message['To'] = 'example@example.com'
new_message.set_payload('This is the body of the message..')

print(new_message)

with imaplib_connect.open_connection() as c:
    c.append('INBOX', '',
             imaplib.Time2Internaldate(time.time()),
             str(new_message).encode('utf-8'))

    # Show the headers for all messages in the mailbox
    c.select('INBOX')
    typ, [msg_ids] = c.search(None, 'ALL')
    for num in msg_ids.split():
        typ, msg_data = c.fetch(num, '(BODY.PEEK[HEADER])')
        for response_part in msg_data:
            if isinstance(response_part, tuple):
                print('.{}:'.format(num))
                print(response_part[1])

本示例中使用的  payload  是一个简单的纯文本电子邮件正文。 Message还支持MIME编码的多部分消息。

$ python3 imaplib_append.py

Subject: subject goes here
From: pymotw@example.com
To: example@example.com

This is the body of the message.

b'1':
b'Return-Path: <doug@doughellmann.com>..Received: from compute
4.internal (compute4.nyi.internal [10.202.2.44])... by sloti2
6t01 (Cyrus 3.0.0-beta1-git-fastmail-12410) with LMTPA;... Su
n, 06 Mar 2016 16:16:03 -0500..X-Sieve: CMU Sieve 2.4..X-Spa
m-known-sender: yes, fadd1cf2-dc3a-4984-a08b-02cef3cf1221="doug"
,..  ea349ad0-9299-47b5-b632-6ff1e394cc7d="both hellfly"..X-
Spam-score: 0.0..X-Spam-hits: ALL_TRUSTED -1, BAYES_00 -1.9, L
ANGUAGES unknown, BAYES_USED global,..  SA_VERSION 3.3.2..X-
Spam-source: IP=.127.0.0.1., Host=.unk., Country=.unk., Fr
omHeader=.com.,..  MailFrom=.com...X-Spam-charsets: plai
n=.us-ascii...X-Resolved-to: doughellmann@fastmail.fm..X-D
elivered-to: doug@doughellmann.com..X-Mail-from: doug@doughell
mann.com..Received: from mx5 ([10.202.2.204])..  by compute4
.internal (LMTPProxy); Sun, 06 Mar 2016 16:16:03 -0500..Receiv
ed: from mx5.nyi.internal (localhost [127.0.0.1])...by mx5.ny
i.internal (Postfix) with ESMTP id 47CBA280DB3...for <doug@do
ughellmann.com>; Sun,  6 Mar 2016 16:16:03 -0500 (EST)..Receiv
ed: from mx5.nyi.internal (localhost [127.0.0.1])..    by mx5.
nyi.internal (Authentication Milter) with ESMTP..    id A71788
6846E.30BA4280D81;..    Sun, 6 Mar 2016 16:16:03 -0500..Auth
entication-Results: mx5.nyi.internal;..    dkim=pass (1024-bit
 rsa key) header.d=messagingengine.com header.i=@messagingengine
.com header.b=Jrsm+pCo;..    x-local-ip=pass..Received: from
 mailout.nyi.internal (gateway1.nyi.internal [10.202.2.221])..
.(using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/25
6 bits))...(No client certificate requested)...by mx5.nyi.
internal (Postfix) with ESMTPS id 30BA4280D81...for <doug@dou
ghellmann.com>; Sun,  6 Mar 2016 16:16:03 -0500 (EST)..Receive
d: from compute2.internal (compute2.nyi.internal [10.202.2.42])
r..by mailout.nyi.internal (Postfix) with ESMTP id 1740420D0A
r..for <doug@doughellmann.com>; Sun,  6 Mar 2016 16:16:03 -050
0 (EST)..Received: from frontend2 ([10.202.2.161])..  by com
pute2.internal (MEProxy); Sun, 06 Mar 2016 16:16:03 -0500..DKI
M-Signature: v=1; a=rsa-sha1; c=relaxed/relaxed; d=...messagi
ngengine.com; h=content-transfer-encoding:content-type...:dat
e:from:message-id:mime-version:subject:to:x-sasl-enc...:x-sas
l-enc; s=smtpout; bh=P98NTsEo015suwJ4gk71knAWLa4=; b=Jrsm+...
pCovRIoQIRyp8Fl0L6JHOI8sbZy2obx7O28JF2iTlTWmX33Rhlq9403XRklwN3JA
...7KSPqMTp30Qdx6yIUaADwQqlO+QMuQq/QxBHdjeebmdhgVfjhqxrzTbSMw
w/ZNhL...Ywv/QM/oDHbXiLSUlB3Qrg+9wsE/0jU/EOisiU=..X-Sasl-en
c: 8ZJ+4ZRE8AGPzdLRWQFivGymJb8pa4G9JGcb7k4xKn+I 1457298962..Re
ceived: from [192.168.1.14] (75-137-1-34.dhcp.nwnn.ga.charter.co
m [75.137.1.34])...by mail.messagingengine.com (Postfix) with
 ESMTPA id C0B366801CD...for <doug@doughellmann.com>; Sun,  6
 Mar 2016 16:16:02 -0500 (EST)..From: Doug Hellmann <doug@doug
hellmann.com>..Content-Type: text/plain; charset=us-ascii..C
ontent-Transfer-Encoding: 7bit..Subject: PyMOTW Example messag
e 2..Message-Id: <00ABCD46-DADA-4912-A451-D27165BC3A2F@doughel
lmann.com>..Date: Sun, 6 Mar 2016 16:16:02 -0500..To: Doug H
ellmann <doug@doughellmann.com>..Mime-Version: 1.0 (Mac OS X M
ail 9.2 .(3112.))..X-Mailer: Apple Mail (2.3112)....'

b'2':
b'Subject: subject goes here..From: pymotw@example.com..To:
example@example.com....'

移动和复制消息

消息在服务器上后,可以使用move()copy()将其移动或复制而无需下载。这些方法对消息ID范围进行操作,就像fetch()一样。

imaplib_archive_read.py

import imaplib
import imaplib_connect

with imaplib_connect.open_connection() as c:
    # Find the "SEEN" messages in INBOX
    c.select('INBOX')
    typ, [response] = c.search(None, 'SEEN')
    if typ != 'OK':
        raise RuntimeError(response)
    msg_ids = ','.join(response.decode('utf-8').split(' '))

    # Create a new mailbox, "Example.Today"
    typ, create_response = c.create('Example.Today')
    print('CREATED Example.Today:', create_response)

    # Copy the messages
    print('COPYING:', msg_ids)
    c.copy(msg_ids, 'Example.Today')

    # Look at the results
    c.select('Example.Today')
    typ, [response] = c.search(None, 'ALL')
    print('COPIED:', response)

该示例脚本在Example下创建一个新邮箱,并将读取的邮件从INBOX复制到其中。

$ python3 imaplib_archive_read.py

CREATED Example.Today: [b'Completed']
COPYING: 2
COPIED: b'1'

再次运行同一脚本显示了检查返回码的重要性。除了引发异常外,对create() 的调用使新邮箱报告该邮箱已存在。

$ python3 imaplib_archive_read.py

CREATED Example.Today: [b'[ALREADYEXISTS] Mailbox already exists
']
COPYING: 2
COPIED: b'1 2'

删除讯息

尽管许多现代邮件客户端使用“垃圾箱”模型来处理已删除的邮件,但通常不会将邮件移到实际的文件夹中。而是更新其标志以添加.eleted。通过EXPUNGE命令实现“清空”垃圾的操作。此示例脚本查找主题中带有“ Lorem ipsum ”的已归档邮件,设置已删除标志,然后通过再次查询服务器来显示邮件仍存在于文件夹中。

imaplib_delete_messages.py

import imaplib
import imaplib_connect
from imaplib_list_parse import parse_list_response

with imaplib_connect.open_connection() as c:
    c.select('Example.Today')

    # What ids are in the mailbox?
    typ, [msg_ids] = c.search(None, 'ALL')
    print('Starting messages:', msg_ids)

    # Find the message(s)
    typ, [msg_ids] = c.search(
        None,
        '(SUBJECT "subject goes here")',
    )
    msg_ids = ','.join(msg_ids.decode('utf-8').split(' '))
    print('Matching messages:', msg_ids)

    # What are the current flags?
     typ, response = c. fetch(msg_ids, '(FLAGS)')
     print('Flags before:', response)

     # Change the Deleted flag
     typ, response = c.store(msg_ids, '+FLAGS', r'(.eleted)')

     # What are the flags now?
     typ, response = c. fetch(msg_ids, '(FLAGS)')
     print('Flags after:', response)

     # Really delete the message.
     typ, response = c.expunge()
     print('Expunged:', response)

     # What ids are left in the mailbox?
     typ, [msg_ids] = c. search(None, 'ALL')
     print('Remaining messages:', msg_ids)

Explicitly calling expunge() deletes the message, but calling close() has the same effect. The difference is that the client is not notified about the deletion when calling close().

$ python3 imaplib_delete_messages.py

Response code: OK
Server response: b'(.HasChildren) "." Example'
Parsed response: ('.HasChildren', '.', 'Example')
Server response: b'(.HasNoChildren) "." Example.Today'
Parsed response: ('.HasNoChildren', '.', 'Example.Today')
Server response: b'(.HasNoChildren) "." Example.2016'
Parsed response: ('.HasNoChildren', '.', 'Example.2016')
Server response: b'(.HasNoChildren) "." Archive'
Parsed response: ('.HasNoChildren', '.', 'Archive')
Server response: b'(.HasNoChildren) "." "Deleted Messages"'
Parsed response: ('.HasNoChildren', '.', 'Deleted Messages')
Server response: b'(.HasNoChildren) "." INBOX'
Parsed response: ('.HasNoChildren', '.', 'INBOX')
Starting messages: b'1 2'
Matching messages: 1,2
Flags before: [b'1 (FLAGS (.Seen))', b'2 (FLAGS (.Seen))']
Flags after: [b'1 (FLAGS (.Deleted.Seen))', b'2 (FLAGS (.Del
eted .Seen))']
Expunged: [b'2', b'1']
Remaining messages: b''

see also

Likes(1)

Comment list count 0 Comments

No Comments

WeChat Self-Service

WeChat Consult

TaoBao

support@elephdev.com

发表
评论
Go
Top