¿Cómo enviar un correo electrónico con Gmail como proveedor usando Python?

289

Estoy tratando de enviar un correo electrónico (Gmail) usando Python, pero recibo el siguiente error.

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.

El script de Python es el siguiente.

import smtplib
fromaddr = '[email protected]'
toaddrs  = '[email protected]'
msg = 'Why,Oh why!'
username = '[email protected]'
password = 'pwd'
server = smtplib.SMTP('smtp.gmail.com:587')
server.starttls()
server.login(username,password)
server.sendmail(fromaddr, toaddrs, msg)
server.quit()
Mahori
fuente
16
Esta pregunta debería desbloquearse ahora que Google se está moviendo hacia una API interna para enviar mensajes a través de gmail. Este movimiento hace que todas las respuestas menos una sean obsoletas, y la única respuesta relevante es muy ligera para resolver el problema.
Andrew
Además, para los usuarios de VPN, si el problema persiste, apague su VPN. Eso funcionó para mí.
Paul

Respuestas:

211

Tienes que decir EHLOantes de simplemente correr directamente a STARTTLS:

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

También debe crear realmente From:, To:y Subject:encabezados de los mensajes, separada del cuerpo del mensaje por una línea en blanco y el uso CRLFcomo marca EOL.

P.ej

msg = "\r\n".join([
  "From: [email protected]",
  "To: [email protected]",
  "Subject: Just a message",
  "",
  "Why, oh why"
  ])
MattH
fuente
2
invocando server.sendmail (fromaddr, toaddrs, msg) el segundo parámetro, toaddrs debe ser una lista, toaddrs = ['[email protected]']
Massimo Fazzolari
14
A partir de agosto de 2014, esto ahora aumenta smtplib.SMTPAuthenticationError: (534, '
5.7.9 Se
77
Sin embargo, para mí, tuve que habilitar una contraseña de 'aplicación' para iniciar sesión usando una cuenta de @google para enviar correos electrónicos a través de python: support.google.com/accounts/answer/…
anon58192932
1
Aquí hay un enlace sobre cómo enviar correos a varias personas: stackoverflow.com/questions/8856117/…
anon58192932
1
Una vez inicié sesión en un servidor SMTP por telnet y lo envié EHLOpor error tipográfico. Después probé HELO muchas veces, pero la respuesta fue diferente. Me llevó horas descubrir que EHLO es en realidad un comando que SMTP entiende y que hice el error tipográfico.
Shiplu Mokaddim
297
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"

si desea usar el puerto 465, debe crear un SMTP_SSLobjeto:

# 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'
David Okwii
fuente
2
Muy buena muestra gracias. Una cosa que noté es que si quiero usar una conexión SSL tuve que eliminar server.starttls ()
Dowlers
18
Desafortunadamente, ya no funciona: smtplib.SMTPAuthenticationError: (534, '5.7.14 < accounts.google.com/… ... Inicie sesión a través de su navegador web y \ n5.7.14 e intente nuevamente. \ N5.7.14 Más información más en \ n5.7.14 support.google.com/mail/bin/answer.py?answer=78754 ... Luego recibí un correo de google, que ha habido un intento de conexión sospechoso.
royskatt
13
@royskatt: todo lo que necesita hacer es crear una contraseña de aplicación y usarla en lugar de la contraseña de su cuenta. Cree una contraseña de aplicación aquí: security.google.com/settings/security/apppasswords
Jared
15
@royskatt: Acabo de recibir una solución para el problema al que te enfrentas. Google tiene una configuración para permitir el acceso a aplicaciones menos seguras, solo tiene que activarla. puede llegar desde: Google -> mi cuenta -> Inicio de sesión y seguridad -> Aplicaciones y sitios conectados -> desplácese hacia abajo y encontrará 'Permitir aplicaciones menos seguras'
shaleen mohan
2
Si su gmail está protegido por la autenticación de dos factores, primero debe generar una contraseña específica de la aplicación -> luego usar esa contraseña de aplicación para el código de ejemplo anterior (esto es muy importante porque no está escribiendo su contraseña en ningún lado en texto sin cifrar Y puede revocar la contraseña de la aplicación en cualquier momento).
Trevor Boyd Smith
136

Me encontré con un problema similar y me topé con esta pregunta. Recibí un error de autenticación SMTP pero mi nombre de usuario / contraseña era correcta. Esto es lo que lo arregló. Leo esto:

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

En pocas palabras, google no le permite iniciar sesión a través de smtplib porque ha marcado este tipo de inicio de sesión como "menos seguro", por lo que lo que debe hacer es ir a este enlace mientras está conectado a su cuenta de google, y permitir el acceso:

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

Una vez que esté configurado (vea mi captura de pantalla a continuación), debería funcionar.

ingrese la descripción de la imagen aquí

Iniciar sesión ahora funciona:

smtpserver = smtplib.SMTP("smtp.gmail.com", 587)
smtpserver.ehlo()
smtpserver.starttls()
smtpserver.ehlo()
smtpserver.login('[email protected]', 'me_pass')

Respuesta después del cambio:

(235, '2.7.0 Accepted')

Respuesta previa:

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

¿Sigue sin funcionar? Si aún obtiene el SMTPAuthenticationError pero ahora el código es 534, es porque la ubicación es desconocida. Siga este enlace:

https://accounts.google.com/DisplayUnlockCaptcha

Haga clic en continuar y esto le dará 10 minutos para registrar su nueva aplicación. Así que proceda a hacer otro intento de inicio de sesión ahora y debería funcionar.

ACTUALIZACIÓN : Esto no parece funcionar de inmediato, puede estar atascado por un tiempo al obtener este error en smptlib:

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

El mensaje dice que use el navegador para iniciar sesión:

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

Después de habilitar 'lesssecureapps', vaya a tomar un café, regrese e intente nuevamente el enlace 'DisplayUnlockCaptcha'. Según la experiencia del usuario, el cambio puede tardar hasta una hora en iniciarse. Luego, intente el proceso de inicio de sesión nuevamente.

radtek
fuente
3
SI, GRACIAS ! Sucedió mientras usaba el correo electrónico
Giannis
1
gracias hombre solo problema para mí: accounts.google.com/DisplayUnlockCaptcha
Sin límite isa
66
Además, deje de media hora a una hora para que cambie la configuración. Creé una nueva cuenta, deshabilité toda la seguridad añadida y aún recibí el mismo error. Aproximadamente una hora después, todo funcionó.
jkgeyti
2
No es posible habilitar aplicaciones menos seguras si tiene habilitada la "Verificación en 2 pasos". La opción mejor y más segura es habilitar la "contraseña de la aplicación" security.google.com/settings/security/apppasswords como ya se sugirió, y funciona de
maravilla
2
Cuando sigo el enlace de contraseñas de aplicación, todas mis cuentas de Google reciben el error "La configuración que está buscando no está disponible para su cuenta".
Suzanne
18

¿Estás abajo con 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')
Ricky Wilson
fuente
33
Si su clase tiene solo dos métodos, uno de los cuales es __init__, simplemente use una función.
JoeQuery
¿Cómo agregaría un archivo adjunto con este método?
sgerbhctim
Usar una clase sería bueno si quisieras iniciar el cliente y pasarlo a otras partes del código, en lugar de pasar un correo electrónico y una contraseña. O si desea enviar varios mensajes sin pasar el correo electrónico y la contraseña cada vez.
Sami Comienza el
15

Esto funciona

¡Crea la contraseña de la aplicación de Gmail!

Después de crear eso, cree un archivo llamado sendgmail.py

Luego agregue este código :

#!/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 = '[email protected]'
gmail_app_password = 'YOUR-GOOGLE-APPLICATION-PASSWORD!!!!'

# =============================================================================
# SET THE INFO ABOUT THE SAID EMAIL
# =============================================================================
sent_from = gmail_user
sent_to = ['[email protected]', '[email protected]']
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)

Entonces, si tiene éxito, verá una imagen como esta:

Lo probé enviando un correo electrónico desde y hacia mí.

Correcto correo electrónico enviado.

Nota: Tengo habilitada la verificación en dos pasos en mi cuenta. ¡La contraseña de la aplicación funciona con esto! (para la configuración de gmail smtp, debe ir a https://support.google.com/accounts/answer/185833?hl=es y seguir los pasos a continuación)

Esta configuración no está disponible para cuentas con la verificación en dos pasos habilitada. Dichas cuentas requieren una contraseña específica de la aplicación para un acceso a aplicaciones menos seguro.

Acceso a aplicaciones menos seguro ... Esta configuración no está disponible para cuentas con la verificación en dos pasos habilitada.

JayRizzo
fuente
1
Fantástica solución y muy bien explicada en el código. Gracias Jay, muy apreciado. Pregunta tonta: ¿sabrías cuál es el límite máximo de correos electrónicos por día que se pueden enviar (con gmail)?
Angelo
Gracias @Angelo, pero sí, hay un límite, GMail = 500 correos electrónicos o 500 destinatarios / día ref: support.google.com/mail/answer/22839 G SUITE es diferente y tiene 2000 mensajes / día y se puede encontrar aquí: soporte .google.com / a / answer / 166852 ¡ Buena suerte!
JayRizzo
2
Todos los demás son publicaciones antiguas y pueden no funcionar, pero esto funciona al 100%. Genere contraseñas de aplicaciones. Gracias por la respuesta
Shubh
1
Estoy un poco sorprendido de que esta solución no tenga más votos a favor. No he probado todos los demás, pero he probado varios, y solo este funcionó de fábrica, con 0 ajustes.
mcouthon
14

Puede encontrarlo aquí: 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())
Froyo
fuente
2
más 1 porque es mejor construir un MIME que codificar su propia cadena de formato. ¿Se requiere MIMEMultipart para un mensaje de texto simple? ¿O también es correcto lo siguiente: stackoverflow.com/a/6270987/895245
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
¿Dónde instancia la variable de correo electrónico?
madcat
11

No está directamente relacionado, pero aún vale la pena señalar que mi paquete intenta hacer que el envío de mensajes de Gmail sea realmente rápido e indoloro. También intenta mantener una lista de errores e intenta señalar la solución de inmediato.

Literalmente, solo necesitaría este código para hacer exactamente lo que escribió:

import yagmail
yag = yagmail.SMTP('[email protected]')
yag.send('[email protected]', 'Why,Oh why!')

O un trazador de líneas:

yagmail.SMTP('[email protected]').send('[email protected]', 'Why,Oh why!')

Para el paquete / instalación, consulte git o pip , disponible para Python 2 y 3.

PascalVKooten
fuente
9

Aquí hay un ejemplo de API de Gmail. Aunque es más complicado, este es el único método que encontré que funciona en 2019. Este ejemplo fue tomado y modificado de:

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

Deberá crear un proyecto con las interfaces API de Google a través de su sitio web. A continuación, deberá habilitar la API de GMAIL para su aplicación. Cree credenciales y luego descargue esos créditos, guárdelos como 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("[email protected]", "[email protected]", "Subject", "Msg")
    send_message( service, 'me', msg)

if __name__ == '__main__':
    main()
Luke Dupin
fuente
smtplib no es completamente seguro para subprocesos, por lo que tendrá problemas para enviar mensajes concurrentes. Este es el enfoque correcto.
dsbajna
2

Ahora hay una API de gmail, que le permite enviar correos electrónicos, leer correos electrónicos y crear borradores a través de REST. A diferencia de las llamadas SMTP, no es de bloqueo, lo que puede ser algo bueno para los servidores web basados ​​en hilos que envían correo electrónico en el hilo de solicitud (como los servidores web python). La API también es bastante poderosa.

  • Por supuesto, el correo electrónico debe enviarse a una cola que no sea de servidor web, pero es bueno tener opciones.

Es más fácil de configurar si tiene derechos de administrador de Google Apps en el dominio, porque entonces puede otorgar un permiso general a su cliente. De lo contrario, debe jugar con la autenticación y el permiso de OAuth.

Aquí hay una esencia que lo demuestra:

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

Tim Richardson
fuente
2

gran respuesta de @David, aquí está para Python 3 sin el genérico try-except:

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()
juan Isaza
fuente
2

Habilite aplicaciones menos seguras en su cuenta de gmail y use (Python> = 3.6):

import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText

gmailUser = '[email protected]'
gmailPassword = 'XXXXX'
recipient = '[email protected]'

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...')
CONvid19
fuente
1
Realmente fantástica respuesta. El mejor del grupo, súper conciso. Gracias.
Bruce Dean el
Gracias Pedro, tu respuesta lo resolvió. Por cierto para cualquiera que use Gsuite con múltiples alias; simplemente agregue el alias a su cuenta de gmail siguiendo support.google.com/mail/answer/22370?hl=es y puede enviar usando el alias reemplazándolo <{gmailUser}>con <YourAlias>.
LucSpan
1

Parece un problema de lo viejo smtplib. En python2.7todo funciona bien.

Actualización : Sí, server.ehlo()también podría ayudar.

mega.venik
fuente
-1
    import smtplib

    fromadd='[email protected]'
    toadd='[email protected]'

    msg='''hi,how r u'''
    username='[email protected]'
    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
Shyam Gupta
fuente
Estoy publicando el código simple que hará cómo enviar correo desde la cuenta de Gmail. Si necesita información, hágamelo saber. Espero que el código ayude a todos los usuarios.
Shyam Gupta
-2
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()
Sam Divya Kumar
fuente
código simple para enviar un correo a través de gmail usando código python. desde la dirección es su gmailID y la dirección de recepción es la identificación de correo que envía.
Sam Divya Kumar