Ответ показывает, как отправлять электронную почту с помощью API gmail и python. Также обновлен ответ на отправку писем с приложением.
Gmail API & OAuth -> не нужно сохранять имя пользователя и пароль в скрипте.
В первый раз, когда скрипт открывает браузер для авторизации сценария и будет хранить учетные данные локально (он не будет хранить имя пользователя и пароль). Последующие прогоны не понадобятся браузеру и могут отправлять электронные письма прямо.
С помощью этого метода вы не получите ошибки, как SMTPException ниже, и нет никакой необходимости, чтобы обеспечить доступ для менее защищенных приложений:
raise SMTPException("SMTP AUTH extension not supported by server.")
smtplib.SMTPException: SMTP AUTH extension not supported by server.
Вот шаги для отправки электронной почты с помощью GMAIL API:
(Мастер ссылка here, Подробнее here)
Шаг 2: Установите библиотеку Google Client
pip install --upgrade google-api-python-client
Шаг 3: Используйте следующий скрипт для отправки электронной почты (просто изменить переменные в главной функции)
import httplib2
import os
import oauth2client
from oauth2client import client, tools
import base64
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from apiclient import errors, discovery
import mimetypes
from email.mime.image import MIMEImage
from email.mime.audio import MIMEAudio
from email.mime.base import MIMEBase
SCOPES = 'https://www.googleapis.com/auth/gmail.send'
CLIENT_SECRET_FILE = 'client_secret.json'
APPLICATION_NAME = 'Gmail API Python Send Email'
def get_credentials():
home_dir = os.path.expanduser('~')
credential_dir = os.path.join(home_dir, '.credentials')
if not os.path.exists(credential_dir):
os.makedirs(credential_dir)
credential_path = os.path.join(credential_dir,
'gmail-python-email-send.json')
store = oauth2client.file.Storage(credential_path)
credentials = store.get()
if not credentials or credentials.invalid:
flow = client.flow_from_clientsecrets(CLIENT_SECRET_FILE, SCOPES)
flow.user_agent = APPLICATION_NAME
credentials = tools.run_flow(flow, store)
print('Storing credentials to ' + credential_path)
return credentials
def SendMessage(sender, to, subject, msgHtml, msgPlain, attachmentFile=None):
credentials = get_credentials()
http = credentials.authorize(httplib2.Http())
service = discovery.build('gmail', 'v1', http=http)
if attachmentFile:
message1 = createMessageWithAttachment(sender, to, subject, msgHtml, msgPlain, attachmentFile)
else:
message1 = CreateMessageHtml(sender, to, subject, msgHtml, msgPlain)
result = SendMessageInternal(service, "me", message1)
return result
def SendMessageInternal(service, user_id, message):
try:
message = (service.users().messages().send(userId=user_id, body=message).execute())
print('Message Id: %s' % message['id'])
return message
except errors.HttpError as error:
print('An error occurred: %s' % error)
return "Error"
return "OK"
def CreateMessageHtml(sender, to, subject, msgHtml, msgPlain):
msg = MIMEMultipart('alternative')
msg['Subject'] = subject
msg['From'] = sender
msg['To'] = to
msg.attach(MIMEText(msgPlain, 'plain'))
msg.attach(MIMEText(msgHtml, 'html'))
return {'raw': base64.urlsafe_b64encode(msg.as_string())}
def createMessageWithAttachment(
sender, to, subject, msgHtml, msgPlain, attachmentFile):
"""Create a message for an email.
Args:
sender: Email address of the sender.
to: Email address of the receiver.
subject: The subject of the email message.
msgHtml: Html message to be sent
msgPlain: Alternative plain text message for older email clients
attachmentFile: The path to the file to be attached.
Returns:
An object containing a base64url encoded email object.
"""
message = MIMEMultipart('mixed')
message['to'] = to
message['from'] = sender
message['subject'] = subject
messageA = MIMEMultipart('alternative')
messageR = MIMEMultipart('related')
messageR.attach(MIMEText(msgHtml, 'html'))
messageA.attach(MIMEText(msgPlain, 'plain'))
messageA.attach(messageR)
message.attach(messageA)
print("create_message_with_attachment: file: %s" % attachmentFile)
content_type, encoding = mimetypes.guess_type(attachmentFile)
if content_type is None or encoding is not None:
content_type = 'application/octet-stream'
main_type, sub_type = content_type.split('/', 1)
if main_type == 'text':
fp = open(attachmentFile, 'rb')
msg = MIMEText(fp.read(), _subtype=sub_type)
fp.close()
elif main_type == 'image':
fp = open(attachmentFile, 'rb')
msg = MIMEImage(fp.read(), _subtype=sub_type)
fp.close()
elif main_type == 'audio':
fp = open(attachmentFile, 'rb')
msg = MIMEAudio(fp.read(), _subtype=sub_type)
fp.close()
else:
fp = open(attachmentFile, 'rb')
msg = MIMEBase(main_type, sub_type)
msg.set_payload(fp.read())
fp.close()
filename = os.path.basename(attachmentFile)
msg.add_header('Content-Disposition', 'attachment', filename=filename)
message.attach(msg)
return {'raw': base64.urlsafe_b64encode(message.as_string())}
def main():
to = "[email protected]"
sender = "[email protected]"
subject = "subject"
msgHtml = "Hi<br/>Html Email"
msgPlain = "Hi\nPlain Email"
SendMessage(sender, to, subject, msgHtml, msgPlain)
# Send message with attachment:
SendMessage(sender, to, subject, msgHtml, msgPlain, '/path/to/file.pdf')
if __name__ == '__main__':
main()
Подсказка для запуск этого кода в Linux, без браузера:
Если в вашей среде linux нет браузера для завершения процесса авторизации в первый раз, вы можете запустить один раз на вашем ноутбуке (Mac или Windows), а затем скопируйте учетные данные на конечную Linux-машину. Полномочия, как правило, хранятся в следующей назначения:
~/.credentials/gmail-python-email-send.json
У вас есть googled "python gmail oauth library"? – Selcuk
Я не вижу каких-либо ослабленных замечаний по безопасности в этом [Gmail Python API Quickstart] (https://developers.google.com/gmail/api/quickstart/python), в котором используется oauth2 – davedwards
, что подтверждает этот вопрос, потому что избыток документации Google затушевывает возможную простоту решения. Исследование поиска подходящего ответа не было очевидным или простым. Кроме того, @Selcuk, просто поиграть в «библиотеку python oauth» - это не ответ, или даже один шаг, удаленный от разработки ответа. – Andrew