Автор оригинала: Doug Hellmann.
Цель:
Клиентская библиотека для обмена данными по протоколу IMAP4.
imaplib
реализует клиент для связи с серверами протокола доступа к сообщениям Интернета (IMAP) версии 4. Протокол IMAP определяет набор команд, отправляемых на сервер, и ответов, возвращаемых клиенту. Большинство команд доступны как методы объекта IMAP4
, используемые для связи с сервером.
В этих примерах обсуждается часть протокола IMAP, но они никоим образом не полны. См. RFC 3501 для получения полной информации.
Вариации
Существует три клиентских класса для связи с серверами с использованием различных механизмов. Первый, IMAP4
, использует сокеты для открытого текста; IMAP4_SSL
использует шифрованную связь через сокеты SSL; а IMAP4_stream
использует стандартный ввод и стандартный вывод внешней команды. Все примеры здесь будут использовать IMAP4_SSL
, но API для других классов аналогичны.
Подключение к серверу
Есть два шага для установки соединения с сервером IMAP. Сначала установите само соединение сокета. Во-вторых, авторизуйтесь как пользователь с учетной записью на сервере. В следующем примере кода будет считываться информация о сервере и пользователе из файла конфигурации.
imaplib_connect.py
import imaplib import configparser import os def open_connection(verboseFalse): # 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(verboseTrue) as c: print(c)
При запуске open_connection ()
считывает информацию о конфигурации из файла в домашнем каталоге пользователя, затем открывает соединение IMAP4_SSL
и выполняет аутентификацию.
$ python3 imaplib_connect.py Connecting to pymotw.hellfly.net Logging in as example
В других примерах в этом разделе этот модуль используется повторно, чтобы избежать дублирования кода.
Ошибка аутентификации
Если соединение установлено, но аутентификация не удалась, возникает исключение.
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)
В этом примере для вызова исключения специально используется неправильный пароль.
$ python3 imaplib_connect_fail.py Connecting to pymotw.hellfly.net Logging in as example ERROR: b'[AUTHENTICATIONFAILED] Authentication failed.'
Пример конфигурации
В примере учетной записи есть несколько почтовых ящиков в иерархии:
- INBOX
- Удаленные сообщения
- Архив
- Пример
- 2016
- 2016 г.
Одно непрочитанное сообщение находится в папке INBOX
и одно прочитанное сообщение в Example/2016
.
Список почтовых ящиков
Чтобы получить почтовые ящики, доступные для учетной записи, используйте метод list ()
.
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)
Возвращаемое значение – это кортеж
, содержащий код ответа и данные, возвращаемые сервером. Код ответа – OK
, если не было ошибки. Данные для list ()
– это последовательность строк, содержащих флаги , разделитель иерархии и имя почтового ящика для каждый почтовый ящик.
$ 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']
Каждую строку ответа можно разделить на три части с помощью re или csv (см. Сценарий резервного копирования IMAP в ссылках в конце этого раздела для примера с использованием csv
).
imaplib_list_parse.py
import imaplib import re from imaplib_connect import open_connection list_response_pattern re.compile( r'\((?P.*?)\) "(?P .*)" (?P .*)' ) 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))
Сервер цитирует имя почтового ящика, если оно включает в себя пробелы, но эти кавычки необходимо удалить, чтобы использовать имя почтового ящика в других вызовах сервера позже.
$ 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 ()
принимает аргументы для указания почтовых ящиков в части иерархии. Например, чтобы перечислить подпапки Example
, передайте "Example"
в качестве аргумента directory
.
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)
Родитель и подпапка возвращаются.
$ python3 imaplib_list_subfolders.py Response code: OK Server response: b'(\\HasChildren) "." Example' Server response: b'(\\HasNoChildren) "." Example.2016'
В качестве альтернативы, чтобы перечислить папки, соответствующие шаблону, передайте аргумент pattern
.
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)
В этом случае в ответ включаются и Example
, и Example.2016
.
$ python3 imaplib_list_pattern.py Response code: OK Server response: b'(\\HasChildren) "." Example' Server response: b'(\\HasNoChildren) "." Example.2016'
Статус почтового ящика
Используйте status ()
, чтобы запросить агрегированную информацию о содержимом. в таблице ниже перечислены условия состояния, определенные стандартом.
Условия состояния почтового ящика IMAP 4
Условие
Смысл
СООБЩЕНИЯ
Количество сообщений в почтовом ящике.
НЕДАВНИЙ
Количество сообщений с
\Недавний
установлен флаг.
UIDNEXT
Следующее значение уникального идентификатора почтового ящика.
UIDVALIDITY
Значение достоверности уникального идентификатора почтового ящика.
НЕВИДИМЫЙ
Количество сообщений, не имеющих
\Видимый
установлен флаг.
Условия состояния должны быть отформатированы как строка, разделенная пробелами, заключенная в круглые скобки, кодировка для «списка» в спецификации IMAP4. Имя почтового ящика заключено в "
в случае, если какое-либо из имен включает пробелы или другие символы, которые могут быть выброшены анализатором.
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)
Возвращаемое значение – обычный кортеж
, содержащий код ответа и список информации с сервера. В этом случае список содержит одну строку, отформатированную с именем почтового ящика в кавычках, а затем условиями статуса и значениями в круглых скобках.
$ 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)'])
Выбор почтового ящика
После аутентификации клиента основным режимом работы является выбор почтового ящика, а затем опрос сервера относительно сообщений в почтовом ящике. Соединение имеет состояние, поэтому после выбора почтового ящика все команды работают с сообщениями в этом почтовом ящике, пока не будет выбран новый почтовый ящик.
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))
Данные ответа содержат общее количество сообщений в почтовом ящике.
$ python3 imaplib_select.py OK [b'1'] There are 1 messages in INBOX
Если указан недопустимый почтовый ящик, код ответа – 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)
Данные содержат сообщение об ошибке с описанием проблемы.
$ python3 imaplib_select_invalid.py NO [b"Mailbox doesn't exist: Does-Not-Exist"]
Поиск сообщений
После выбора почтового ящика используйте search ()
для получения идентификаторов сообщений в почтовом ящике.
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), readonlyTrue) typ, msg_ids c.search(None, 'ALL') print(mbox_name, typ, msg_ids)
Идентификаторы сообщений назначаются сервером и зависят от реализации. Протокол IMAP4 делает различие между последовательными идентификаторами сообщений в определенный момент времени во время транзакции и идентификаторами UID для сообщений, но не все серверы реализуют и то, и другое.
$ 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']
В этом случае, INBOX
и Example.2016
имеют разные сообщения с идентификатором 1
. Остальные почтовые ящики пусты.
Критерий поиска
Можно использовать множество других критериев поиска, включая просмотр дат сообщения, флагов и других заголовков. См. Раздел 6.4.4. RFC 3501 для получения полной информации.
Чтобы искать сообщения с 'Example message 2'
в теме, критерии поиска должны быть построены следующим образом:
(SUBJECT "Example message 2")
Этот пример находит все сообщения с заголовком «Пример сообщения 2» во всех почтовых ящиках:
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), readonlyTrue) 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), readonlyTrue) typ, msg_ids c.search( None, '(FROM "Doug" SUBJECT "Example message 2")', ) print(mbox_name, typ, msg_ids)
Критерии комбинируются с логической операцией и
.
$ 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 ()
. Он принимает два аргумента: идентификаторы сообщений для извлечения и части сообщения для извлечения.
Аргумент message_ids
представляет собой список идентификаторов, разделенных запятыми (например, "1"
, "1,2"
) или диапазонов идентификаторов (например, 1: 2
). Аргумент message_parts
– это список IMAP с именами сегментов сообщения. Как и в случае с критериями поиска для search ()
, протокол IMAP определяет именованные сегменты сообщения, поэтому клиенты могут эффективно извлекать только те части сообщения, которые им действительно нужны. Например, чтобы получить заголовки сообщений в почтовом ящике, используйте fetch ()
с аргументом BODY.PEEK [HEADER]
.
Примечание
Другой способ получить заголовки – это 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', readonlyTrue) 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 19:40.70 > b'IIEN0 CAPABILITY' 19:40.73 < b'* CAPABILITY IMAP4rev1 LITERAL+ SASL-IR LOGIN-REF ERRALS ID ENABLE IDLE 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',) 19:40.73 > b'IIEN1 LOGIN example "TMFw00fpymotw"' 19:40.79 < b'* CAPABILITY IMAP4rev1 LITERAL+ SASL-IR LOGIN-REF ERRALS ID ENABLE IDLE SORT =REFS ELECT CHILDREN NAMESPACE UIDPLUS LIST-EXTENDED TORE QRESYNC ESEARCH ESORT SEARCHRES WITHIN 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:\r\nReceived: from compu te4.internal (' b'compute4.nyi.internal [10.202.2.44])\r\n\t by sloti26t01 (Cy rus 3.0.0-beta1' b'-git-fastmail-12410) with LMTPA;\r\n\t Sun, 06 Mar 2016 16:1 6:03 -0500\r' b'\nX-Sieve: CMU Sieve 2.4\r\nX-Spam-known-sender: yes, fadd1c f2-dc3a-4984-a0' ,\r\n ea349ad0-9299-47b5-b632-6ff1e39 b'llfly"\r\nX-Spam-score: 0.0\r\nX-Spam-hits: ALL_TRUSTED -1, BAYES_00 -1.' b'9, LANGUAGES unknown, BAYES_USED global,\r\n SA_VERSION 3.3 .2\r\nX-Spam' b"-source:,,, FromHead,\r\n " b"\r\nX-Spam-charsets:\r\nX-Re solved-to: d" b'oughellmann@fastmail.fm\r\nX-Delivered-to: doug@doughellmann .com\r\nX-Ma' b'il-from: doug@doughellmann.com\r\nReceived: from mx5 ([10.20 2.2.204])\r' b'\n by compute4.internal (LMTPProxy); Sun, 06 Mar 2016 16:16 :03 -0500\r\nRe' b'ceived: from mx5.nyi.internal (localhost [127.0.0.1])\r\n\tb y mx5.nyi.inter' b'nal (Postfix) with ESMTP id 47CBA280DB3\r\n\tfor ; S' b'un, 6 Mar 2016 16:16:03 -0500 (EST)\r\nReceived: from mx5.n yi.internal (l' b'ocalhost [127.0.0.1])\r\n by mx5.nyi.internal (Authentica tion Milter) w' b'ith ESMTP\r\n id A717886846E.30BA4280D81;\r\n Sun, 6 M ar 2016 16:1' b'6:03 -0500\r\nAuthentication-Results: mx5.nyi.internal;\r\n b' (1024-bit rsa key) essagingengi' b'ne.com : from mailo' b'ut.nyi.internal (gateway1.nyi.internal [10.202.2.221])\r\n\t (using TLSv1.2 ' b'with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits))\r\n\ t(No client cer' b'tificate requested)\r\n\tby mx5.nyi.internal (Postfix) with ESMTPS id 30BA4' b'280D81\r\n\tfor ; Sun, 6 Mar 2016 16 :16:03 -0500 (E' b'ST)\r\nReceived: from compute2.internal (compute2.nyi.intern al [10.202.2.4' b'2])\r\n\tby mailout.nyi.internal (Postfix) with ESMTP id 174 0420D0A\r\n\tf' b'or ; Sun, 6 Mar 2016 16:16:03 -0500 (EST)\r\nRecei' b'ved: from frontend2 ([10.202.2.161])\r\n by compute2.intern al (MEProxy); ' b'Sun, 06 Mar 2016 16:16:03 -0500\r\nDKIM-Signature: a-sha1; b'xed/relaxed; -encoding:conte' b'nt-type\r\n\t:date:from:message-id:mime-version:subject:to:x -sasl-enc\r\n' b'\t:x-sasl-enc; =Jrsm+\r\n\t' b'pCovRIoQIRyp8Fl0L6JHOI8sbZy2obx7O28JF2iTlTWmX33Rhlq9403XRklw N3JA\r\n\t7KSPq' b'MTp30Qdx6yIUaADwQqlO+QMuQq/QxBHdjeebmdhgVfjhqxrzTbSMww/ZNhL\ r\n\tYwv/QM/oDH' RWQFivGymJb8pa' b'4G9JGcb7k4xKn+I 1457298962\r\nReceived: from [192.168.1.14] (75-137-1-34.d' b'hcp.nwnn.ga.charter.com [75.137.1.34])\r\n\tby mail.messagin gengine.com (Po' b'stfix) with ESMTPA id C0B366801CD\r\n\tfor ; Sun, 6' b' Mar 2016 16:16:02 -0500 (EST)\r\nFrom: Doug Hellmann \r\nContent-Type: text/plain; -Transfer-En' b'coding: 7bit\r\nSubject: PyMOTW Example message 2\r\nMessage -Id: <00ABCD' b'46-DADA-4912-A451-D27165BC3A2F@doughellmann.com>\r\nDate: Su n, 6 Mar 2016 ' b'16:16:02 -0500\r\nTo: Doug Hellmann \ r\nMime-Vers' b'ion: 1.0 (Mac OS X Mail 9.2 \\(3112\\))\r\nX-Mailer: Apple M ail (2.3112)' b'\r\n\r\n'), b')']
Ответ от команды FETCH
начинается с флагов, затем указывает, что имеется 595 байт данных заголовка. Клиент создает кортеж
imaplib_fetch_separately.py
import imaplib import pprint import imaplib_connect with imaplib_connect.open_connection() as c: c.select('INBOX', readonlyTrue) 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('\nBODY 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('\nFLAGS:') 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:\r\nReceived: from compute 4.internal (compute4.nyi.internal [10.202.2.44])\r\n\t by sloti2 6t01 (Cyrus 3.0.0-beta1-git-fastmail-12410) with LMTPA;\r\n\t Su n, 06 Mar 2016 16:16:03 -0500\r\nX-Sieve: CMU Sieve 2.4\r\nX-Spa m-known-sender: yes, ,\r\n \r\nX- Spam-score: 0.0\r\nX-Spam-hits: ALL_TRUSTED -1, BAYES_00 -1.9, L ANGUAGES unknown, BAYES_USED global,\r\n SA_VERSION 3.3.2\r\nX- Spam-source: IP=\'127.0.0.1\', Host=\'unk\', Country=\'unk\', Fr omHeader=\'com\',\r\n MailFrom=\'com\'\r\nX-Spam-charsets: plai n=\'us-ascii\'\r\nX-Resolved-to: doughellmann@fastmail.fm\r\nX-D elivered-to: doug@doughellmann.com\r\nX-Mail-from: doug@doughell mann.com\r\nReceived: from mx5 ([10.202.2.204])\r\n by compute4 .internal (LMTPProxy); Sun, 06 Mar 2016 16:16:03 -0500\r\nReceiv ed: from mx5.nyi.internal (localhost [127.0.0.1])\r\n\tby mx5.ny i.internal (Postfix) with ESMTP id 47CBA280DB3\r\n\tfor ; Sun, 6 Mar 2016 16:16:03 -0500 (EST)\r\nReceiv ed: from mx5.nyi.internal (localhost [127.0.0.1])\r\n by mx5. nyi.internal (Authentication Milter) with ESMTP\r\n id A71788 6846E.30BA4280D81;\r\n Sun, 6 Mar 2016 16:16:03 -0500\r\nAuth entication-Results: mx5.nyi.internal;\r\n rsa key) .com mailout.nyi.internal (gateway1.nyi.internal [10.202.2.221])\r\n \t(using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/25 6 bits))\r\n\t(No client certificate requested)\r\n\tby mx5.nyi. internal (Postfix) with ESMTPS id 30BA4280D81\r\n\tfor ; Sun, 6 Mar 2016 16:16:03 -0500 (EST)\r\nReceive d: from compute2.internal (compute2.nyi.internal [10.202.2.42])\ r\n\tby mailout.nyi.internal (Postfix) with ESMTP id 1740420D0A\ r\n\tfor ; Sun, 6 Mar 2016 16:16:03 -050 0 (EST)\r\nReceived: from frontend2 ([10.202.2.161])\r\n by com pute2.internal (MEProxy); Sun, 06 Mar 2016 16:16:03 -0500\r\nDKI M-Signature: ngengine.com; e:from:message-id:mime-version:subject:to:x-sasl-enc\r\n\t:x-sas l-enc; pCovRIoQIRyp8Fl0L6JHOI8sbZy2obx7O28JF2iTlTWmX33Rhlq9403XRklwN3JA \r\n\t7KSPqMTp30Qdx6yIUaADwQqlO+QMuQq/QxBHdjeebmdhgVfjhqxrzTbSMw c: 8ZJ+4ZRE8AGPzdLRWQFivGymJb8pa4G9JGcb7k4xKn+I 1457298962\r\nRe ceived: from [192.168.1.14] (75-137-1-34.dhcp.nwnn.ga.charter.co m [75.137.1.34])\r\n\tby mail.messagingengine.com (Postfix) with ESMTPA id C0B366801CD\r\n\tfor ; Sun, 6 Mar 2016 16:16:02 -0500 (EST)\r\nFrom: Doug Hellmann \r\nContent-Type: text/plain; ontent-Transfer-Encoding: 7bit\r\nSubject: PyMOTW Example messag e 2\r\nMessage-Id: <00ABCD46-DADA-4912-A451-D27165BC3A2F@doughel lmann.com>\r\nDate: Sun, 6 Mar 2016 16:16:02 -0500\r\nTo: Doug H ellmann \r\nMime-Version: 1.0 (Mac OS X M ail 9.2 \\(3112\\))\r\nX-Mailer: Apple Mail (2.3112)\r\n\r\n' BODY TEXT: b'This is the second example message.\r\n' 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', readonlyTrue) 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 HellmannFROM : Doug Hellmann
Отправка сообщений
Чтобы добавить новое сообщение в почтовый ящик, создайте экземпляр 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.\n') 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('\n{}:'.format(num)) print(response_part[1])
payload
, использованная в этом примере, представляет собой простое текстовое тело письма. Сообщение
также поддерживает составные сообщения в кодировке 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:\r\nReceived: from compute 4.internal (compute4.nyi.internal [10.202.2.44])\r\n\t by sloti2 6t01 (Cyrus 3.0.0-beta1-git-fastmail-12410) with LMTPA;\r\n\t Su n, 06 Mar 2016 16:16:03 -0500\r\nX-Sieve: CMU Sieve 2.4\r\nX-Spa m-known-sender: yes, ,\r\n \r\nX- Spam-score: 0.0\r\nX-Spam-hits: ALL_TRUSTED -1, BAYES_00 -1.9, L ANGUAGES unknown, BAYES_USED global,\r\n SA_VERSION 3.3.2\r\nX- Spam-source: IP=\'127.0.0.1\', Host=\'unk\', Country=\'unk\', Fr omHeader=\'com\',\r\n MailFrom=\'com\'\r\nX-Spam-charsets: plai n=\'us-ascii\'\r\nX-Resolved-to: doughellmann@fastmail.fm\r\nX-D elivered-to: doug@doughellmann.com\r\nX-Mail-from: doug@doughell mann.com\r\nReceived: from mx5 ([10.202.2.204])\r\n by compute4 .internal (LMTPProxy); Sun, 06 Mar 2016 16:16:03 -0500\r\nReceiv ed: from mx5.nyi.internal (localhost [127.0.0.1])\r\n\tby mx5.ny i.internal (Postfix) with ESMTP id 47CBA280DB3\r\n\tfor ; Sun, 6 Mar 2016 16:16:03 -0500 (EST)\r\nReceiv ed: from mx5.nyi.internal (localhost [127.0.0.1])\r\n by mx5. nyi.internal (Authentication Milter) with ESMTP\r\n id A71788 6846E.30BA4280D81;\r\n Sun, 6 Mar 2016 16:16:03 -0500\r\nAuth entication-Results: mx5.nyi.internal;\r\n rsa key) .com mailout.nyi.internal (gateway1.nyi.internal [10.202.2.221])\r\n \t(using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/25 6 bits))\r\n\t(No client certificate requested)\r\n\tby mx5.nyi. internal (Postfix) with ESMTPS id 30BA4280D81\r\n\tfor ; Sun, 6 Mar 2016 16:16:03 -0500 (EST)\r\nReceive d: from compute2.internal (compute2.nyi.internal [10.202.2.42])\ r\n\tby mailout.nyi.internal (Postfix) with ESMTP id 1740420D0A\ r\n\tfor ; Sun, 6 Mar 2016 16:16:03 -050 0 (EST)\r\nReceived: from frontend2 ([10.202.2.161])\r\n by com pute2.internal (MEProxy); Sun, 06 Mar 2016 16:16:03 -0500\r\nDKI M-Signature: ngengine.com; e:from:message-id:mime-version:subject:to:x-sasl-enc\r\n\t:x-sas l-enc; pCovRIoQIRyp8Fl0L6JHOI8sbZy2obx7O28JF2iTlTWmX33Rhlq9403XRklwN3JA \r\n\t7KSPqMTp30Qdx6yIUaADwQqlO+QMuQq/QxBHdjeebmdhgVfjhqxrzTbSMw c: 8ZJ+4ZRE8AGPzdLRWQFivGymJb8pa4G9JGcb7k4xKn+I 1457298962\r\nRe ceived: from [192.168.1.14] (75-137-1-34.dhcp.nwnn.ga.charter.co m [75.137.1.34])\r\n\tby mail.messagingengine.com (Postfix) with ESMTPA id C0B366801CD\r\n\tfor ; Sun, 6 Mar 2016 16:16:02 -0500 (EST)\r\nFrom: Doug Hellmann \r\nContent-Type: text/plain; ontent-Transfer-Encoding: 7bit\r\nSubject: PyMOTW Example messag e 2\r\nMessage-Id: <00ABCD46-DADA-4912-A451-D27165BC3A2F@doughel lmann.com>\r\nDate: Sun, 6 Mar 2016 16:16:02 -0500\r\nTo: Doug H ellmann \r\nMime-Version: 1.0 (Mac OS X M ail 9.2 \\(3112\\))\r\nX-Mailer: Apple Mail (2.3112)\r\n\r\n' b'2': b'Subject: subject goes here\r\nFrom: pymotw@example.com\r\nTo: example@example.com\r\n\r\n'
Перемещение и копирование сообщений
Когда сообщение находится на сервере, его можно перемещать или копировать без загрузки с помощью move ()
или copy ()
. Эти методы работают с диапазонами идентификаторов сообщений, как и 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'
Удаление сообщений
Хотя многие современные почтовые клиенты используют модель «Папка для мусора» для работы с удаленными сообщениями, сообщения обычно не перемещаются в настоящую папку. Вместо этого их флаги обновляются, чтобы добавить \ Deleted
. Операция по «очистке» корзины реализуется с помощью команды 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'(\Deleted)') # 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)
Явный вызов expunge ()
удаляет сообщения, но вызов close ()
имеет тот же эффект. Разница в том, что клиент не уведомляется об удалении при вызове 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''
Смотрите также
- стандартная библиотечная документация для imaplib
rfc822
– модульrfc822
включает анализатор RFC 822/RFC 5322.email
– модульemail
для анализа сообщений электронной почты.- mailbox – Парсер локального почтового ящика.
ConfigParser
– чтение и запись файлов конфигурации.- Информационный центр IMAP Вашингтонского университета – хороший ресурс с информацией об IMAP вместе с исходным кодом.
- RFC 3501 – протокол доступа к сообщениям Интернета
- RFC 5322 – формат интернет-сообщений
- Сценарий резервного копирования IMAP – сценарий для резервного копирования электронной почты с сервера IMAP.
- IMAPClient – клиент более высокого уровня для взаимодействия с серверами IMAP, написанный Менно Смитсом.
- offlineimap – приложение Python для синхронизации локального набора почтовых ящиков с сервером IMAP.
- Заметки о переносе Python 2 на 3 для imaplib