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

Posted

技术标签:

【中文标题】如何通过 gmail-api for python 发送 HTML 格式的电子邮件【英文标题】:How do I send HTML Formatted emails, through the gmail-api for python 【发布时间】:2017-05-15 04:20:44 【问题描述】:

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


问题

如何使用 python 在我的 gmail-api 发送消息中获取 HTML 格式?

我有这个...

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 格式的代码,也无法获得简单的转义字符来在需要的位置创建回车。


这是没有用的

尝试以下方法也无效:

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

这不是重复问题的原因

This SO link deals with multipart/signed messages 我对多部分消息传递不感兴趣,因为它是一个不雅的解决方案。我希望整个消息是HTML。 此外,此链接没有接受的答案,并且存在的一个答案是非答案,因为它只是表明他们正在向 Google 发送问题陈述。 这个处理的是c#,因为我是用python写的,所以我对它不感兴趣 This one also uses SMTP which I'm not interested in This one is for Rails

【问题讨论】:

【参考方案1】:

在进行了大量研究之后,我开始研究消息处理的 python 端,并注意到一个 python 对象实际上正在将要发送的消息构造为 base64 编码到 gmail-api 消息对象构造函数中。

从上面查看第 63 行:message = MIMEText(message_text)

最终对我有用的一个技巧,在尝试修改标头值和有效负载字典(它是 message 对象的成员)之后,设置 (@987654324 @):

message = MIMEText(message_text, 'html') 'html'作为MIMEText对象构造函数的第二个参数

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

我必须查找 python 类 email.mime.text.MIMEText 对象。 在那里您将看到 MIMEText 对象的构造函数的定义:

类 email.mime.text.MIMEText(_text[, _subtype[, _charset]]) 我们想明确地将一个值传递给_subtype。在这种情况下,我们希望通过:'html' 作为_subtype

现在,Google 或 Python mime.text.MIMEText 对象将不再有意外的自动换行应用于您的消息


固定密码

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())

【讨论】:

【参考方案2】:

试试这个:

    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'

【讨论】:

异常处理是一个很好的调用,但总的来说,我对当前应用程序的多部分消息传递不感兴趣。我只需要 html 格式。 我只是给了一个适合两者的功能:) 对,如果需要的话,那就太棒了。在问题的最后,我特别声明我对多部分 mime 类型不感兴趣,这个问题与纯 html 格式有关。对于更健壮、更复杂的 API 方法来说,这是一个很好的答案,这对这个项目和问题来说是过度设计的。我确实喜欢它作为一个不同问题的一般答案,尤其是 try catch 位。 很好的答案。 - 如果 SO 不是为了让具有广泛需求的用户社区受益 - 那么它的意义何在?即使对于 this 问题,这也是一个很好的答案。 @ hmedia1,问题本身已经提供了一个链接,可以通过多部分消息解决这个问题 - 所以我已经承认它是一个可用的解决方案,并给出了如何以这种方式解决它的链接 - 如以及声明对于这个解决方案我不感兴趣。我不认为我的批评是不公平的。

以上是关于如何通过 gmail-api for python 发送 HTML 格式的电子邮件的主要内容,如果未能解决你的问题,请参考以下文章

如何仅为社交电子邮件创建 gmail-api 访问范围

Gmail-api 附带电子邮件时如何收到通知

如何使用 java 和 google gmail-api 获取收到的邮件? [关闭]

如何在没有手动浏览器身份验证的情况下从 Meteor.js 应用程序对 GMail-api 进行 oauth (2) 身份验证?

请求 gmail-api 时出现 HttpError 429:超出用户速率限制

在我们的谷歌云 Pub/Sub 发布者权限之外提供 Gmail-API