如何通过 python 的 gmail-api 发送 HTML 格式的电子邮件

How do I send HTML Formatted emails, through the gmail-api for python

使用 GMail API Example: Send Mail 中的示例代码,并在遵循身份验证规则后,通过 gmail 帐户发送以编程方式生成的电子邮件就足够简单了。该示例中不明显的是如何将该电子邮件设置为 HTML 格式。


问题

如何在我的 gmail 中设置 HTML 格式-api 发送邮件,使用 python?

我有这个...

message_body = "Hello!\nYou've just received a test message!\n\nSincerely,\n-Test Message Generator\n"

我希望它是这样的...

Hello!
You've just received a test message!

Sincerely,
-Test Message Generator

来自 GMail 的示例源代码-API

下面是示例的略微修改版本,但仍然有效:

import argparse
import base64
from pprint import pformat
from pprint import pprint
import httplib2
import os
from email.MIMEMultipart import MIMEMultipart
from email.MIMEText import MIMEText

from apiclient import discovery
from oauth2client import client
from oauth2client import tools
from oauth2client.file import Storage


SCOPES = 'https://mail.google.com/'
CLIENT_SECRET_FILE = 'client_secret.json'
APPLICATION_NAME = 'Test EMail App'


def get_credentials():
    """Gets valid user credentials from storage.

    If nothing has been stored, or if the stored credentials are invalid,
    the OAuth2 flow is completed to obtain the new credentials.

    Returns:
        Credentials, the obtained credential.
    """

    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-quickstart.json')

    store = 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
        if flags:
            credentials = tools.run_flow(flow, store, flags)
        else: # Needed only for compatibility with Python 2.6
            credentials = tools.run(flow, store)
        print('Storing credentials to ' + credential_path)
    return credentials

def create_message(sender, to, cc, subject, message_text):
    """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.
    message_text: The text of the email message.

    Returns:
    An object containing a base64url encoded email object.
    """
    print(sender + ', ' + to + ', ' + subject + ', ' + message_text)
    message = MIMEText(message_text)
    message['to'] = to
    message['from'] = sender
    message['subject'] = subject
    message['cc'] = cc
    pprint(message)
    return {'raw': base64.urlsafe_b64encode(message.as_string())}

def send_message(service, user_id, message_in):
    """Send an email message.

    Args:
    service: Authorized Gmail API service instance.
    user_id: User's email address. The special value "me"
    can be used to indicate the authenticated user.
    message: Message to be sent.

    Returns:
    Sent Message.
    """
    pprint(message_in)
    try:
        message = (service.users().messages().send(userId=user_id, body=message_in).execute())
        pprint(message)
        print ('Message Id: %s' % message['id'])
        return message
    except errors.HttpError, error:
        print ('An error occurred: %s' % error)

def main(cli):
    """Shows basic usage of the Gmail API.

    Creates a Gmail API service object and outputs a list of label names
    of the user's Gmail account.
    """


    credentials = get_credentials()
    http = credentials.authorize(httplib2.Http())
    service = discovery.build('gmail', 'v1', http=http)

    email_msg = create_message(cli.addr_from, cli.addr_to, cli.addr_cc, cli.subject, cli.message)
    msg_out = service.users().messages().send(userId = 'me', body = email_msg).execute()
    pprint(msg_out)


if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument('-m', '--message',   help = 'The message to send in the email', default='<MESSAGE = unfinished>')
    parser.add_argument('-t', '--addr_to',   help = 'the list of comma separated emails to send', default='cbsd.tools@gmail.com')
    parser.add_argument('-s', '--subject',   help = 'the email subject', default='<SUBJECT = undefined>')
    parser.add_argument('-c', '--addr_cc',   help = 'email CC\'s', default='')
    parser.add_argument('-f', '--addr_from', help = 'Email address to send from', default='cbsd.tools@gmail.com')
    cli = parser.parse_args()
    pprint(dir(cli))

    main(cli)

尽我所能,使用此代码及其变体,我无法获得 html 格式化代码,也无法获得简单的转义字符以在需要的地方创建回车符 returns是。


这是无效的

尝试以下方法也无效:

  1. 正在修改 line 69 以添加额外的消息字典参数...即。
    • {'raw': base64.urlsafe_b64encode(message.as_string()), 'payload': {'mimeType': 'text/html'}}
    • GMail API Docs
    • 中所述
  2. 在消息文本中添加各种转义反斜杠:
    • \n... 即:\n\\n 并且只是呈现为那些确切的字符
    • 添加 <br> </br> <br/> 并没有添加新行,只是呈现为那些确切的字符
    • 添加 \r 并没有添加新行,只是呈现为那个确切的字符

这不是重复问题的原因

经过大量挖掘后,我开始查看消息处理的 python 端,并注意到 python object 实际上正在构建消息以被发送到 gmail-api 消息 object 构造函数中进行 base64 编码。

See line 63 from above: message = MIMEText(message_text)

在尝试修改 header 值和有效负载字典(它是 message object), 是设置 (line 63):

  • message = MIMEText(message_text, 'html') <-- 添加 'html' 作为 MIMEText object 构造函数的第二个参数

Google 为他们的 gmail API 提供的默认代码只告诉你如何发送纯文本电子邮件,但他们隐藏了他们是如何发送的这样做。 翼... message = MIMEText(message_text)

我不得不查找 python class email.mime.text.MIMEText object。 在那里您将看到 MIMEText object:

构造函数的定义
  • class email.mime.text.MIMEText(_text[ _subtype[ _charset]]) 我们想明确地将一个值传递给 _subtype。在这种情况下,我们要传递:'html' 作为 _subtype.

现在,Google 或 Python mime.text.MIMEText 不会再对您的消息应用意外的自动换行=65=]


固定码

def create_message(sender, to, cc, subject, message_text):
    """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.
    message_text: The text of the email message.

    Returns:
    An object containing a base64url encoded email object.
    """
    print(sender + ', ' + to + ', ' + subject + ', ' + message_text)
    message = MIMEText(message_text,'html')
    message['to'] = to
    message['from'] = sender
    message['subject'] = subject
    message['cc'] = cc
    pprint(message)
    return {'raw': base64.urlsafe_b64encode(message.as_string())}

试试这个:

    def CreateMessage(emailSubject, emailTo, emailFrom, message_body, emailCc, html_content=None):
        try:
            message = MIMEMultipart('alternative')
            message['to'] = emailTo
            message['from'] = emailFrom
            message['subject'] = emailSubject
            message['Cc'] = emailCc
            body_mime = MIMEText(message_body, 'plain')
            message.attach(body_mime)
            if html_content:
                html_mime = MIMEText(html_content, 'html')
                message.attach(html_mime)
            return {
                'raw': base64.urlsafe_b64encode(
                    bytes(
                        message.as_string(),
                        "utf-8")).decode("utf-8")}
        except Exception as e:
            print('Error in CreateMessage()', e)
            return '400'