Быстрый в изучении - мощный в программировании
>> Telegram ЧАТ для Python Программистов

Свободное общение и помощь советом и решением проблем с кодом! Заходите в наш TELEGRAM ЧАТ!

>> Python Форум Помощи!

Мы создали форум где отвечаем на все вопросы связанные с языком программирования Python. Ждем вас там!

>> Python Канал в Telegram

Обучающие статьи, видео и новости из мира Python. Подпишитесь на наш TELEGRAM КАНАЛ!

Читаем почту через IMAP в Python

30 марта 2017 г. Archy Просмотров: 59877 RSS 9
Примеры Python , , ,

IMAP Python

У меня не вышло найти всю необходимую информацию об IMAP в интернете, кроме RFC3501. Документ протокола IMAP Python – ключ к пониманию доступных пользователю команд, однако позвольте пропустить попытки объяснить все сразу, и лучше взглянем на пример, где я смогу объяснить основные принципы работы

Заходим в почтовый ящик

import imaplib
mail = imaplib.IMAP4_SSL('imap.gmail.com')
mail.login('myusername@gmail.com', 'mypassword')
mail.list()
 
# Выводит список папок в почтовом ящике.
mail.select("inbox") # Подключаемся к папке "входящие".
  • Выбираем последние письма
  • Начнем с поиска входящих среди всех писем при помощи функции поиска
  • Воспользуемся встроенным ключом “ALL”, чтобы получить все результаты (документировано в RFC3501)

Далее мы извлечем необходимые нам данные из ответа, затем получим почту, через вычисленный нами ID.

result, data = mail.search(None, "ALL")
 
ids = data[0] # Получаем сроку номеров писем
id_list = ids.split() # Разделяем ID писем
latest_email_id = id_list[-1] # Берем последний ID
 
result, data = mail.fetch(latest_email_id, "(RFC822)") # Получаем тело письма (RFC822) для данного ID
 
raw_email = data[0][1] # Тело письма в необработанном виде
# включает в себя заголовки и альтернативные полезные нагрузки

Используем UID вместо ID

Функция поиска imap возвращает последовательный ID: если значение ID равно 5, значит это пятый электронный адрес в вашей почте. Это значит, что когда пользователь удаляет адрес под номером 10, все письма, находящиеся выше этой отметки указывают на неправильный адрес.

Это неприемлемо!

Не можете найти дешевых подписчиков в YouTube с гарантиями? Предлагаем Вам обратиться за помощью по ссылке и сделать выгодную покупку на надежном сайте SMM услуг. Здесь Вы получите качественный гарантированный ресурс по заманчивым ценам.

К счастью, мы можем отправить запрос на сервер imap и вернуть уникальный id (UID). Это работает очень просто: мы используем функцию uid и передаём её строке команды in в качестве первого аргумента. Остальная часть кода остается неизменной.

result, data = mail.uid('search', None, "ALL") # Выполняет поиск и возвращает UID писем.
latest_email_uid = data[0].split()[-1]
result, data = mail.uid('fetch', latest_email_uid, '(RFC822)')
raw_email = data[0][1]

Анализ необработанных писем

Работа с почтой очень похожа на разбор невнятной речи. К счастью, мы располагаем библиотекой python для работы с почтой, которая называется, кто бы мог подумать, email. Данная библиотека может конвертировать необработанные письма в знакомый нам объект EmailMessage.

import email
email_message = email.message_from_string(raw_email)
 
print email_message['To']
 
print email.utils.parseaddr(email_message['From']) # получаем имя отправителя "Yuji Tomita" 
 
print email_message.items() # Выводит все заголовки.
 
def get_first_text_block(self, email_message_instance):
    maintype = email_message_instance.get_content_maintype()
    if maintype == 'multipart':
        for part in email_message_instance.get_payload():
            if part.get_content_maintype() == 'text':
                return part.get_payload()
    elif maintype == 'text':
        return email_message_instance.get_payload()

Расширенный поиск

Мы только что закончили с базовым поиском для всей почты в целом. Теперь мы научимся сортировать выдачу поиска на нужную и ненужную. Все доступные параметры поиска находятся в протокольной документации IMAP, так что вам определенно захочется ознакомиться с разделом SEARCH Command поближе.

Попробуем следующие способы поиска.

Поиск любых заголовков

Для поиска заголовков, так их как «Ответы», «Входящие» и т.д., используем команду (HEADER “”)

mail.uid('search', None, '(HEADER Subject "My Search Term")')
mail.uid('search', None, '(HEADER Received "localhost")')

Поиск писем, начиная с датированных вчерашним днем

Часто количество входящих сообщений слишком велико, и в IMAP не указывается способ ограничения результатов, и это делает поиск слишком медленным. Существует способ ограничить этот объем – использовать ключевое слово SENTSINCE. Формат даты SENTSINCE будет следующими: ДД-МММ-ГГГГ. В то время как Python формат будет следующем: strftime('%d-%b-%Y')

import datetime
date = (datetime.date.today() - datetime.timedelta(1)).strftime("%d-%b-%Y")
result, data = mail.uid('search', None, '(SENTSINCE {date})'.format(date=date))

Ограничение по дате, поиск по темам и исключение отправителей

date = (datetime.date.today() - datetime.timedelta(1)).strftime("%d-%b-%Y")
 
result, data = mail.uid('search', None, '(SENTSINCE {date} HEADER Subject "My Subject" NOT FROM "yuji@grovemade.com")'.format(date=date))

Выборка

Получаем ID переписки Gmail

Выборка может включать в себя как все тело письма, так и любое сочетание результатов выдачи, такие как отмеченные письма (просмотренные\не просмотренные). Также это касается и определенных ID в Gmail, таких как ID переписки.

result, data = mail.uid('fetch', uid, '(X-GM-THRID X-GM-MSGID)')

Получаем только ключ заголовка

result, data = mail.uid('fetch', uid, '(BODY[HEADER.FIELDS (DATE SUBJECT)]])')

Множественная выборка

Вы можете выбрать несколько писем за раз. Опытным путем я пришел к тому, что это обязательно подразумевает ввод данных через запятую.

result, data = mail.uid('fetch', '1938,2398,2487', '(X-GM-THRID X-GM-MSGID)')

Используем регулярное выражение для анализа

Полученную выдачу достаточно трудно понять, так как результат разделен парами «ключ-значение». Так что мы используем регулярное выражение для получения необходимых данных. Это достаточно просто:

import re

result, data = mail.uid('fetch', uid, '(X-GM-THRID X-GM-MSGID)')
re.search('X-GM-THRID (?P\d+) X-GM-MSGID (?P\d+)', data[0]).groupdict()
# это ваш спасательный круг, который поможет организовать большой объем полученных данных.

Подведем итоги

Поздравляем, теперь вы свободно ориентируетесь в протоколе IMAP и можете использовать Python для работы с Gmail.

Комментариев: 9
  1. Эдуард | 2017-05-10 в 08:02:48

    А ссылку на оригинал?

    (https://yuji.wordpress.com/2011/06/22/python-imaplib-imap-example-with-gmail/)

  2. Алексей | 2020-04-24 в 09:36:44

    А можно забирать файлы с эл. почты? Как скачивать файлы по ссылке через python, я уже знаю.

  3. Купить натяжной потолок в Киеве и Киевской области:Ирпень, Буча можно купить натяжной потолок

  4. "Функция поиска imap возвращает последовательный ID: если значение ID равно 5, значит это пятый электронный адрес в вашей почте." – бред какой-то! Это пятое по счету письмо в папке входящих, а вовсе не пятый адрес электронной почты. Адрес электронной почты, он же e-mail – это, например, fuckinshit@yandex.ru. И писем от с этого адреса может быть десять или тысяча. Похоже, автор не понимает, о чем пишет.

  5. Под заголовком "Анализ необработанных писем" приведет кусок кода, явно относящийся к версии языка Python 2. А всё, что до этого, было для Python 3. Автор издевается?

  6. В куске кода под заголовком "Анализ необработанных писем" грубая ошибка во второй строке. Функция email.message_from_stringтребует строку в качестве аргумента, а raw_email – это байтовый список. Вместо message_from_string нужно использовать message_from_bytes

  7. Разве есть ошибка во второй строке? я не вижу

  8. Если оставить этот код без изменений, то вылетает длинное сообщение об ошибке, заканчивающееся строкой

    TypeError: initial_value must be str or None, not bytes

    Это потому, что raw_email – вовсе не строка текста, а список байтов. У меня в последнем письме этот список выглядит так:

    b'Received: from iva6-94240f0bdfbb.qloud-c.yandex.net......'

    Видите символ b перед строкой? Признак байтов. Элемент raw_email[0] не равен 'R', он равен 82.

  9. Евгений | 2022-12-01 в 12:02:34

    import imaplib

    mail = imaplib.IMAP4_SSL('imap.gmail.com')

    mail.login('my_adress', 'mypassword')

    mail.list()

    Это не работает. Дальнейшее не имеет смысла.