Gửi mail với Python

Bạn cần gì để gửi email với Python? Chỉ một chút kiến thức lập trình web sử dụng Python cơ bản. Hướng dẫn này sẽ hướng dẫn bạn qua các bước cần thiết nhất để gửi email qua máy chủ SMTP:

  • Cấu hình một server local để testing
  • Máy chủ SMTP local
  • Mailtrap để test máy chủ SMTP local
  • Các loại email khác nhau: HTML, có hình ảnh và tệp đính kèm
  • Gửi nhiều email được cá nhân hóa
  • Một số tùy chọn gửi email phổ biến như Gmail và dịch vụ email giao dịch

Gửi email bằng cách sử dụng SMTP

Tin tốt đầu tiên về Python là nó có một module tích hợp sẵn để gửi email qua SMTP trong thư viện chuẩn của nó. Không cần cài đặt thêm hoặc thủ thuật. Bạn có thể nhập module bằng cách sử dụng câu lệnh sau:

import smtplib

Để đảm bảo rằng module đã được nhập đúng cách và có được mô tả đầy đủ về các lớp và đối số của nó, hãy nhập một Python session:

help(smtplib)

Ở bước tiếp theo, mình sẽ nói một chút về cách cấu hình máy chủ SMTP.

Tạo một máy chủ SMTP để test email trong Python

Khi viết một ứng dụng mới hoặc thêm bất kỳ chức năng nào, đặc biệt là khi thực hiện lần đầu tiên, bạn nên test nó trên một máy chủ thử nghiệm. Dưới đây là vài lý do bạn rất cần làm việc đó:

  • Bạn không lỡ tay gửi hàng trăm email cho bạn bè, khách hàng hay tệ hơn nữa là người lạ. Nó sẽ thành một incident rất lớn đấy.
  • Bạn không làm hòm thư của bạn tràn ngập với đống mail test.
  • Tên miền của bạn không bị liệt vào danh sách đen do spam.

Và với Python bạn có 2 lựa chọn để tạo một máy chủ để test :3

tạo một local SMTP server

Nếu bạn thích làm việc trong môi trường local, máy chủ SMTP local là một lựa chọn rất hợp lý. Python cung cấp một module smtpd. Nó có tính năng DebuggingServer, sẽ loại bỏ các email bạn đang gửi và sẽ in chúng ra ở stdout.

Để chạy SMTP server ở cổng 1025 bạn sẽ dùng lệnh sau:

python -m smtpd -n -c DebuggingServer localhost: 1025

Để chạy máy chủ SMTP trên cổng 25, bạn sẽ cần có quyền root:

sudo python -m smtpd -n -c DebuggingServer localhost: 25

Nó sẽ giúp bạn xác minh xem mã của bạn có hoạt động hay không và chỉ ra các vấn đề có thể xảy ra nếu có. Tuy nhiên, với nó bạn không có cơ hội kiểm tra mẫu email HTML của bạn được hiển thị như thế nào. Lúc này một server SMTP giả sẽ thứ bạn tìm tới.

Giả SMTP server

Máy chủ SMTP giả bắt chước công việc của máy chủ thực sự. Trong các ví dụ khác trong bài viết này, mình sẽ sử dụng Mailtrap. Ngoài việc kiểm tra gửi email, nó sẽ cho phép chúng ta kiểm tra xem email sẽ được hiển thị và hiển thị như thế nào, xem lại dữ liệu thô của tin nhắn cũng như sẽ cung cấp cho chúng ta một báo cáo spam. Mailtrap rất dễ thiết lập: bạn sẽ chỉ cần sao chép thông tin đăng nhập do ứng dụng tạo ra và dán chúng vào mã của bạn.

Đây là cách bạn cài đặt nó:

import smtplib
port = 2525
smtp_server = "smtp.mailtrap.io"
login = "1a2b3c4d5e6f7g" # your login generated by Mailtrap
password = "1a2b3c4d5e6f7g" # your password generated by Mailtrap

Mailtrap làm cho mọi thứ thậm chí dễ dàng hơn. Chuyển đến phần Tích hợp trong tab Cài đặt SMTP và một template mail đơn giản được cung cấp sẵn, với thông tin đăng nhập Mailtrap của bạn trong đó. Đây là tùy chọn cơ bản nhất để hướng dẫn Python script của bạn về việc gửi mail:

Code trông khá đơn giản, phải không? Chúng ta hãy xem xét kỹ hơn về nó và thêm một số phương thức xử lý lỗi (xem #explanations ở giữa). Để bắt lỗi, chúng tôi sử dụng các khối “try” và “except”.

# the first step is always the same: import all necessary components:
import smtplib
from socket import gaierror

# now you can play with your code. Let’s define the SMTP server separately here:
port = 2525 
smtp_server = "smtp.mailtrap.io"
login = "1a2b3c4d5e6f7g" # paste your login generated by Mailtrap
password = "1a2b3c4d5e6f7g" # paste your password generated by Mailtrap

# specify the sender’s and receiver’s email addresses
sender = "[email protected]"
receiver = "[email protected]"

# type your message: use two newlines (\n) to separate the subject from the message body, and use 'f' to  automatically insert variables in the text
message = f"""\
Subject: Hi Mailtrap
To: {receiver}
From: {sender}
This is my first message with Python."""

try:
    #send your message with credentials specified above
    with smtplib.SMTP(smtp_server, port) as server:
        server.login(login, password)
        server.sendmail(sender, receiver, message)

    # tell the script to report if your message was sent or which errors need to be fixed 
    print('Sent')
except (gaierror, ConnectionRefusedError):
    print('Failed to connect to the server. Bad connection settings?')
except smtplib.SMTPServerDisconnected:
    print('Failed to connect to the server. Wrong user/password?')
except smtplib.SMTPException as e:
    print('SMTP error occurred: ' + str(e))

Khi bạn nhận được kết quả Đã gửi trong Shell, bạn sẽ thấy tin nhắn của mình trong hộp thư đến Mailtrap:

Gửi email với định dạng HTML

Trong hầu hết các trường hợp, bạn cần thêm một số định dạng, liên kết hoặc hình ảnh email của bạn. Chúng ta chỉ cần đặt tất cả những thứ này với nội dung HTML. Với mục đích này, Python có một email package.

Chúng ta sẽ xử lý loại tin nhắn MIME, có thể kết hợp HTML và văn bản thuần túy. Trong Python, nó được xử lý bởi module email.mime.

Tốt hơn hết chúng ta viết riêng một phiên bản văn bản và một phiên bản HTML, sau đó hợp nhất chúng với phiên bản MIMEMultipart ("thay thế"). Nó có nghĩa là một tin nhắn như vậy có hai tùy chọn render tương ứng. Trong trường hợp HTML không được hiển thị thành công vì một số lý do nào đó, phiên bản văn bản vẫn sẽ có sẵn.

Đầu vào:

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

port = 2525 
smtp_server = "smtp.mailtrap.io" 
login = "1a2b3c4d5e6f7g" # paste your login generated by Mailtrap 
password = "1a2b3c4d5e6f7g" # paste your password generated by Mailtrap
sender_email = "[email protected]" 
receiver_email = "[email protected]" 
message = MIMEMultipart("alternative") 
message["Subject"] = "multipart test" 
message["From"] = sender_email 
message["To"] = receiver_email 

# write the plain text part text = """\ Hi, Check out the new post on the Mailtrap blog: SMTP Server for Testing: Cloud-based or Local? https://blog.mailtrap.io/2018/09/27/cloud-or-local-smtp-server/ Feel free to let us know what content would be useful for you!""" 
# write the HTML part html = """\ <html> <body> <p>Hi,<br> Check out the new post on the Mailtrap blog:</p> <p><a href="https://blog.mailtrap.io/2018/09/27/cloud-or-local-smtp-server">SMTP Server for Testing: Cloud-based or Local?</a></p> <p> Feel free to <strong>let us</strong> know what content would be useful for you!</p> </body> </html> """

# convert both parts to MIMEText objects and add them to the MIMEMultipart message 
part1 = MIMEText(text, "plain") 
part2 = MIMEText(html, "html") 
message.attach(part1)
message.attach(part2)
 
# send your email with smtplib.SMTP("smtp.mailtrap.io", 2525) as server: server.login(login, password) 
server.sendmail( sender_email, receiver_email, message.as_string() ) 
print('Sent')

Kết quả:

Gửi email với tệp đính kèm trong Python

Bước tiếp theo trong việc thành thạo gửi email bằng Python là đính kèm tệp. Tệp đính kèm vẫn là đối tượng của MIME nhưng chúng ta cần mã hóa chúng bằng module base64. Một vài điểm quan trọng về các tệp đính kèm:

  • Python cho phép bạn đính kèm tệp văn bản, hình ảnh, tệp âm thanh và thậm chí các ứng dụng. Bạn chỉ cần sử dụng lớp email thích hợp như email.mime.audio.MIMEAudio hoặc email.mime.image.MIMEImage. Để biết thông tin đầy đủ, hãy tham khảo phần này của tài liệu Python.
  • Hãy nhớ về kích thước tệp: gửi tệp trên 20MB là một thực tế xấu. Trong các email giao dịch, các tệp PDF được sử dụng thường xuyên nhất: chúng ta thường nhận được biên lai, vé, thẻ lên máy bay, xác nhận đơn đặt hàng, v.v ... Vì vậy, hãy xem lại cách gửi thẻ lên máy bay dưới dạng tệp PDF.

Đầu vào:

import smtplib

# import the corresponding modules
from email import encoders
from email.mime.base import MIMEBase
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText

port = 2525 
smtp_server = "smtp.mailtrap.io"
login = "1a2b3c4d5e6f7g" # paste your login generated by Mailtrap
password = "1a2b3c4d5e6f7g" # paste your password generated by Mailtrap

subject = "An example of boarding pass"
sender_email = "[email protected]"
receiver_email = "[email protected]"

message = MIMEMultipart()
message["From"] = sender_email
message["To"] = receiver_email
message["Subject"] = subject

# Add body to email
body = "This is an example of how you can send a boarding pass in attachment with Python"
message.attach(MIMEText(body, "plain"))

filename = "yourBP.pdf"
# Open PDF file in binary mode

# We assume that the file is in the directory where you run your Python script from
with open(filename, "rb") as attachment:
    # The content type "application/octet-stream" means that a MIME attachment is a binary file
    part = MIMEBase("application", "octet-stream")
    part.set_payload(attachment.read())

# Encode to base64
encoders.encode_base64(part)

# Add header 
part.add_header(
    "Content-Disposition",
    f"attachment; filename= {filename}",
)

# Add attachment to your message and convert it to string
message.attach(part)
text = message.as_string()

# send your email
with smtplib.SMTP("smtp.mailtrap.io", 2525) as server:
    server.login(login, password)
    server.sendmail(
        sender_email, receiver_email, text
    )
print('Sent')

Kết quả:

Để đính kèm nhiều tệp, bạn có thể gọi phương thức message.attach () nhiều lần.

Cách gửi email có đính kèm hình ảnh

Hình ảnh, ngay cả khi chúng là một phần của nội dung thư, cũng là tệp đính kèm. Có ba loại trong số chúng: tệp đính kèm CID (được nhúng dưới dạng đối tượng MIME), hình ảnh định dạng base64 (nhúng trong code) và hình ảnh được liên kết.

Để thêm tệp đính kèm CID, chúng tôi sẽ tạo một email MIME với thành phần MIMEImage:

Đầu vào:

# import all necessary components
import smtplib
from email.mime.text import MIMEText
from email.mime.image import MIMEImage
from email.mime.multipart import MIMEMultipart

port = 2525
smtp_server = "smtp.mailtrap.io"
login = "1a2b3c4d5e6f7g" # paste your login generated by Mailtrap
password = "1a2b3c4d5e6f7g" # paste your password generated by Mailtrap

sender_email = "[email protected]"
receiver_email = "[email protected]"
message = MIMEMultipart("alternative")
message["Subject"] = "CID image test"
message["From"] = sender_email
message["To"] = receiver_email

# write the HTML part
html = """\
<html>
 <body>
   <img src="cid:Mailtrapimage">
 </body>
</html>
"""

part = MIMEText(html, "html")
message.attach(part)

# We assume that the image file is in the same directory that you run your Python script from
FP = open('mailtrap.jpg', 'rb')
image = MIMEImage(fp.read())
fp.close()

# Specify the  ID according to the img src in the HTML part
image.add_header('Content-ID', '<Mailtrapimage>')
message.attach(image)

# send your email
with smtplib.SMTP("smtp.mailtrap.io", 2525) as server:
   server.login(login, password)
   server.sendmail(
       sender_email, receiver_email, message.as_string()
   )
print('Sent')

Kết quả:

Hình ảnh CID được hiển thị cả dưới dạng một phần của thông điệp HTML và dưới dạng tệp đính kèm. Các thư có loại hình ảnh này thường được coi là thư rác: kiểm tra tab Analytics trong Mailtrap để xem tỷ lệ spam và các đề xuất về cải tiến của nó. Nhiều ứng dụng email - cụ thể là Gmail - không thể hiển thị hình ảnh CID trong hầu hết các trường hợp. Vì vậy, hãy cùng xem lại cách nhúng hình ảnh được mã hóa base64.

Ở đây mình sẽ sử dụng module base64 và thử nghiệm với cùng một tệp ảnh:

# import the necessary components first
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
import base64

port = 2525
smtp_server = "smtp.mailtrap.io"
login = "1a2b3c4d5e6f7g" # paste your login generated by Mailtrap
password = "1a2b3c4d5e6f7g" # paste your password generated by Mailtrap

sender_email = "[email protected]"
receiver_email = "[email protected]"
message = MIMEMultipart("alternative")
message["Subject"] = "inline embedding"
message["From"] = sender_email
message["To"] = receiver_email

# We assume that the image file is in the same directory that you run your Python script from
encoded = base64.b64encode(open("mailtrap.jpg", "rb").read()).decode()

html = f"""\
<html>
 <body>
   <img src="data:image/jpg;base64,{encoded}">
 </body>
</html>
"""

part = MIMEText(html, "html")
message.attach(part)

# send your email
with smtplib.SMTP("smtp.mailtrap.io", 2525) as server:
   server.login(login, password)
   server.sendmail(
       sender_email, receiver_email, message.as_string()
   )
print('Sent')

Kết quả:

Bây giờ hình ảnh được nhúng vào thông điệp HTML và không hiển thị dưới dạng tệp đính kèm. Python đã mã hóa hình ảnh jpg của mình và nếu chúng ta đi đến tab Nguồn HTML, chúng ta sẽ thấy chuỗi dữ liệu hình ảnh dài trong img src.

Cách gửi nhiều email

Gửi nhiều email đến những người nhận khác nhau và cá nhân hóa chúng là điều đặc biệt về email trong Python.

Để thêm nhiều người nhận, bạn chỉ cần nhập địa chỉ của họ bằng dấu phẩy, thêm CC và BCC. Nhưng nếu bạn làm việc với việc gửi email hàng loạt, Python sẽ giúp bạn tiết kiệm bằng các vòng lặp.

Một trong các tùy chọn là tạo cơ sở dữ liệu theo định dạng .csv (chúng tôi giả sử nó được lưu vào cùng thư mục với tập lệnh Python của bạn). Ví dụ:

#name,email
John Johnson,[email protected]
Peter Peterson,[email protected]

Đoạn mã dưới đây sẽ mở tệp và lặp qua từng hàng của nó, thay thế {name} bằng giá trị từ cột tên.

Đầu vào:

import csv, smtplib

port = 2525 
smtp_server = "smtp.mailtrap.io"
login = "1a2b3c4d5e6f7g" # paste your login generated by Mailtrap
password = "1a2b3c4d5e6f7g" # paste your password generated by Mailtrap

message = """Subject: Order confirmation
To: {recipient}
From: {sender}

Hi {name}, thanks for your order! We are processing it now and will contact you soon"""
sender = "[email protected]"

with smtplib.SMTP("smtp.mailtrap.io", 2525) as server:
    server.login(login, password)
    with open("contacts.csv") as file:
        reader = csv.reader(file)
        next(reader)  # it skips the header row
        for name, email in reader:
            server.sendmail(
               sender,
                email,
                message.format(name=name, recipient=email, sender=sender)
            )
            print(f'Sent to {name}')

Sau khi chạy tập lệnh, chúng ta sẽ nhận được phản hồi như sau:

>>>
Sent to John Johnson
Sent to Peter Peterson
>>>

Kết quả:


All Rights Reserved