Поскольку можно утверждать, что SMTP — это лучший вариант отправки почты из сценариев на языке Python, рассмотрим простую почтовую программу, иллюстрирующую его интерфейсы. Сценарий Python, представленный в примере 13.19, предназначен для использования из интерактивной командной строки. Он читает новое почтовое сообщение, вводимое пользователем, и отправляет почту по SMTP с помощью модуля Python smtplib.
Пример 13.19. PP4E\Internet\Email\smtpmail.py
#!/usr/local/bin/python
########################################################################### использует модуль Python почтового интерфейса SMTP для отправки сообщений; это простой сценарий одноразовой отправки — смотрите pymail, PyMailGUI и PyMailCGI, реализующие клиентов с более широкими возможностями взаимодействия с пользователями; смотрите также popmail.py — сценарий получения почты, и пакет mailtools, позволяющий добавлять вложения и форматировать сообщения с помощью стандартного пакета email;
###########################################################################
import smtplib, sys, email.utils, mailconfig
mailserver = mailconfig.smtpservername # например: smtp.rmi.net
From = input(‘From? ‘).strip() # или импортировать из mailconfig
To = input(‘To? ‘).strip() # например: python-list@python.org
Tos = To.split(‘;’) # допускается список получателей
Subj = input(‘Subj? ‘).strip()
Date = email.utils.formatdate() # текущие дата и время, rfc2822 # стандартные заголовки, за которыми следует пустая строка и текст text = (‘From: %s\nTo: %s\nDate: %s\nSubject: %s\n\n’ % (From, To, Date, Subj)) print(‘Type message text, end with line=[Ctrl+d (Unix), Ctrl+z (Windows)]’) while True:
line = sys.stdin.readline() if not line:
break # выход по ctrl—d/z
#if line[:4] == ‘From’:
# line = ‘>’ + line # серверы могут экранировать
text += line
print(‘Connecting…’)
server = smtplib.SMTP(mailserver) # соединиться без регистрации failed = server.sendmail(From, Tos, text) server.quit()
if failed: # smtplib может возбуждать исключения
print(‘Failed recipients:’, failed) # но здесь они не обрабатываются else:
print(‘No errors.’)
print(‘Bye.’)
Большая часть этого сценария реализует интерфейс пользователя — вводится адрес отправителя («From»), один или более адресов получателя («To», разделенные символом «;») и строка темы сообщения. Дата отправки формируется с помощью стандартного модуля Python time, строки стандартных заголовков форматируются, и цикл while читает строки сообщения, пока пользователь не введет символ конца файла (Ctrl+Z в Windows, Ctrl+D в Linux).
Для надежности убедитесь, что добавили пустую строку между строками заголовков и текстом тела сообщения, — этого требует протокол SMTP, и некоторые серверы SMTP считают это обязательным. Наш сценарий удовлетворяет это требование, вставляя пустую строку в виде пары символов \n\n в конце строки выражения форматирования — первый символ \n завершает текущую строку, а второй образует пустую строку. Модуль smtplib преобразует символы \n в пары символов \r\n в стиле Интернета перед передачей, поэтому здесь вполне можно использовать краткую форму. Далее в этой главе мы будем формировать наши сообщения с помощью пакета Python email, который автоматически выполняет такие тонкости.
В остальной части сценария происходят все чудеса SMTP: для отправки почты по SMTP нужно выполнить следующие два типа вызовов:
server = smtplib.SMTP(mailserver)
Создать экземпляр объекта SMTP, передав ему имя сервера SMTP, который первым отправит сообщение. Если при этом не возникнет исключения, значит, при возврате из конструктора вы окажетесь соединены с SMTP-сервером через сокет. Технически соединение с сервером устанавливает метод connect, но конструктор объекта SMTP автоматически вызывает этот метод, когда ему передается имя сервера.
failed = server.sendmail(From, Tos, text)
Вызвать метод sendmail объекта SMTP с передачей ему адреса отправителя, одного или более адресов получателя и собственно текста сообщения со всеми строками стандартных почтовых заголовков, какие вы зададите.
Закончив передачу, обязательно вызовите метод quit, чтобы отключиться от сервера и завершить сеанс. Обратите внимание, что при неудаче метод sendmail может возбудить исключение или возвратить список адресов получателей, которые не были приняты; сценарий обрабатывает последний случай, но позволяет исключительным ситуациям прервать работу сценария с выводом сообщения об ошибке.
Одна тонкая особенность: вызов метода quit объекта SMTP после возбуждения исключения модулем sendmail может иногда не действовать — метод quit фактически может зависнуть до истечения тайм-аута, если операция передачи потерпела неудачу и оставила интерфейс в непредусмотренном состоянии. Например, такое положение вещей может возникнуть в случае появления ошибок кодирования Юникода при трансляции исходящего сообщения в байты по схеме ASCII (запрос сброса rset в данном случае также зависает). Альтернативный метод close просто закрывает сокет на стороне клиента, не пытаясь отправить команду завершения на сервер — метод quit вызывает метод close на последнем этапе (предполагается, что команда завершения может быть отправлена!).
Дополнительно объекты SMTP предоставляют методы, не использованные в этом примере:
• server.login(user, password) предоставляет интерфейс к серверам SMTP, которые требуют и поддерживают ау тен ти фи ка цию; пример использования этого метода вы найдете в примере пакета mailtools далее в этой главе.
• server.starttls([keyfile[, certfile]]) переводит соединение SMTP в режим соблюдения безопасности на транспортном уровне (Transport Layer Security, TLS) — все команды шифруются с использованием модуля ssl, реализующего обертку SSL сокетов, при условии, что сервер поддерживает этот режим.
Обращайтесь к руководству по библиотеке Python за дополнительной информацией об этих и других методах, не упомянутых здесь.
Использованная литература:
Марк Лутц — Программирование на Python, 4-е издание, II том, 2011