【问题标题】:Send mail in Gmail with python使用 python 在 Gmail 中发送邮件
【发布时间】:2020-07-12 15:28:11
【问题描述】:

我正在尝试使用 Gmail API 发送一封简单的测试电子邮件,但我找到的每个代码示例都不断收到相同的错误。

我现在的代码是这样的:

def validationService():
    SCOPES = ['https://mail.google.com/']
    SERVICE_ACCOUNT_FILE = 'creds.json'
    creds = service_account.Credentials.from_service_account_file(SERVICE_ACCOUNT_FILE, scopes=SCOPES)

    service = build('gmail', 'v1', credentials=creds)
    return service


def SendMessage(service, user_id, message):
  try:
    message = (service.users().messages().send(userId=user_id, body=message).execute())
    print('Message Id:',message['id'])
    return message
  except errors.HttpError as error:
    print('An error occurred:', error)


def CreateMessage(sender, to, subject, message_text):
  message = MIMEText(message_text)
  message['to'] = to
  message['from'] = sender
  message['subject'] = subject
  return {'raw': base64.urlsafe_b64encode(message.as_string().encode()).decode()}

service = validationService()

email = CreateMessage("sendermail@gmail.com", "receiverrmail@gmail.com", "Test", "This is a test")

sent = SendMessage(service, "sendermail@gmail.com", email)

返回

>>> An error occurred: <HttpError 400 when requesting https://www.googleapis.com/gmail/v1/users/me/messages/send?alt=json returned "Bad Request">

我也不明白 CreateMessage 中的“sender”参数和 SendMessage 中的 userId 之间的区别。

如果有目的,我使用的是服务帐户凭据

- 谢谢!

【问题讨论】:

    标签: python google-api gmail-api google-api-python-client service-accounts


    【解决方案1】:

    service accountGmail API 一起使用时。您需要设置domain-wide delegation,它允许服务帐户模拟您的G Suite domain 中的任何用户。因此,您必须拥有 G Suite 帐户才能使用域范围授权,如文档所述:

    如果您有 G Suite 域(例如,如果您使用 G Suite),则 G Suite 域的管理员可以授权应用程序 代表 G Suite 域中的用户访问用户数据。

    那么现在,您为什么需要冒充用户(真人)?这是因为服务帐户是一个机器人(不是真人),用于服务器到服务器的交互,使您的应用程序可以调用 Google API,尽管服务帐户有一个名为 client_email 的参数,它有一个像name@project-randomnumber.iam.gserviceaccount.com 这样的结构,它不是属于真实人的真实电子邮件(我知道有点令人困惑)。

    话虽如此,我对您的代码进行了一些更改。首先,我修改了您的 validationService 函数,以便使用域范围委托构建服务。

    def validationService():
        # Set the crendentials 
        credentials = service_account.Credentials.\
            from_service_account_file(SERVICE_ACCOUNT_FILE, scopes= SCOPES)
        # Delegate the credentials to the user you want to impersonate
        delegated_credentials = credentials.with_subject(USER_EMAIL)
        service = discovery.build('gmail', 'v1', credentials=delegated_credentials)
        return service
    

    在您的SendMessage 函数中不一定要传递要发送电子邮件的用户。使用 me 作为Users.messages: send Parameters 状态就足够了:

    userId 字符串 用户的电子邮件地址。我的特殊价值可以是 用于表示经过身份验证的用户。

    def SendMessage(service, message):
        message = service.users().messages().send(userId="me", body=message).execute()
        return message
    

    您最后的整个代码可能如下所示:

    from googleapiclient import discovery, errors
    from oauth2client import file, client, tools
    from google.oauth2 import service_account
    from email.mime.text import MIMEText
    import base64
    
    SERVICE_ACCOUNT_FILE = 'service_account.json'
    SCOPES = [' https://mail.google.com/']
    # The user we want to "impersonate"
    USER_EMAIL = "user@domain"
    
    
    def validationService():
        # Set the crendentials 
        credentials = service_account.Credentials.\
            from_service_account_file(SERVICE_ACCOUNT_FILE, scopes= SCOPES)
        # Delegate the credentials to the user you want to impersonate
        delegated_credentials = credentials.with_subject(USER_EMAIL)
        service = discovery.build('gmail', 'v1', credentials=delegated_credentials)
        return service
    
    
    def SendMessage(service, message):
        message = service.users().messages().send(userId="me", body=message).execute()
        return message
    
    
    def CreateMessage(sender, to, subject, message_text):
      message = MIMEText(message_text)
      message['to'] = to
      message['from'] = sender
      message['subject'] = subject
      return {'raw': base64.urlsafe_b64encode(message.as_string().encode()).decode()}
    
    
    def main():
        try:
            service = validationService()
            email = CreateMessage(USER_EMAIL, "receiverrmail@domain", "Test", "This is a test")
            email_sent = SendMessage(service, email)
            print('Message Id:', email_sent['id'])
        except errors.HttpError as err:
            print('\n---------------You have the following error-------------')
            print(err)
            print('---------------You have the following error-------------\n')
    
    
    if __name__ == '__main__':
        main()
    

    通知

    您还需要通过在您的 G Suite 帐户上设置 Managing API client access 来允许您的服务帐户在使用域范围委派时访问 Google 的 API。

    【讨论】:

    • 如果我有一个 GSuite 域,这将起作用。我认为谷歌不会让你使用自己的帐户这很奇怪。我将不得不创建 OAuth 凭据。谢谢!
    【解决方案2】:

    您不会伤脑筋……只需使用电子邮件和 smtplib 库即可。如果你问我,通过 Python 发送电子邮件非常简单有趣。我在下面给出了我的编码,您应该基于此开发自己的代码。如果它有效,请告诉我 - 我会很高兴。在这里……

        import email, smtplib, os, time, fnmatch
        from datetime import date, timedelta
        from email.mime.text import MIMEText
        from email.mime.image import MIMEImage
        from email.mime.multipart import MIMEMultipart
    
        def sendMailTo(recepient_id, today):
    
          login_id = 'myemailID@gmail.com'
          login_pwd = "mypassword"
          fro_ = login_id
          s_name = 'smtp.gmail.com'
          s_port = 587
    
          fileList = []
          fi_type = '*.pdf'
          file_path = "C:\\Myfolder\\Subfolder"
    
          srvr = smtplib.SMTP(s_name, s_port)
          srvr.ehlo()
          srvr.starttls()
          srvr.ehlo()
          srvr.login(login_id, login_pwd)
    
          sub = "Your subject here"
          # loading MIMEMultipart obj onto outer var
          outer = MIMEMultipart('alternative')
          outer["From"] = 'Your company/Personal Name'
          outer["To"] = recepient_id 
          outer['Subject'] = sub
    
          # storing only pdf files          
          fileList = fnmatch.filter(os.listdir(file_path), fi_type)
    
          for fi in fileList:
            fi_name = os.path.join(file_path, fi)
            fp = open(fi_name, 'rb')
            img = MIMEImage(fp.read(), _subtype='jpg')
            fp.close()
            img.add_header('Content-disposition', "attachment", filename = fi)
            outer.attach(img)
    
          #start sending email with attachment with original file name
          srvr.sendmail(fro_, recepient_id, outer.as_string())
    
        part = None
        outer = None
        srvr.quit()
    

    【讨论】:

    • 行得通!但是我必须停用谷歌的安全设置才能使其正常工作。我可以将它与其他邮寄服务一起使用。谢谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-07-12
    • 2011-11-24
    • 1970-01-01
    • 2011-02-16
    • 1970-01-01
    • 2020-12-16
    • 2015-09-28
    相关资源
    最近更新 更多