Python ile e-posta nasıl gönderilir?


170

Bu kod işe yarar ve bana bir e-posta gönderir:

import smtplib
#SERVER = "localhost"

FROM = 'monty@python.com'

TO = ["jon@mycompany.com"] # must be a list

SUBJECT = "Hello!"

TEXT = "This message was sent with Python's smtplib."

# Prepare actual message

message = """\
From: %s
To: %s
Subject: %s

%s
""" % (FROM, ", ".join(TO), SUBJECT, TEXT)

# Send the mail

server = smtplib.SMTP('myserver')
server.sendmail(FROM, TO, message)
server.quit()

Ancak ben böyle bir işlev sarmaya çalışırsanız:

def sendMail(FROM,TO,SUBJECT,TEXT,SERVER):
    import smtplib
    """this is some test documentation in the function"""
    message = """\
        From: %s
        To: %s
        Subject: %s
        %s
        """ % (FROM, ", ".join(TO), SUBJECT, TEXT)
    # Send the mail
    server = smtplib.SMTP(SERVER)
    server.sendmail(FROM, TO, message)
    server.quit()

ve diyorum ki aşağıdaki hataları alıyorum:

 Traceback (most recent call last):
  File "C:/Python31/mailtest1.py", line 8, in <module>
    sendmail.sendMail(sender,recipients,subject,body,server)
  File "C:/Python31\sendmail.py", line 13, in sendMail
    server.sendmail(FROM, TO, message)
  File "C:\Python31\lib\smtplib.py", line 720, in sendmail
    self.rset()
  File "C:\Python31\lib\smtplib.py", line 444, in rset
    return self.docmd("rset")
  File "C:\Python31\lib\smtplib.py", line 368, in docmd
    return self.getreply()
  File "C:\Python31\lib\smtplib.py", line 345, in getreply
    raise SMTPServerDisconnected("Connection unexpectedly closed")
smtplib.SMTPServerDisconnected: Connection unexpectedly closed

Biri nedenini anlamama yardımcı olabilir mi?


2
işlevi nasıl çağırırsın?
Jochen Ritzel

Gönderdiğiniz girinti dosyanızdakiyle aynı mı?
gddc

@gddc hayır Düzgün girintiden emin oldum, bu sadece yapıştırdım.
cloud311

Fonksiyonu ana modülüme alarak ve tanımladığım parametreleri geçirerek çağırıyorum.
cloud311

4
@ Arrieta'nın e-posta paketini kullanma önerisi, bunu çözmenin en iyi yolu olsa da, yaklaşımınız işe yarayabilir. İki sürümünüz arasındaki farklar dizede: (1) @NickODell'in işaret ettiği gibi, işlev sürümünde önde gelen boşluk var. Üstbilgilerin ön boşlukları olmamalıdır (kaydırılmadığı sürece). (2) TEXT önde gelen bir boş satır içermiyorsa, üstbilgiler ve gövde arasındaki ayırıcıyı kaybettiniz.
Tony Meyer

Yanıtlar:


192

E-posta göndermek için standart paketleri emailve smtplibbirlikte kullanmanızı öneririz . Lütfen aşağıdaki örneğe bakın ( Python belgelerinden çoğaltılmıştır ). Bu yaklaşımı izlerseniz, "basit" görev gerçekten basittir ve daha karmaşık görevlerin (ikili nesneler eklemek veya düz / HTML çok parçalı mesajlar göndermek gibi) çok hızlı bir şekilde gerçekleştirildiğine dikkat edin.

# Import smtplib for the actual sending function
import smtplib

# Import the email modules we'll need
from email.mime.text import MIMEText

# Open a plain text file for reading.  For this example, assume that
# the text file contains only ASCII characters.
with open(textfile, 'rb') as fp:
    # Create a text/plain message
    msg = MIMEText(fp.read())

# me == the sender's email address
# you == the recipient's email address
msg['Subject'] = 'The contents of %s' % textfile
msg['From'] = me
msg['To'] = you

# Send the message via our own SMTP server, but don't include the
# envelope header.
s = smtplib.SMTP('localhost')
s.sendmail(me, [you], msg.as_string())
s.quit()

Birden çok hedefe e-posta göndermek için Python belgelerindeki örneği de takip edebilirsiniz :

# Import smtplib for the actual sending function
import smtplib

# Here are the email package modules we'll need
from email.mime.image import MIMEImage
from email.mime.multipart import MIMEMultipart

# Create the container (outer) email message.
msg = MIMEMultipart()
msg['Subject'] = 'Our family reunion'
# me == the sender's email address
# family = the list of all recipients' email addresses
msg['From'] = me
msg['To'] = ', '.join(family)
msg.preamble = 'Our family reunion'

# Assume we know that the image files are all in PNG format
for file in pngfiles:
    # Open the files in binary mode.  Let the MIMEImage class automatically
    # guess the specific image type.
    with open(file, 'rb') as fp:
        img = MIMEImage(fp.read())
    msg.attach(img)

# Send the email via our own SMTP server.
s = smtplib.SMTP('localhost')
s.sendmail(me, family, msg.as_string())
s.quit()

Gördüğünüz gibi To, MIMETextnesnedeki başlık virgülle ayrılmış e-posta adreslerinden oluşan bir dize olmalıdır. Öte yandan, sendmailişlevin ikinci bağımsız değişkeni bir dize listesi olmalıdır (her dize bir e-posta adresidir).

Eğer üç e-posta adresleri varsa, yani: person1@example.com, person2@example.com, ve person3@example.com, yapabileceğin olarak (bariz kesitler atlanmıştır) aşağıdaki gibidir:

to = ["person1@example.com", "person2@example.com", "person3@example.com"]
msg['To'] = ",".join(to)
s.sendmail(me, to, msg.as_string())

",".join(to)bölüm virgülle ayrılmış liste, dışarı tek bir sicim yapar.

Sorularınızı Python öğretici geçti değil toplamak - Python herhangi bir yere almak istiyorsanız bir zorunluluktur - belgeler çoğunlukla standart kütüphane için mükemmel.


1
Teşekkür ederim, bu bir işlev içinde çok güzel çalışıyor, ancak birden çok alıcıya nasıl gönderebilirim? Msg [to] bir sözlük anahtarı gibi göründüğünden, msg [ile] 'yi noktalı virgülle ayırmayı denedim ama bu işe yaramıyor.
cloud311

1
@ cloud311 kodunuzda olduğu gibi. Virgülle ayrılmış bir dize istiyor: ", ".join(["a@example.com", "b@example.net"])
Tim McNamara

3
Ayrıca Kime: başlığının zarf alıcısından farklı semantiğe sahip olduğunu unutmayın. Örneğin, Kime: başlığında adres olarak '"Tony Meyer" <tony.meyer@gmail.com>' adresini kullanabilirsiniz, ancak zarf alıcısının yalnızca "tony.meyer@gmail.com" olması gerekir. 'Güzel' Alıcı: adresi oluşturmak için email.utils.formataddr ("Tony Meyer", "tony.meyer@gmail.com") gibi email.utils.formataddr kullanın.
Tony Meyer

2
Küçük iyileştirme: dosya with: kullanılarak açılmalıdır with open(textfile, 'rb') as fp:. Açık withdosya kapatılabilir , çünkü blok dosyada bir hata olsa bile dosyayı kapatacaktır.
jpmc26

1
Bu yanıta özgü değil, ancak kontrol etmeyen herhangi bir SMTP sunucusuna bağlanırken, erişilemez, yavaş, bağlantıları reddetme veya başka herhangi bir şey olma olasılığını göz önünde bulundurmalısınız. Kod açısından bir istisna alacaksınız, ancak daha sonra göndermeyi biraz daha denemek için bir yol bulmanız gerekiyor. Kendi sendmail / postfix'inizle konuşursanız, sizin için yeniden göndermeyle ilgilenir.
Ralph Bolton

67

Güncel ve modern bir cevap almak istersiniz.

İşte cevabım:

Python postalamak gerektiğinde, maillun API wich olsun hangi postaları sıralanmış gönderme ile baş ağrısı bir sürü kullanın . Onlar ayda ücretsiz 10.000 e-posta göndermek için izin veren bir acayip uygulaması / api var.

Bir e-posta göndermek şu şekilde olur:

def send_simple_message():
    return requests.post(
        "https://api.mailgun.net/v3/YOUR_DOMAIN_NAME/messages",
        auth=("api", "YOUR_API_KEY"),
        data={"from": "Excited User <mailgun@YOUR_DOMAIN_NAME>",
              "to": ["bar@example.com", "YOU@YOUR_DOMAIN_NAME"],
              "subject": "Hello",
              "text": "Testing some Mailgun awesomness!"})

Ayrıca etkinlikleri ve çok daha fazlasını izleyebilirsiniz , hızlı başlangıç ​​kılavuzuna bakın .

Umarım bunu faydalı bulursun!


3
Bu gerçekten de "bu tarihten" çok daha fazla. Harici bir API kullanıyor olsa da. Şahsen yagmail gibi bir şeyi dahili tutmak için kullanırdım .
PascalVKooten

6
@PascalvKooten Yammail için sürekli reklamlarınızı takip etmek kesinlikle eğlenceli (evet, efendim, bir dahaki sefere dikkate alacağım, efendim;). Ancak, neredeyse hiç kimsenin OP sorununu önemsemediğini, ancak daha çok farklı çözümler önerdiğini çok kafa karıştırıcı buluyorum. Sanki 2009
smart'ımdaki

Görevimin bazılarına eğlence değeri olması harika.
PascalVKooten

4
@flaschbier Hiç kimsenin OP sorunuyla ilgilenmemesinin nedeni, başlığın yanlış olması. "Python ile e-posta nasıl gönderilir?" insanların bu soruyu tıkladıklarında bakmak için gelmelerinin gerçek sebebidir ve yagmail'in sağlayabileceği bir cevap beklerlerdi : hoş ve kısa. İşte böyle. Daha fazla yagmail reklamı.
PascalVKooten

1
Mailgun müşterileri için, web servisleriyle posta göndermenin SMTP'den (özellikle herhangi bir ek kullanıyorsanız) bant genişliği açısından çok daha kolay olduğunu söylemek gerekir.
Ralph Bolton

44

Yagmail paketine tavsiyede bulunarak e-posta göndermenize yardımcı olmak istiyorum (reklamcı için özür dilerim, ama gerçekten yardımcı olabileceğini hissediyorum!).

Kodun tamamı sizin için:

import yagmail
yag = yagmail.SMTP(FROM, 'pass')
yag.send(TO, SUBJECT, TEXT)

Tüm argümanlar için varsayılanlar sağladığımı unutmayın; örneğin, kendinize göndermek istiyorsanız, atlayabilirsiniz TO, bir konu istemiyorsanız, atlayabilirsiniz.

Ayrıca, amaç html kodunu veya resimlerini (ve diğer dosyaları) eklemeyi gerçekten kolaylaştırmaktır.

İçeriği koyduğunuzda şöyle bir şey yapabilirsiniz:

contents = ['Body text, and here is an embedded image:', 'http://somedomain/image.png',
            'You can also find an audio file attached.', '/local/path/song.mp3']

Vay, ekleri göndermek ne kadar kolay! Bu yagmail olmadan 20 satır alacaktır;)

Ayrıca, bir kez ayarladıysanız, şifreyi bir daha asla girmeniz (ve güvenli bir şekilde saklamanız) gerekmez. Sizin durumunuzda şöyle bir şey yapabilirsiniz:

import yagmail
yagmail.SMTP().send(contents = contents)

ki bu çok daha özlü!

Sizi github'a bakmaya ya da doğrudan kurmaya davet ediyorum pip install yagmail.


4
bu en iyi çözüm olmalıdır.
Richard de Ree

1
OMG, bu tatlı, BASİT bir e-posta modülü. Teşekkürler!
pepoluan

1
yagmailGmail dışında kullanabilir miyim ? Kendi SMTP sunucum için kullanmaya çalışıyorum.
Anirban Nag'ın 'tintinmj'

@dtgq Endişeniz için teşekkür ederiz. Şahsen, saldırı vektörünü görmüyorum. Birisi göndermek istediğiniz yolun altındaki dosyayı değiştirecekse, bir Attachmentsınıfınız olması önemli değildir ; hala aynı şey. Kodunuzu değiştirebilirlerse, yine de istedikleri her şeyi yapabilirler (root ile / root olmadan, aynı wrt e-posta gönderme). Bu bana tipik "uygun / büyülü yani daha az güvenli olmalı" gibi görünüyor. Hangi tehdidi gördüğünüzü merak ediyorum.
PascalVKooten

14

Girinti problemi var. Aşağıdaki kod çalışacaktır:

import textwrap

def sendMail(FROM,TO,SUBJECT,TEXT,SERVER):
    import smtplib
    """this is some test documentation in the function"""
    message = textwrap.dedent("""\
        From: %s
        To: %s
        Subject: %s
        %s
        """ % (FROM, ", ".join(TO), SUBJECT, TEXT))
    # Send the mail
    server = smtplib.SMTP(SERVER)
    server.sendmail(FROM, TO, message)
    server.quit()


3
@geotheory SERVERişleve iletilen değişken kullanıcı kimlik bilgileri.
Kullanıcı

Geçerli bir SMTP iletisini basit dize enterpolasyonu ile el ile birleştirmek önerilmez ve yanıtınızda bunu mükemmel bir şekilde gösteren bir hata vardır. (Bu mesajın geçerli bir mesaj olması için vücudun boş bir satırla başlaması gerekir.) 1980'lerde düz metin dışındaki karakter kümelerini içeren herhangi bir şey için 7 bitlik yalnızca İngilizce olan ABD-ASCII (ekler, uluslararasılaştırma ve diğer MIME desteği) gerçekten bu şeyleri işleyen bir kitaplık kullanmak istiyorum. Python emailkütüphanesi bunu oldukça iyi yapıyor (özellikle 3.6'dan beri), yine de ne yaptığınızı anlamanız gerekiyor.
tripleee

7

İşte Python'da bir örnek 3.x, çok daha basit 2.x:

import smtplib
from email.message import EmailMessage
def send_mail(to_email, subject, message, server='smtp.example.cn',
              from_email='xx@example.com'):
    # import smtplib
    msg = EmailMessage()
    msg['Subject'] = subject
    msg['From'] = from_email
    msg['To'] = ', '.join(to_email)
    msg.set_content(message)
    print(msg)
    server = smtplib.SMTP(server)
    server.set_debuglevel(1)
    server.login(from_email, 'password')  # user & password
    server.send_message(msg)
    server.quit()
    print('successfully sent the mail.')

bu işlevi çağır:

send_mail(to_email=['12345@qq.com', '12345@126.com'],
          subject='hello', message='Your analysis has done!')

aşağıda sadece Çinli kullanıcılar için olabilir:

126/163, 网易 邮箱 kullanıyorsanız, aşağıdaki gibi "客户 端 授权 密码" ayarlamanız gerekir:

resim açıklamasını buraya girin

ref: https://stackoverflow.com/a/41470149/2803344 https://docs.python.org/3/library/email.examples.html#email-examples


4

Kodda işlevde (tamam) girintilerken, ham ileti dizesinin satırlarını da girintilendirdiniz. Ancak önde gelen beyaz boşluk, RFC 2822 - İnternet İleti Biçimi'nin 2.2.3 ve 3.2.3 bölümlerinde açıklandığı gibi başlık satırlarının katlanmasını (birleştirme) gerektirir :

Her başlık alanı, mantıksal olarak alan adı, iki nokta üst üste ve alan gövdesini içeren tek bir karakter dizisidir. Bununla birlikte, kolaylık sağlamak ve satır başına 998/78 karakter sınırlamalarını ele almak için, bir başlık alanının alan gövde kısmı, çok satırlı bir temsile bölünebilir; buna "katlama" denir.

sendmailÇağrınızın işlev formunda , tüm hatlar boşlukla başlar ve bu nedenle "katlanır" (birleştirilmiş) ve siz göndermeye çalışıyorsunuz

From: monty@python.com    To: jon@mycompany.com    Subject: Hello!    This message was sent with Python's smtplib.

Zihnimizin önerdiği gibi smtplib, artık anlamayacak To:ve Subject:başlıkları artık anlayamayacak , çünkü bu isimler sadece bir satırın başında tanınıyor. Bunun yerine smtplibçok uzun bir gönderen e-posta adresi alır:

monty@python.com    To: jon@mycompany.com    Subject: Hello!    This message was sent with Python's smtplib.

Bu işe yaramaz ve İstisna gelir.

Çözüm basit: Dizeyi messagedaha önce olduğu gibi koruyun. Bu bir işlevle (Zeeshan'ın önerdiği gibi) veya kaynak kodda hemen yapılabilir:

import smtplib

def sendMail(FROM,TO,SUBJECT,TEXT,SERVER):
    """this is some test documentation in the function"""
    message = """\
From: %s
To: %s
Subject: %s

%s
""" % (FROM, ", ".join(TO), SUBJECT, TEXT)
    # Send the mail
    server = smtplib.SMTP(SERVER)
    server.sendmail(FROM, TO, message)
    server.quit()

Şimdi açılma gerçekleşmiyor ve siz

From: monty@python.com
To: jon@mycompany.com
Subject: Hello!

This message was sent with Python's smtplib.

eski kodunuzla işe yarayan ve yapılan şey budur.

Ayrıca , RFC'nin 3.5 bölümünü (gerekli olan) barındırmak için başlık ve gövde arasındaki boş çizgiyi koruduğumu ve Python stil kılavuzu PEP-0008'e (isteğe bağlı olan) göre fonksiyonun dışında yer aldığını unutmayın .


3

Muhtemelen mesajınıza sekmeler koyuyor. SendMail'e iletmeden önce mesajı yazdırın.


1

E-posta Hesabındaki hem Gönderen hem de Alıcı için e-posta gönderip Bilinmeyen kaynaklardan (Harici Kaynaklar) e-posta alma izni verdiğinizden emin olun.

import smtplib

#Ports 465 and 587 are intended for email client to email server communication - sending email
server = smtplib.SMTP('smtp.gmail.com', 587)

#starttls() is a way to take an existing insecure connection and upgrade it to a secure connection using SSL/TLS.
server.starttls()

#Next, log in to the server
server.login("#email", "#password")

msg = "Hello! This Message was sent by the help of Python"

#Send the mail
server.sendmail("#Sender", "#Reciever", msg)

resim açıklamasını buraya girin


msggeçerli bir SMTP mesajı değildir ve posta sunucunuz kabul ederse eterde kaybolmuş gibi görünür.
tripleee

0

Bunun nasıl çalıştığını yeni anladığım için iki parçamı buraya koyacağımı düşündüm.

SERVER bağlantı ayarlarınızda bağlantı noktası belirtilmemiş gibi görünüyor, varsayılan bağlantı noktası kullanmayan SMTP sunucuma bağlanmaya çalıştığımda bu beni biraz etkiledi: 25.

Smtplib.SMTP belgelerine göre, ehlo veya helo isteğiniz / yanıtınız otomatik olarak ele alınmalıdır, bu nedenle bu konuda endişelenmenize gerek yoktur (ancak her şey başarısız olursa onaylanacak bir şey olabilir).

Kendinize sormanız gereken başka bir şey de, SMTP sunucunuzda SMTP bağlantılarına izin vermiş olmanız mı? GMAIL ve ZOHO gibi bazı siteler için gerçekten girip e-posta hesabındaki IMAP bağlantılarını etkinleştirmeniz gerekir. Posta sunucunuz belki 'localhost' dan gelmeyen SMTP bağlantılarına izin vermeyebilir? Bakılacak bir şey.

Son olarak, TLS'de bağlantıyı denemek ve başlatmak isteyebilirsiniz. Çoğu sunucu artık bu tür bir kimlik doğrulama gerektiriyor.

E-postama iki TO alanı sıkıştığımı göreceksiniz. Msg ['TO'] ve msg ['FROM'] msg sözlük öğeleri, e-postanın başlıklarında doğru bilginin Kime / Kimden alanlarındaki e-postanın alıcı ucunda görmesini sağlar (siz hatta burada bir Yanıtla alanı ekleyebilir. TO ve FROM alanlarının kendileri sunucunun gerektirdiği şeydir.Yerlerinde uygun e-posta başlıklarına sahip değilse e-postaları reddeden bazı e-posta sunucularını duydum.

Bu, bir işlevde, yerel bilgisayarımı ve uzak bir SMTP sunucusunu (gösterildiği gibi ZOHO) kullanarak bir * .txt dosyasının içeriğini e-posta ile göndermem için kullandığım kod:

def emailResults(folder, filename):

    # body of the message
    doc = folder + filename + '.txt'
    with open(doc, 'r') as readText:
        msg = MIMEText(readText.read())

    # headers
    TO = 'to_user@domain.com'
    msg['To'] = TO
    FROM = 'from_user@domain.com'
    msg['From'] = FROM
    msg['Subject'] = 'email subject |' + filename

    # SMTP
    send = smtplib.SMTP('smtp.zoho.com', 587)
    send.starttls()
    send.login('from_user@domain.com', 'password')
    send.sendmail(FROM, TO, msg.as_string())
    send.quit()

0

SMTP modülünün içerik yöneticisini desteklediğini belirtmek gerekir, bu nedenle quit () öğesini elle çağırmaya gerek yoktur, bu bir istisna olsa bile her zaman çağrıldığını garanti eder.

    with smtplib.SMTP_SSL('smtp.gmail.com', 465) as server:
        server.ehlo()
        server.login(user, password)
        server.sendmail(from, to, body)

-1

Kodunuz söz konusu olduğunda, bunun dışında temelde yanlış bir şey yok gibi görünüyor, bunun dışında aslında bu işlevi nasıl çağırdığınız belli değil. Düşünebildiğim tek şey, sunucunuz yanıt vermediğinde bu SMTPServerDisconnected hatasını alacağınızdır. Smtplib'de getreply () işlevini ararsanız (aşağıda alıntı), bir fikir edineceksiniz.

def getreply(self):
    """Get a reply from the server.

    Returns a tuple consisting of:

      - server response code (e.g. '250', or such, if all goes well)
        Note: returns -1 if it can't read response code.

      - server response string corresponding to response code (multiline
        responses are converted to a single, multiline string).

    Raises SMTPServerDisconnected if end-of-file is reached.
    """

https://github.com/rreddy80/sendEmails/blob/master/sendEmailAttachments.py adresinden e-posta göndermek için bir işlev çağrısı da kullanan bir örnek kontrol edin (yapmaya çalışıyorsanız) (DRY yaklaşımı).

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.