Работа с почтой при помощи Python

Обновлено и опубликовано Опубликовано:

Используемые термины: Python, IMAP, POP3, SMTP.

В данной инструкции мы рассмотрим фрагменты скриптов на Python 3 для работы с почтой. Для примера мы выполним:

  • отправку письма
  • чтение почты на сервере
  • копирование и удаление почты

В качестве операционной системы нами будет использоваться Linux, однако такой же набор команд будет работать на Windows.

Подготовка

Установка python

Ставим python в систему одной из команд.

а) На системах RPM (Red Hat, CentOS, Fedora):

yum install python3

б) На системах DEB (Debian, Ubuntu):

apt-get install python3

Создание скрипта

Для нашей работы будет использоваться скрипт, написанный на python. Создадим каталог, в котором он будет находиться:

mkdir /scripts

Создадим сам скрипт:

vi mail.py

#!/usr/bin/env python3
# -*- encoding: utf-8 -*-

* в данном примере мы создали скрипт mail.py с двумя строчками — шебангом и определением кодировки.

И, на последок, даем разрешение на выполнение скрипта:

chmod +x /scripts/mail.py

Настройка почты

Если мы планируем использовать почту на бесплатном хостинге, например, MAIL.RU, Яндекс, GMAIL, то необходимо настроить почтовый ящик для возможности подключения к нему по IMAP/POP3.

Подробнее процесс описан в статье Настройка почты GMAIL и Яндекс для подключения по IMAP или POP3.

В случае использования собственного сервера, нам необходимо создать почтовую учетную запись или использовать имеющуюся.

Отправка почты

Разберемся, как можно использовать python для отправки почты. Мы попробуем учесть все правила отправки сообщений, чтобы минимизировать попадание в СПАМ. Также в нашем примере письмо будет с вложением.

Фрагмент кода:

  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3.  
  4. import smtplib
  5. import os
  6. from email.mime.multipart import MIMEMultipart
  7. from email.mime.text import MIMEText
  8. from email.mime.base import MIMEBase
  9. from email import encoders
  10. from platform import python_version
  11.  
  12. server = 'smtp.mail.ru'
  13. user = 'example@mail.ru'
  14. password = 'password_mail'
  15.  
  16. recipients = ['example2@mail.ru', 'example3@mail.ru']
  17. sender = 'example@mail.ru'
  18. subject = 'Тема сообщения'
  19. text = 'Текст сообщения'
  20. html = '<html><head></head><body><p>'+text+'</p></body></html>'
  21.  
  22. filepath = "/var/log/maillog"
  23. basename = os.path.basename(filepath)
  24. filesize = os.path.getsize(filepath)
  25.  
  26. msg = MIMEMultipart('alternative')
  27. msg['Subject'] = subject
  28. msg['From'] = 'Python script <' + sender + '>'
  29. msg['To'] = ', '.join(recipients)
  30. msg['Reply-To'] = sender
  31. msg['Return-Path'] = sender
  32. msg['X-Mailer'] = 'Python/'+(python_version())
  33.  
  34. part_text = MIMEText(text, 'plain')
  35. part_html = MIMEText(html, 'html')
  36. part_file = MIMEBase('application', 'octet-stream; name="{}"'.format(basename))
  37. part_file.set_payload(open(filepath,"rb").read() )
  38. part_file.add_header('Content-Description', basename)
  39. part_file.add_header('Content-Disposition', 'attachment; filename="{}"; size={}'.format(basename, filesize))
  40. encoders.encode_base64(part_file)
  41.  
  42. msg.attach(part_text)
  43. msg.attach(part_html)
  44. msg.attach(part_file)
  45.  
  46. mail = smtplib.SMTP_SSL(server)
  47. mail.login(user, password)
  48. mail.sendmail(sender, recipients, msg.as_string())
  49. mail.quit()

Описание скрипта:

Описание
4 Импорт модуля smtplib для работы с почтой по протоколу SMTP. Данный модуль является стандартным и не требует отдельной установки.
5 Импорт модуля os для работы с операционной системой.
6 Импорт атрибута email.mime.multipart из модуля MIMEMultipart. Будет использоваться для отправки сообщения в двух форматах (text и html).
7 Импорт атрибута email.mime.text из модуля MIMEText. С его помощью будем задавать формат сообщения.
8 Импорт атрибута email.mime.base из модуля MIMEBase. С его помощью будем добавлять вложения в письмо.
9 Импорт атрибута из модуля encoders для кодирования. Будем применять для преобразование файла при его отправке в качестве вложения.
10 Импорт атрибута platform из модуля python_version для определения версии Python.
12 Определение переменной с именем SMTP-сервера для подключения.
13 Задаем логин для подключения к серверу. Если сервер не требует аутентификации, оставляем пустым.
14 Пароль для подключения к серверу. Если сервер не требует аутентификации, оставляем пустым.
16 Перечисляем получателей нашего письма
17 Адрес отправителя.
18 Тема нашего письма.
19 Тело письма в текстовом формате.
20 Тело письма в формате html.
22 Создаем переменную с полным путем до файла, который будем прикреплять к письму.
23 Извлекаем имя файла (отбрасываем путь). Значение сохраняем в переменной basename.
24 Рассчитываем размер файла. Результат записываем в переменную filesize.
26 Создаем нашу сообщение, как многокомпонентный объект (письмо будет состоять из текстовой части, html и вложения).
27 - 32 Задаем заголовки для письма.
34 Формируем тело в текстовом формате.
35 Формируем тело в формате html.
36 Формируем тело для вложения.
37 Загружаем в тело письма файл.
38 Добавляем заголовок к Content-Description контентной части письма. Указываем просто имя файла.
39 Добавляем заголовок к Content-Disposition контентной части письма. Указываем имя файла и его размер.
40 Кодируем файл в base64.
42 Присоединяем к письму сформированное тело в текстовом формате.
43 Присоединяем к письму сформированное тело в формате html.
44 Присоединяем к письму сформированное тело с вложением.
46 Создаем SMTP сессию по защищенному протоколу с сервером отправки. Саму сессию записываем в переменную mail, по которой можно будем обращаться к объектам smtplib. Полный их перечень можно найти в документации python. если нам необходимо создать незащищенную сессию, то можно заменить строку на mail = smtplib.SMTP(server, 25), где 25 — стандартный порт для отправки почты.
47 Авторизуемся на сервере. Если наш сервер позволяет анонимную отправку сообщений, то данную строчку можно пропустить.
48 Отправляем письмо.
49 Закрываем сессию.

Чтение почты

Разобьем процесс на несколько шагов.

Подключение к почтовому ящику

Чтение электронной почты начинается с подключения к почтовому ящику на сервере. Для этого используем такой код:

  1. #!/usr/bin/env python3
  2. # -*- encoding: utf-8 -*-
  3.  
  4. import imaplib
  5.  
  6. mail = imaplib.IMAP4_SSL('imap.gmail.com')
  7. mail.login('example@gmail.com', 'password_mail')
  8.  
  9. mail.list()
  10. mail.select("inbox")

Описание скрипта:

Описание
1 Определяем интерпретатор для нашего скрипта
2 Явно указываем кодировку
4 Импортируем модуль imaplib для возможности подключения к почтовому ящику по IMAP.
6 Создаем сессию для подключения к почтовому ящику по IMAP и заносим ее в переменную mail
7 Подключаемся к почтовому ящику по IMAP с использованием учетной записи example@gmail.com.
9 Выводим список папок в почтовом ящике
10 Выбираем для работы папку входящие (inbox)

Пробуем запустить наш скрипт:

/scripts/mail.py

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

Поиск нужного письма

Найдем письмо, с которым будем работать — в данном примере, последнее:

  1. result, data = mail.search(None, "ALL")
  2.  
  3. ids = data[0]
  4. id_list = ids.split()
  5. latest_email_id = id_list[-1]
  6.  
  7. result, data = mail.fetch(latest_email_id, "(RFC822)")
  8. raw_email = data[0][1]
  9. raw_email_string = raw_email.decode('utf-8')

Описание скрипта:

Описание
1 Получаем массив со списком найденных почтовых сообщений
3 Сохраняем в переменную ids строку с номерами писем
4 Получаем массив номеров писем
5 Задаем переменную latest_email_id, значением которой будет номер последнего письма
7 Получаем письмо с идентификатором latest_email_id (последнее письмо).
8 В переменную raw_email заносим необработанное письмо
9 Переводим текст письма в кодировку UTF-8 и сохраняем в переменную raw_email_string

Пробуем запустить скрипт — он тоже должен вернуть пустой ответ. Мы можем добавить в конец нашего скрипта такую строку:

...
print(raw_email_string)

... это позволит увидеть тело письма. После проверки удаляем строку.

Чтение заголовков

Если нам нужны заголовки письма, выполняем такой блок кода:

  1. import email
  2. email_message = email.message_from_string(raw_email_string)
  3.  
  4. print(email_message['To'])
  5. print(email.utils.parseaddr(email_message['From']))
  6. print(email_message['Date'])
  7. print(email_message['Message-Id'])

Описание скрипта:

Описание
1 Импортируем модуль email для получения заголовков и тела писем
2 Получаем заголовки и тело письма и заносим результат в переменную email_message. Обратите внимание, что мы используем переменную raw_email_string, в которую на этапе выше занесли необработанное письмо.
4 Выводим на экран заголовок To (кому отправлено письмо)
5 Выводим на экран заголовок From (от кого отправлено письмо)
6 Выводим на экран заголовок Date (дата отправки письма)
7 Выводим на экран заголовок Message-Id (идентификатор письма)

* в данном скрипте приведен пример нескольких заголовков. Весь массив с заголовками можно вывести строкой print(email_message).

Чтение тела письма

Получаем текст письма:

  1. import email
  2. email_message = email.message_from_string(raw_email_string)
  3.  
  4. if email_message.is_multipart():
  5.     for payload in email_message.get_payload():
  6.         body = payload.get_payload(decode=True).decode('utf-8')
  7.         print(body)
  8. else:    
  9.     body = payload.get_payload(decode=True).decode('utf-8')
  10.     print(body)

Описание скрипта:

Описание
1 Импортируем модуль email для получения заголовков и тела писем
2 Получаем заголовки и тело письма и заносим результат в переменную email_message. Обратите внимание, что мы используем переменную raw_email_string, в которую на этапе выше занесли необработанное письмо.
4 - 7 Проверяем, является ли письмо многокомпонентным. Если да, то выводим по очереди на экран значения каждого компонента. Предварительно, перекодируем текст в UTF-8.
8 - 10 Если письмо не многокомпонентное, выводим его содержимое.

Управление почтовыми сообщениями

С помощью Python по IMAP мы можем копировать почтовые сообщения, удалять их, перемещать, а также управлять папками на сервере.

Копирование

Для копирования предусмотрен специальный метод copy: 

mail.copy(mail_id, 'INBOX/Completed')

* в данном примере мы должны вызвать метод copy для нашей почтовой сессии mail; для копирования конкретного письма мы передаем его идентификатор (mail_id); копия письма окажется в каталоге Completed, который находится в каталоге Входящие (INBOX).

Удаление

Для удаления письма, сначала необходимо пометить его на удаление. После выполнить удаление помеченных почтовых сообщений:

mail.store(mail_id, '+FLAGS', '\\Deleted')
mail.expunge()

* в данном примере мы вызываем метод store сессии mail, устанавливаем флаг Deleted для письма с идентификатором mail_id.

Перемещение

Перемещение можно выполнить копированием с последующим удалением:

copy_res = mail.copy(mail_id, 'INBOX/Completed')
if copy_res[0] == 'OK':
    mail.store(mail_id, '+FLAGS', '\\Deleted')
    mail.expunge()

* в данном примере мы копируем письмо с mail_id в INBOX/Completed; если копирование выполнено успешно, то письмо помечается на удаление и удаляется окончательно.

Пример скрипта

Пример скрипта с подключением к почте, поиском писем в папке «Входящие», перебором всех писем с последующим их переносом в корневой каталог Completed:

  1. #!/usr/bin/env python3
  2. # -*- encoding: utf-8 -*-
  3.  
  4. import imaplib
  5.  
  6. mail = imaplib.IMAP4_SSL('imap.yandex.ru')
  7. mail.login('example@yandex.ru', 'password_mail')
  8.  
  9. mail.list()
  10. mail.select("inbox", readonly = False)
  11. result, data = mail.search(None, "ALL")
  12. ids = data[0]
  13. id_list = ids.split()
  14.  
  15. for mail_id in id_list:
  16.     copy_res = mail.copy(mail_id, 'Completed')
  17.     if copy_res[0] == 'OK':
  18.         delete_res = mail.store(mail_id, '+FLAGS', '\\Deleted')
  19.  
  20. mail.expunge()

Описание скрипта:

Описание
1 Определяем интерпретатор для нашего скрипта
2 Явно указываем кодировку
4 Импортируем модуль imaplib для возможности подключения к почтовому ящику по IMAP
6 Создаем сессию для подключения к почтовому ящику по IMAP и заносим ее в переменную mail
7 Подключаемся к почтовому ящику по IMAP с использованием учетной записи example@yandex.ru
9 Выводим список папок в почтовом ящике
10 Выбираем для работы папку входящие (inbox). Явно указываем, что мы подключаемся к каталогу в режиме не только для чтения
11 Получаем массив со списком найденных почтовых сообщений
12 Сохраняем в переменную ids строку с номерами писем
13 Получаем массив номеров писем
15 Делаем перебор нашего массива id_list (список идентификаторов писем)
16 Копируем письмо с идентификатором mail_id в каталог Completed. Резуьтат заносим в переменную copy_res
17 - 18 Проверяем результат копирования письма. Если письмо успешно скопировано, то устанавливаем на него флаг Deleted (для удаления)
20 Удаляем все письма с установленным флагом Deleted
# Почта
Дмитрий Моск — частный мастер
Была ли полезна вам эта инструкция?

Да            Нет