Bagaimana cara mengirim email dengan Python?

170

Kode ini berfungsi dan mengirimkan saya email:

import smtplib
#SERVER = "localhost"

FROM = '[email protected]'

TO = ["[email protected]"] # 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()

Namun jika saya mencoba untuk membungkusnya dalam fungsi seperti ini:

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

dan menyebutnya saya mendapatkan kesalahan berikut:

 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

Adakah yang bisa membantu saya memahami mengapa?

cloud311
sumber
2
bagaimana Anda memanggil fungsi?
Jochen Ritzel
Apakah lekukan yang Anda poskan sama dengan yang ada di file Anda?
gddc
@ gddc tidak, saya memastikan untuk membuat lekukan dengan benar, itulah cara saya menempelkannya.
cloud311
Saya memanggil fungsi dengan mengimpornya ke modul utama saya dan mengirimkan parameter yang telah saya tetapkan ke dalamnya.
cloud311
4
Meskipun saran @ Arrieta untuk menggunakan paket email adalah cara terbaik untuk menyelesaikan ini, pendekatan Anda dapat bekerja. Perbedaan antara dua versi Anda ada di string: (1) seperti yang ditunjukkan oleh @NickODell, Anda memiliki spasi kosong di versi fungsi. Tajuk tidak boleh memiliki ruang depan (kecuali jika dibungkus). (2) kecuali TEXT menyertakan baris kosong terkemuka, Anda telah kehilangan pemisah antara header dan tubuh.
Tony Meyer

Jawaban:

192

Saya sarankan Anda menggunakan paket standar emaildan smtplibbersama - sama mengirim email. Silakan lihat contoh berikut (direproduksi dari dokumentasi Python ). Perhatikan bahwa jika Anda mengikuti pendekatan ini, tugas "sederhana" memang sederhana, dan tugas yang lebih kompleks (seperti melampirkan objek biner atau mengirim pesan multipart HTML / polos) diselesaikan dengan sangat cepat.

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

Untuk mengirim email ke beberapa tujuan, Anda juga dapat mengikuti contoh dalam dokumentasi Python :

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

Seperti yang Anda lihat, header Todi MIMETextobjek harus berupa string yang terdiri dari alamat email yang dipisahkan oleh koma. Di sisi lain, argumen kedua ke sendmailfungsi harus berupa daftar string (setiap string adalah alamat email).

Jadi, jika Anda memiliki tiga alamat email: [email protected], [email protected], dan [email protected], dapat Anda lakukan sebagai berikut (bagian jelas dihilangkan):

to = ["[email protected]", "[email protected]", "[email protected]"]
msg['To'] = ",".join(to)
s.sendmail(me, to, msg.as_string())

yang ",".join(to)bagian membuat string tunggal dari daftar, dipisahkan dengan koma.

Dari pertanyaan Anda, saya berpendapat bahwa Anda belum melalui tutorial Python - itu HARUS jika Anda ingin mendapatkan di mana saja di Python - dokumentasi sebagian besar sangat baik untuk perpustakaan standar.

Escualo
sumber
1
Terima kasih ini berfungsi dengan sangat baik dari dalam suatu fungsi, namun bagaimana saya bisa mengirim ke banyak penerima? Karena msg [sepertinya] adalah kunci kamus, saya mencoba memisahkan msg [ke] dengan tanda titik koma tapi itu sepertinya tidak berfungsi.
cloud311
1
@ cloud311 persis seperti yang Anda miliki di kode Anda. Ia ingin string dibatasi dengan koma: ", ".join(["[email protected]", "[email protected]"])
Tim McNamara
3
Perhatikan juga bahwa tajuk Ke: memiliki semantik yang berbeda dari penerima amplop. Misalnya, Anda dapat menggunakan '"Tony Meyer" <[email protected]>' sebagai alamat di header To:, tetapi penerima amplop harus hanya "[email protected]". Untuk membangun alamat 'bagus' Ke:, gunakan email.utils.formataddr, seperti email.utils.formataddr ("Tony Meyer", "[email protected]").
Tony Meyer
2
Perbaikan kecil: file harus dibuka menggunakan with: with open(textfile, 'rb') as fp:. Tutup eksplisit dapat dijatuhkan, karena withblok akan menutup file meskipun terjadi kesalahan di dalamnya.
jpmc26
1
Tidak spesifik untuk jawaban ini, tetapi saat menghubungkan ke server SMTP yang tidak Anda kontrol, Anda harus mempertimbangkan kemungkinan bahwa itu tidak dapat dijangkau, lambat, menolak koneksi atau apa pun. Dari segi kode Anda akan mendapatkan pengecualian, tetapi kemudian Anda harus mencari cara untuk mencoba kembali pengirimannya nanti. Jika Anda berbicara dengan sendmail / postfix Anda sendiri, maka itu akan menangani pengiriman ulang itu untuk Anda.
Ralph Bolton
67

Nah, Anda ingin memiliki jawaban yang mutakhir dan modern.

Inilah jawaban saya:

Ketika saya perlu mengirimkan python, saya menggunakan mailgun API yang banyak sakit kepala dengan mengirim surat diurutkan. Mereka memiliki aplikasi / api hebat yang memungkinkan Anda mengirim 10.000 email per bulan secara gratis.

Mengirim email akan seperti ini:

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": ["[email protected]", "YOU@YOUR_DOMAIN_NAME"],
              "subject": "Hello",
              "text": "Testing some Mailgun awesomness!"})

Anda juga dapat melacak acara dan banyak lagi, lihat panduan mulai cepat .

Saya harap Anda menemukan ini berguna!

Dennis Decoene
sumber
3
Ini memang jauh lebih "dari tanggal ini". Meskipun menggunakan API eksternal. Saya pribadi menggunakan sesuatu seperti yagmail untuk menjaganya tetap internal.
PascalVKooten
6
@PascalvKooten Benar-benar lucu untuk mengikuti iklan konstan Anda untuk yagmail (ya, Pak, saya akan mempertimbangkannya lain kali, Sir;). Tetapi saya merasa sangat membingungkan bahwa hampir tidak ada yang peduli dengan masalah OP, tetapi menyarankan solusi yang jauh berbeda. Seolah-olah saya bertanya bagaimana mengganti bola lampu di 2009 pintar saya dan jawabannya adalah: Beli Mercedes nyata ...
flaschbier
Hebat bahwa misi saya memiliki nilai hiburan bagi sebagian orang.
PascalVKooten
4
@ flaschbier Alasan tidak ada yang peduli tentang masalah OP adalah karena judulnya salah. "Bagaimana cara mengirim email dengan Python?" adalah alasan sebenarnya orang datang untuk melihat ketika mereka mengklik pertanyaan itu, dan mereka akan mengharapkan jawaban yang dapat diberikan yagmail : bagus dan pendek. Ini dia. Lebih banyak iklan yagmail.
PascalVKooten
1
Hanya untuk mengatakan bahwa untuk pelanggan Mailgun, mengirim email melalui layanan web jauh lebih ramah bandwidth daripada melalui SMTP (terutama jika menggunakan lampiran apa pun).
Ralph Bolton
44

Saya ingin membantu Anda mengirim email dengan menyarankan paket yagmail (saya pengelola, maaf untuk iklannya, tetapi saya merasa ini benar-benar dapat membantu!).

Seluruh kode untuk Anda adalah:

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

Perhatikan bahwa saya memberikan default untuk semua argumen, misalnya jika Anda ingin mengirim sendiri, Anda dapat menghilangkan TO , jika Anda tidak menginginkan subjek, Anda juga dapat menghilangkannya.

Selain itu, tujuannya juga untuk membuatnya sangat mudah untuk melampirkan kode html atau gambar (dan file lainnya).

Di mana Anda meletakkan konten, Anda dapat melakukan sesuatu seperti:

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

Wow, betapa mudahnya mengirim lampiran! Ini akan memakan waktu 20 baris tanpa yagmail;)

Juga, jika Anda mengaturnya sekali, Anda tidak perlu memasukkan kata sandi lagi (dan menyimpannya dengan aman). Dalam kasus Anda, Anda dapat melakukan sesuatu seperti:

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

yang jauh lebih ringkas!

Saya mengundang Anda untuk melihat github atau menginstalnya secara langsung pip install yagmail.

PascalVKooten
sumber
4
ini harus menjadi solusi teratas.
Richard de Ree
1
OMG, itu modul email yang manis dan SEDERHANA . Terima kasih!
pepoluan
1
Bisakah saya menggunakan yagmailselain gmail? Saya mencoba menggunakan untuk server SMTP saya sendiri.
Anirban Nag 'tintinmj'
@ dtgq Terima kasih atas perhatian Anda. Secara pribadi, saya tidak melihat vektor serangan. Jika seseorang akan mengubah file di bawah jalur yang ingin Anda kirim, maka tidak masalah jika Anda memiliki Attachmentkelas; itu masih hal yang sama. Jika mereka dapat mengubah kode Anda, maka mereka dapat tetap melakukan apa pun yang mereka inginkan (dengan / tanpa root, itu sama dengan pengiriman email wrt). Bagi saya ini seperti "khasnya nyaman / magis sehingga pasti kurang aman". Saya ingin tahu apa ancaman nyata yang Anda lihat?
PascalVKooten
14

Ada masalah lekukan. Kode di bawah ini akan berfungsi:

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

Zeeshan
sumber
3
@geotheory SERVERvariabel yang diteruskan ke fungsi adalah kredensial pengguna.
Pengguna
Menyambung secara bersama-sama pesan SMTP yang valid dengan interpolasi string sederhana tidak disarankan, dan jawaban Anda memiliki bug yang menggambarkan hal ini dengan sempurna. (Badan harus mulai dengan baris kosong agar ini menjadi pesan yang valid.) Untuk apa pun yang melibatkan rangkaian karakter selain teks biasa pada 1980-an US-ASCII 7-bit hanya dalam bahasa Inggris (lampiran, internasionalisasi, dan dukungan MIME lainnya) Anda benar - benar ingin menggunakan perpustakaan yang menangani hal ini. emailPustaka Python melakukan ini dengan cukup baik (terutama sejak 3,6) meskipun masih membutuhkan pemahaman tentang apa yang Anda lakukan.
tripleee
7

Berikut adalah contoh tentang Python 3.x, jauh lebih sederhana daripada 2.x:

import smtplib
from email.message import EmailMessage
def send_mail(to_email, subject, message, server='smtp.example.cn',
              from_email='[email protected]'):
    # 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.')

panggil fungsi ini:

send_mail(to_email=['[email protected]', '[email protected]'],
          subject='hello', message='Your analysis has done!')

di bawah ini hanya untuk pengguna China:

Jika Anda menggunakan 126/163, 网易 邮箱, Anda perlu mengatur "客户 端 授权 密码", seperti di bawah ini:

masukkan deskripsi gambar di sini

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

Belter
sumber
4

Saat indentasi kode Anda dalam fungsi (yang ok), Anda juga membuat indentasi baris string pesan mentah. Tetapi ruang putih terdepan menyiratkan pelipatan (penggabungan) dari baris header, seperti yang dijelaskan dalam bagian 2.2.3 dan 3.2.3 dari RFC 2822 - Format Pesan Internet :

Setiap bidang tajuk secara logis adalah satu baris karakter yang terdiri dari nama bidang, titik dua, dan badan bidang. Namun untuk kenyamanan, dan untuk menangani batasan karakter 998/78 per baris, bagian tubuh bidang dari bidang tajuk dapat dibagi menjadi beberapa representasi garis; ini disebut "lipat".

Dalam bentuk fungsi sendmailpanggilan Anda , semua saluran dimulai dengan spasi putih dan "terbuka" (digabungkan) dan Anda mencoba mengirim

From: [email protected]    To: [email protected]    Subject: Hello!    This message was sent with Python's smtplib.

Selain yang disarankan oleh pikiran kita, smtplibtidak akan memahami To:dan Subject:header lagi, karena nama-nama ini hanya dikenali pada awal baris. Sebaliknya smtplibakan menerima alamat email pengirim yang sangat panjang:

[email protected]    To: [email protected]    Subject: Hello!    This message was sent with Python's smtplib.

Ini tidak akan berhasil dan begitu pula Pengecualian Anda.

Solusinya sederhana: Pertahankan messagestring seperti sebelumnya. Ini dapat dilakukan oleh suatu fungsi (seperti yang disarankan Zeeshan) atau langsung dalam kode sumber:

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

Sekarang unfolding tidak terjadi dan Anda kirim

From: [email protected]
To: [email protected]
Subject: Hello!

This message was sent with Python's smtplib.

mana yang berhasil dan apa yang dilakukan oleh kode lama Anda.

Perhatikan bahwa saya juga menjaga garis kosong antara header dan tubuh untuk mengakomodasi bagian 3.5 dari RFC (yang diperlukan) dan meletakkan menyertakan di luar fungsi sesuai dengan panduan gaya Python PEP-0008 (yang opsional).

flaschbier
sumber
3

Mungkin menempatkan tab ke pesan Anda. Cetak pesan sebelum Anda meneruskannya ke sendMail.

Nick ODell
sumber
1

Pastikan Anda telah memberikan izin kepada Pengirim dan Penerima untuk mengirim email dan menerima email dari sumber tidak dikenal (Sumber Eksternal) di Akun Email.

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)

masukkan deskripsi gambar di sini

Devanand Sharma
sumber
msgbukan pesan SMTP yang valid, dan hanya akan tampak menghilang ke eter jika server email Anda menerimanya.
tripleee
0

Kupikir aku akan memasukkan dua bitku di sini karena aku baru saja menemukan cara kerjanya.

Tampaknya Anda tidak memiliki port yang ditentukan pada pengaturan koneksi SERVER Anda, ini mempengaruhi saya sedikit ketika saya mencoba untuk terhubung ke server SMTP saya yang tidak menggunakan port default: 25.

Menurut dokumen smtplib.SMTP, permintaan / respons ehlo atau helo Anda secara otomatis akan dijaga, jadi Anda tidak perlu khawatir tentang ini (tetapi mungkin sesuatu untuk mengonfirmasi jika semuanya gagal).

Hal lain yang perlu Anda tanyakan adalah apakah Anda mengizinkan koneksi SMTP di server SMTP Anda sendiri? Untuk beberapa situs seperti GMAIL dan ZOHO Anda harus benar-benar masuk dan mengaktifkan koneksi IMAP dalam akun email. Server email Anda mungkin tidak mengizinkan koneksi SMTP yang tidak datang dari 'localhost' mungkin? Sesuatu untuk dilihat.

Yang terakhir adalah Anda mungkin ingin mencoba dan memulai koneksi pada TLS. Sebagian besar server sekarang memerlukan jenis otentikasi ini.

Anda akan melihat saya memasukkan dua bidang KE ke dalam email saya. Item kamus msg ['TO'] dan msg ['FROM'] memungkinkan informasi yang benar muncul di tajuk email itu sendiri, yang dilihat orang di ujung penerima email di bidang Ke / Dari (Anda bahkan mungkin dapat menambahkan bidang Balas Ke di sini. Bidang TO dan FROM sendiri adalah yang diperlukan server. Saya tahu saya pernah mendengar beberapa server email menolak email jika mereka tidak memiliki header email yang tepat.

Ini adalah kode yang saya gunakan, dalam suatu fungsi, yang berfungsi bagi saya untuk mengirim email isi file * .txt menggunakan komputer lokal saya dan server SMTP jarak jauh (ZOHO seperti yang ditunjukkan):

def emailResults(folder, filename):

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

    # headers
    TO = '[email protected]'
    msg['To'] = TO
    FROM = '[email protected]'
    msg['From'] = FROM
    msg['Subject'] = 'email subject |' + filename

    # SMTP
    send = smtplib.SMTP('smtp.zoho.com', 587)
    send.starttls()
    send.login('[email protected]', 'password')
    send.sendmail(FROM, TO, msg.as_string())
    send.quit()
ntk4
sumber
0

Perlu dicatat bahwa modul SMTP mendukung manajer konteks sehingga tidak perlu secara manual memanggil berhenti (), ini akan menjamin itu selalu dipanggil bahkan jika ada pengecualian.

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

Sejauh menyangkut kode Anda, tampaknya tidak ada yang salah secara mendasar kecuali bahwa, Anda tidak jelas bagaimana Anda sebenarnya memanggil fungsi itu. Yang bisa saya pikirkan adalah bahwa ketika server Anda tidak merespons maka Anda akan mendapatkan kesalahan SMTPServerDisconnected ini. Jika Anda mencari fungsi getreply () di smtplib (kutipan di bawah), Anda akan mendapatkan ide.

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.
    """

periksa contoh di https://github.com/rreddy80/sendEmails/blob/master/sendEmailAttachments.py yang juga menggunakan panggilan fungsi untuk mengirim email, jika itu yang Anda coba lakukan (pendekatan KERING).

kasar
sumber