【问题标题】:How to send an email with Gmail as provider using Python?如何使用 Python 以 Gmail 作为提供商发送电子邮件?
【发布时间】:2012-04-26 05:00:28
【问题描述】:

我正在尝试使用 python 发送电子邮件 (Gmail),但出现以下错误。

Traceback (most recent call last):  
File "emailSend.py", line 14, in <module>  
server.login(username,password)  
File "/usr/lib/python2.5/smtplib.py", line 554, in login  
raise SMTPException("SMTP AUTH extension not supported by server.")  
smtplib.SMTPException: SMTP AUTH extension not supported by server.

Python 脚本如下。

import smtplib
fromaddr = 'user_me@gmail.com'
toaddrs  = 'user_you@gmail.com'
msg = 'Why,Oh why!'
username = 'user_me@gmail.com'
password = 'pwd'
server = smtplib.SMTP('smtp.gmail.com:587')
server.starttls()
server.login(username,password)
server.sendmail(fromaddr, toaddrs, msg)
server.quit()

【问题讨论】:

  • 另外,对于 VPN 用户,如果问题仍然存在,请关闭您的 VPN。这对我有用。

标签: python email smtp gmail smtp-auth


【解决方案1】:

2022 年 2 月更新:

尝试 2 件事,以便能够使用 Python 发送 Gmail

  1. 允许安全性较低的应用:开启 ↓↓↓

    https://myaccount.google.com/lesssecureapps

  2. 允许访问您的 Google 帐户:开启(点按“继续”)↓↓↓

    https://accounts.google.com/DisplayUnlockCaptcha

【讨论】:

    【解决方案2】:

    意识到通过 Python 发送电子邮件有多么痛苦,因此我为它制作了一个扩展库。它还预先配置了 Gmail(因此您不必记住 Gmail 的主机和端口):

    from redmail import gmail
    gmail.user_name = "you@gmail.com"
    gmail.password = "<YOUR APPLICATION PASSWORD>"
    
    # Send an email
    gmail.send(
        subject="An example email",
        receivers=["recipient@example.com"],
        text="Hi, this is text body.",
        html="<h1>Hi, this is HTML body.</h1>"
    )
    

    当然你需要配置你的Gmail账户(不用担心,很简单):

    1. Set up 2-step-verification(如果尚未设置)
    2. Create an Application password
    3. 将应用程序密码放入gmail 对象并完成!

    Red Mail 实际上非常广泛(包括附件、嵌入图像、使用 cc 和 bcc 发送、使用 Jinja 的模板等),并且希望能够满足您对电子邮件发件人的所有需求。它也经过了很好的测试和记录。我希望你觉得它有用。

    安装:

    pip install redmail
    

    文档:https://red-mail.readthedocs.io/en/latest/

    源码:https://github.com/Miksus/red-mail

    请注意,Gmail 不允许更改发件人。发件人地址始终是您。

    【讨论】:

    • 很好地简化了这一点。奇迹般有效。荣誉。
    【解决方案3】:

    在直接跑到STARTTLS之前,你需要说EHLO

    server = smtplib.SMTP('smtp.gmail.com:587')
    server.ehlo()
    server.starttls()
    

    您还应该真正创建From:To:Subject: 邮件标题,用空行与邮件正文分隔,并使用CRLF 作为EOL 标记。

    例如

    msg = "\r\n".join([
      "From: user_me@gmail.com",
      "To: user_you@gmail.com",
      "Subject: Just a message",
      "",
      "Why, oh why"
      ])
    

    注意:

    为了使此功能起作用,您需要在您的 gmail 帐户配置中启用“允许安全性较低的应用程序”选项。否则,当 gmail 检测到非 Google 应用正在尝试登录您的帐户时,您将收到“严重安全警报”。

    【讨论】:

    • 调用server.sendmail(fromaddr, toaddrs, msg)第二个参数,toaddrs必须是一个列表,toaddrs = ['user_me@gmail.com']
    • 截至 2014 年 8 月,这会引发 smtplib.SMTPAuthenticationError: (534, '5.7.9 Application-specific password required.
    • 不过对我来说,我必须启用“应用”密码才能使用@google 帐户登录才能通过 python 发送电子邮件:support.google.com/accounts/answer/…
    • 这是一个如何给多人发邮件的链接:stackoverflow.com/questions/8856117/…
    • 我曾经通过 telnet 登录到 SMTP 服务器,并通过拼写错误发送了EHLO。在我多次尝试 HELO 但反应不同之后。花了几个小时才弄清楚 EHLO 实际上是 SMTP 理解的命令,我打错了。
    【解决方案4】:

    这个工程

    Create Gmail APP Password!

    创建之后,创建一个名为sendgmail.py的文件

    Then add this code:

    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    # =============================================================================
    # Created By  : Jeromie Kirchoff
    # Created Date: Mon Aug 02 17:46:00 PDT 2018
    # =============================================================================
    # Imports
    # =============================================================================
    import smtplib
    
    # =============================================================================
    # SET EMAIL LOGIN REQUIREMENTS
    # =============================================================================
    gmail_user = 'THEFROM@gmail.com'
    gmail_app_password = 'YOUR-GOOGLE-APPLICATION-PASSWORD!!!!'
    
    # =============================================================================
    # SET THE INFO ABOUT THE SAID EMAIL
    # =============================================================================
    sent_from = gmail_user
    sent_to = ['THE-TO@gmail.com', 'THE-TO@gmail.com']
    sent_subject = "Where are all my Robot Women at?"
    sent_body = ("Hey, what's up? friend!\n\n"
                 "I hope you have been well!\n"
                 "\n"
                 "Cheers,\n"
                 "Jay\n")
    
    email_text = """\
    From: %s
    To: %s
    Subject: %s
    
    %s
    """ % (sent_from, ", ".join(sent_to), sent_subject, sent_body)
    
    # =============================================================================
    # SEND EMAIL OR DIE TRYING!!!
    # Details: http://www.samlogic.net/articles/smtp-commands-reference.htm
    # =============================================================================
    
    try:
        server = smtplib.SMTP_SSL('smtp.gmail.com', 465)
        server.ehlo()
        server.login(gmail_user, gmail_app_password)
        server.sendmail(sent_from, sent_to, email_text)
        server.close()
    
        print('Email sent!')
    except Exception as exception:
        print("Error: %s!\n\n" % exception)
    

    所以,如果你成功了,会看到这样的图像:

    我通过向自己发送电子邮件进行了测试。

    注意:我的帐户启用了两步验证。应用密码适用于此! (gmail smtp设置,你必须去https://support.google.com/accounts/answer/185833?hl=en并按照以下步骤)

    此设置不适用于启用两步验证的帐户。此类帐户需要特定于应用程序的密码才能访问不太安全的应用程序。

    【讨论】:

    • 很棒的解决方案,并且在代码中得到了很好的解释。谢谢杰,非常感谢。愚蠢的问题:您知道每天最多可以发送多少封电子邮件(使用 gmail)吗?
    • 谢谢@Angelo,但是有一个限制,GMail = 500 封电子邮件或 500 个收件人/天 ref:support.google.com/mail/answer/22839 G SUITE 不同,每天 2000 封邮件,可以在这里找到:@ 987654327@祝你好运!
    • 所有其他帖子都是较旧的帖子,可能无法正常工作,但这是 100% 的工作。生成应用程序密码。感谢您的回答
    • 我有点惊讶这个解决方案没有更多的支持。我还没有尝试所有其他的,但我已经尝试了几个,只有这个开箱即用,0 修补。
    • @abhyudayasrinet 嗯...有趣...我会调查一下。这可能有助于检查数据损坏和其他一些潜在的事情,例如自动化和/验证。
    【解决方案5】:

    在您的 gmail 帐户上启用 less secure apps 并使用 (Python>=3.6):

    import smtplib
    from email.mime.multipart import MIMEMultipart
    from email.mime.text import MIMEText
    
    gmailUser = 'XXXXX@gmail.com'
    gmailPassword = 'XXXXX'
    recipient = 'XXXXX@gmail.com'
    
    message = f"""
    Type your message here...
    """
    
    msg = MIMEMultipart()
    msg['From'] = f'"Your Name" <{gmailUser}>'
    msg['To'] = recipient
    msg['Subject'] = "Subject here..."
    msg.attach(MIMEText(message))
    
    try:
        mailServer = smtplib.SMTP('smtp.gmail.com', 587)
        mailServer.ehlo()
        mailServer.starttls()
        mailServer.ehlo()
        mailServer.login(gmailUser, gmailPassword)
        mailServer.sendmail(gmailUser, recipient, msg.as_string())
        mailServer.close()
        print ('Email sent!')
    except:
        print ('Something went wrong...')
    

    【讨论】:

    • 非常棒的答案。最好的一个,超级简洁。谢谢。
    • 谢谢佩德罗,你的回答解决了它。顺便说一句,对于使用具有多个别名的 Gsuite 的任何人;只需在 support.google.com/mail/answer/22370?hl=en 之后将别名添加到您的 gmail 帐户,您就可以通过将 &lt;{gmailUser}&gt; 替换为 &lt;YourAlias&gt; 来使用别名发送。
    【解决方案6】:

    这是一个 Gmail API 示例。虽然更复杂,但这是我在 2019 年发现的唯一可行的方法。此示例取自并修改自:

    https://developers.google.com/gmail/api/guides/sending

    您需要通过他们的网站使用 Google 的 API 接口创建一个项目。接下来,您需要为您的应用启用 GMAIL API。创建凭据,然后下载这些凭据,将其保存为 credentials.json。

    import pickle
    import os.path
    from googleapiclient.discovery import build
    from google_auth_oauthlib.flow import InstalledAppFlow
    from google.auth.transport.requests import Request
    
    from email.mime.text import MIMEText
    import base64
    
    #pip install --upgrade google-api-python-client google-auth-httplib2 google-auth-oauthlib
    
    # If modifying these scopes, delete the file token.pickle.
    SCOPES = ['https://www.googleapis.com/auth/gmail.readonly', 'https://www.googleapis.com/auth/gmail.send']
    
    def create_message(sender, to, subject, msg):
        message = MIMEText(msg)
        message['to'] = to
        message['from'] = sender
        message['subject'] = subject
    
        # Base 64 encode
        b64_bytes = base64.urlsafe_b64encode(message.as_bytes())
        b64_string = b64_bytes.decode()
        return {'raw': b64_string}
        #return {'raw': base64.urlsafe_b64encode(message.as_string())}
    
    def send_message(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, error:print( 'An error occurred: %s' % error )
    
    def main():
        """Shows basic usage of the Gmail API.
        Lists the user's Gmail labels.
        """
        creds = None
        # The file token.pickle stores the user's access and refresh tokens, and is
        # created automatically when the authorization flow completes for the first
        # time.
        if os.path.exists('token.pickle'):
            with open('token.pickle', 'rb') as token:
                creds = pickle.load(token)
        # If there are no (valid) credentials available, let the user log in.
        if not creds or not creds.valid:
            if creds and creds.expired and creds.refresh_token:
                creds.refresh(Request())
            else:
                flow = InstalledAppFlow.from_client_secrets_file(
                    'credentials.json', SCOPES)
                creds = flow.run_local_server(port=0)
            # Save the credentials for the next run
            with open('token.pickle', 'wb') as token:
                pickle.dump(creds, token)
    
        service = build('gmail', 'v1', credentials=creds)
    
        # Example read operation
        results = service.users().labels().list(userId='me').execute()
        labels = results.get('labels', [])
    
        if not labels:
            print('No labels found.')
        else:
            print('Labels:')
        for label in labels:
            print(label['name'])
    
        # Example write
        msg = create_message("from@gmail.com", "to@gmail.com", "Subject", "Msg")
        send_message( service, 'me', msg)
    
    if __name__ == '__main__':
        main()
    

    【讨论】:

    • smtplib 不是完全线程安全的,因此在发送并发消息时会出现问题。这是正确的方法。
    • 知道为什么我会得到:googleapiclient.errors.HttpError: &lt;HttpError 403 when requesting [https://gmail.googleapis.com/gmail/v1/users/me/messages/send?alt=json][1] returned "Request had insufficient authentication scopes."&gt;?下载凭据文件并启用 Gmail API。
    • 听起来您在 googleapi 控制台中存在配置错误。我不知道如何具体解决这个问题。对不起。
    • 我遇到了同样的错误Request had insufficient authentication scopes。这显然是因为您已经从本指南(或任何其他)developers.google.com/gmail/api/quickstart/python 生成了 token.pickle 解决方案:1. 您只需使用新权限/范围重新创建 token.pickle 并再次运行脚本。它将自动重新创建具有新权限的token.pickle
    【解决方案7】:
    def send_email(user, pwd, recipient, subject, body):
        import smtplib
    
        FROM = user
        TO = recipient if isinstance(recipient, list) else [recipient]
        SUBJECT = subject
        TEXT = body
    
        # Prepare actual message
        message = """From: %s\nTo: %s\nSubject: %s\n\n%s
        """ % (FROM, ", ".join(TO), SUBJECT, TEXT)
        try:
            server = smtplib.SMTP("smtp.gmail.com", 587)
            server.ehlo()
            server.starttls()
            server.login(user, pwd)
            server.sendmail(FROM, TO, message)
            server.close()
            print 'successfully sent the mail'
        except:
            print "failed to send mail"
    

    如果您想使用端口 465,您必须创建一个 SMTP_SSL 对象:

    # SMTP_SSL Example
    server_ssl = smtplib.SMTP_SSL("smtp.gmail.com", 465)
    server_ssl.ehlo() # optional, called by login()
    server_ssl.login(gmail_user, gmail_pwd)  
    # ssl server doesn't support or need tls, so don't call server_ssl.starttls() 
    server_ssl.sendmail(FROM, TO, message)
    #server_ssl.quit()
    server_ssl.close()
    print 'successfully sent the mail'
    

    【讨论】:

    • 非常好的样品谢谢。我注意到的一个想法是,如果我想使用 SSL 连接,我必须删除 server.starttls()
    • 不幸的是不再起作用:smtplib.SMTPAuthenticationError: (534, '5.7.14 accounts.google.com/… ... 请通过您的网络浏览器登录,\n5.7.14 然后重试。\ n5.7.14 了解详情\n5.7.14 support.google.com/mail/bin/answer.py?answer=78754 ... 然后我收到了来自 google 的邮件,说存在可疑的连接尝试。
    • @royskatt - 您需要做的就是创建一个应用程序密码并使用它来代替您的帐户密码。在此处创建应用密码:security.google.com/settings/security/apppasswords
    • @royskatt :我刚刚解决了您面临的问题。谷歌有一项设置允许访问不太安全的应用程序,您只需将其打开即可。您可以通过以下方式到达:Google-->我的帐户-->登录和安全-->连接的应用程序和站点-->向下滚动,您会发现“允许安全性较低的应用程序”
    • 如果您的 gmail 受双重身份验证保护,您必须首先 generate an application specific password --> 然后在上面的示例代码中使用该应用程序密码(这非常重要,因为这样您就不是t 在任何地方以明文形式写下您的密码,并且您可以随时撤销应用密码)。
    【解决方案8】:

    你可以在这里找到它:http://jayrambhia.com/blog/send-emails-using-python

    smtp_host = 'smtp.gmail.com'
    smtp_port = 587
    server = smtplib.SMTP()
    server.connect(smtp_host,smtp_port)
    server.ehlo()
    server.starttls()
    server.login(user,passw)
    fromaddr = raw_input('Send mail by the name of: ')
    tolist = raw_input('To: ').split()
    sub = raw_input('Subject: ')
    
    msg = email.MIMEMultipart.MIMEMultipart()
    msg['From'] = fromaddr
    msg['To'] = email.Utils.COMMASPACE.join(tolist)
    msg['Subject'] = sub  
    msg.attach(MIMEText(raw_input('Body: ')))
    msg.attach(MIMEText('\nsent via python', 'plain'))
    server.sendmail(user,tolist,msg.as_string())
    

    【讨论】:

    • 加 1,因为构建 MIME 比硬编码自己的格式字符串更好。简单的短信是否需要 MIMEMultipart?或者以下是否也正确:stackoverflow.com/a/6270987/895245
    • 在哪里实例化 email 变量?
    【解决方案9】:

    @David 给出了很好的答案,这是针对没有通用 try-except 的 Python 3:

    def send_email(user, password, recipient, subject, body):
    
        gmail_user = user
        gmail_pwd = password
        FROM = user
        TO = recipient if type(recipient) is list else [recipient]
        SUBJECT = subject
        TEXT = body
    
        # Prepare actual message
        message = """From: %s\nTo: %s\nSubject: %s\n\n%s
        """ % (FROM, ", ".join(TO), SUBJECT, TEXT)
    
        server = smtplib.SMTP("smtp.gmail.com", 587)
        server.ehlo()
        server.starttls()
        server.login(gmail_user, gmail_pwd)
        server.sendmail(FROM, TO, message)
        server.close()
    

    【讨论】:

      【解决方案10】:
          import smtplib
      
          fromadd='from@gmail.com'
          toadd='send@gmail.com'
      
          msg='''hi,how r u'''
          username='abc@gmail.com'
          passwd='password'
      
          try:
              server = smtplib.SMTP('smtp.gmail.com:587')
              server.ehlo()
              server.starttls()
              server.login(username,passwd)
      
              server.sendmail(fromadd,toadd,msg)
              print("Mail Send Successfully")
              server.quit()
      
         except:
              print("Error:unable to send mail")
      
         NOTE:https://www.google.com/settings/security/lesssecureapps that                                                         should be enabled
      

      【讨论】:

      • 我正在发布简单的代码,用于从 Gmail 帐户发送邮件。如果您需要任何信息,请告诉我。我希望这些代码对所有用户都有帮助。
      【解决方案11】:

      我遇到了类似的问题,偶然发现了这个问题。我收到 SMTP 身份验证错误,但我的用户名/密码正确。这是修复它的方法。我读到了:

      https://support.google.com/accounts/answer/6010255

      简而言之,谷歌不允许你通过 smtplib 登录,因为它已将这种登录标记为“不太安全”,所以你要做的就是在你登录到你的google 帐户,并允许访问:

      https://www.google.com/settings/security/lesssecureapps

      一旦设置好(见我下面的截图),它应该可以工作了。

      现在可以登录了:

      smtpserver = smtplib.SMTP("smtp.gmail.com", 587)
      smtpserver.ehlo()
      smtpserver.starttls()
      smtpserver.ehlo()
      smtpserver.login('me@gmail.com', 'me_pass')
      

      更改后的响应:

      (235, '2.7.0 Accepted')
      

      事先回复:

      smtplib.SMTPAuthenticationError: (535, '5.7.8 Username and Password not accepted. Learn more at\n5.7.8 http://support.google.com/mail/bin/answer.py?answer=14257 g66sm2224117qgf.37 - gsmtp')
      

      还是不行?如果你仍然收到 SMTPAuthenticationError 但现在代码是 534,那是因为位置未知。请点击此链接:

      https://accounts.google.com/DisplayUnlockCaptcha

      点击继续,这应该会给您 10 分钟的时间来注册您的新应用。所以现在继续进行另一次登录尝试,它应该可以工作。

      更新:这似乎无法立即工作,您可能会在 smptlib 中遇到此错误一段时间:

      235 == 'Authentication successful'
      503 == 'Error: already authenticated'
      

      消息说使用浏览器登录:

      SMTPAuthenticationError: (534, '5.7.9 Please log in with your web browser and then try again. Learn more at\n5.7.9 https://support.google.com/mail/bin/answer.py?answer=78754 qo11sm4014232igb.17 - gsmtp')
      

      启用“lesssecureapps”后,去喝杯咖啡,回来,然后再次尝试“DisplayUnlockCaptcha”链接。根据用户体验,更改可能需要一个小时才能生效。然后再次尝试登录过程。

      【讨论】:

      • 感谢我唯一的问题:accounts.google.com/DisplayUnlockCaptcha
      • 另外,请留出半小时到一小时来更改设置。我创建了一个新帐户,禁用了所有添加的安全性,但仍然出现相同的错误。大约一个小时后,一切都奏效了。
      • 已更新,谢谢。我知道这可能需要一些时间,所以我写了“喝杯咖啡”,但感谢球场图。添加:)
      • 如果您启用了“两步验证”,则无法启用不太安全的应用程序。最好和最安全的选择是启用“apppassword”security.google.com/settings/security/apppasswords,就像已经建议的那样,它就像一个魅力
      • 当我点击 apppasswords 链接时,我的所有 Google 帐户都会收到“您要查找的设置不适用于您的帐户”错误。
      【解决方案12】:
      import smtplib
      server = smtplib.SMTP('smtp.gmail.com', 587)
      server.ehlo()
      server.starttls()
      server.login("fromaddress", "password")
      msg = "HI!"
      server.sendmail("fromaddress", "receiveraddress", msg)
      server.quit()
      

      【讨论】:

      • 使用python代码通过gmail发送邮件的简单代码。 from address 是您的 gmailID,receiveraddress 是您发送邮件的邮件 ID。
      • 这并不能解决 OP 的问题。
      【解决方案13】:

      没有直接关系但仍然值得指出的是,我的包试图使发送 gmail 消息变得非常快速和轻松。它还尝试维护错误列表并尝试立即指出解决方案。

      实际上只需要这段代码就可以完成您所写的操作:

      import yagmail
      yag = yagmail.SMTP('user_me@gmail.com')
      yag.send('user_you@gmail.com', 'Why,Oh why!')
      

      或者一个班轮:

      yagmail.SMTP('user_me@gmail.com').send('user_you@gmail.com', 'Why,Oh why!')
      

      对于包/安装,请查看gitpip,可用于 Python 2 和 3。

      【讨论】:

        【解决方案14】:

        现在有一个 gmail API,可让您通过 REST 发送电子邮件、阅读电子邮件和创建草稿。 与 SMTP 调用不同,它是非阻塞的,这对于在请求线程中发送电子邮件的基于线程的网络服务器(如 python 网络服务器)来说是一件好事。 API 也很强大。

        • 当然,电子邮件应该交给非网络服务器队列,但有选项也不错。

        如果您在域中拥有 Google Apps 管理员权限,则设置最简单,因为这样您就可以向您的客户授予一揽子权限。否则,您必须摆弄 OAuth 身份验证和权限。

        这是一个证明它的要点:

        https://gist.github.com/timrichardson/1154e29174926e462b7a

        【讨论】:

          【解决方案15】:

          你对 OOP 失望了?

          #!/usr/bin/env python
          
          
          import smtplib
          
          class Gmail(object):
              def __init__(self, email, password):
                  self.email = email
                  self.password = password
                  self.server = 'smtp.gmail.com'
                  self.port = 587
                  session = smtplib.SMTP(self.server, self.port)        
                  session.ehlo()
                  session.starttls()
                  session.ehlo
                  session.login(self.email, self.password)
                  self.session = session
          
              def send_message(self, subject, body):
                  ''' This must be removed '''
                  headers = [
                      "From: " + self.email,
                      "Subject: " + subject,
                      "To: " + self.email,
                      "MIME-Version: 1.0",
                     "Content-Type: text/html"]
                  headers = "\r\n".join(headers)
                  self.session.sendmail(
                      self.email,
                      self.email,
                      headers + "\r\n\r\n" + body)
          
          
          gm = Gmail('Your Email', 'Password')
          
          gm.send_message('Subject', 'Message')
          

          【讨论】:

          • 如果你的类只有两个方法,其中一个是__init__,就用一个函数。
          • 如何使用这种方法添加附件?
          • 如果您想初始化客户端并将其传递给代码的其他部分,而不是传递电子邮件和密码,那么使用类会很好。或者如果您想发送多条消息而不每次都传递电子邮件和密码。
          【解决方案16】:

          似乎是旧smtplib 的问题。在python2.7 一切正常。

          更新:是的,server.ehlo() 也可以提供帮助。

          【讨论】:

            猜你喜欢
            • 2020-07-12
            • 1970-01-01
            • 2016-03-22
            • 2020-07-13
            • 1970-01-01
            • 1970-01-01
            • 2018-08-06
            • 1970-01-01
            相关资源
            最近更新 更多