Использование языка Python для работы с почтой

Мы предоставляем услуги удаленного администрирования серверов

Использование языка 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 /scripts/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 = ‘[email protected]
  14. password = ‘password_mail’
  15.  
  16. recipients = [‘[email protected]’, ‘[email protected]’]
  17. sender = ‘[email protected]
  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(‘[email protected]’, ‘password_mail’)
  8.  
  9. mail.list()
  10. mail.select(«inbox»)

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

Описание
1Определяем интерпретатор для нашего скрипта
2Явно указываем кодировку
4Импортируем модуль imaplib для возможности подключения к почтовому ящику по IMAP.
6Создаем сессию для подключения к почтовому ящику по IMAP и заносим ее в переменную mail
7Подключаемся к почтовому ящику по IMAP с использованием учетной записи [email protected]
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[‘Subject’])
  8. print(email_message[‘Message-Id’])

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

Описание
1Импортируем модуль email для получения заголовков и тела писем
2Получаем заголовки и тело письма и заносим результат в переменную email_message. Обратите внимание, что мы используем переменную raw_email_string, в которую на этапе выше занесли необработанное письмо.
4Выводим на экран заголовок To (кому отправлено письмо)
5Выводим на экран заголовок From (от кого отправлено письмо)
6Выводим на экран заголовок Date (дата отправки письма)
7Выводим на экран заголовок Subject (тема письма)
8Выводим на экран заголовок 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(‘[email protected]’, ‘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 с использованием учетной записи [email protected]
9Выводим список папок в почтовом ящике
10Выбираем для работы папку входящие (inbox). Явно указываем, что мы подключаемся к каталогу в режиме не только для чтения
11Получаем массив со списком найденных почтовых сообщений
12Сохраняем в переменную ids строку с номерами писем
13Получаем массив номеров писем
15Делаем перебор нашего массива id_list (список идентификаторов писем)
16Копируем письмо с идентификатором mail_id в каталог Completed. Резуьтат заносим в переменную copy_res
17 — 18Проверяем результат копирования письма. Если письмо успешно скопировано, то устанавливаем на него флаг Deleted (для удаления)
20Удаляем все письма с установленным флагом Deleted

   Мы принимаем