Hiện nay có rất nhiều cách khác nhau để gửi email với python, có thể là thông qua các thư viện có sẵn như boto và SES, hoặc thông qua giao thức SMTP. Tuy mặc dù bạn có thể sử dụng những thư viện này để gửi mail một cách êm trôi, nhưng sẽ có thể đến lúc có nhiều vấn đề khác nảy sinh mà bạn chưa control được. Mình nghĩ bài viết hướng dẫn gửi mail thông qua gmail bằng python này sẽ rất hữu ích cho bạn để thực hiện các công việc có liên quan đến gmail thông dụng.
Giao thức SMTP là gì?
Python có sẵn một thư viện cho phép bạn kết nối với máy chủ SMTP – như smtp server mà gmail đang sử dụng. Thư viện này được gọi là smtplib, được tích hợp sẵn trong python.
SMTP (Simple Mail Transfer Protocol) là một giao thức ở lớp ứng dụng (trên TCP) được sử dụng để giao tiếp với mail servers từ các dịch vụ bên ngoài, như ứng dụng mail client trên điện thoại của bạn chẳng hạn. SMTP chỉ là một giao thức vận chuyển, như vậy bạn sẽ không thể nhận được email từ nó – nghĩa là bạn chỉ có thể gửi mail thông qua SMTP được thôi và cũng là câu chuyện mà mình sẽ tập trung trong bài viết này. Nếu bạn muốn nhận hay truy xuất thông tin email thì bạn sẽ cần phải đụng đến giao thức
IMAP (Internet Message Access Protocol).
Lưu ý rằng hiện nay có rất nhiều dịch vụ email như gmail, outlook thường không sử dụng SMTP trên máy chủ của họ. SMTP thường chỉ cung cấp một interface hướng ra bên ngoài cho dịch vụ của họ như smtp.gmail.com server. Chủ yếu được sử dụng bởi email client trên điện thoại hay máy tính của bạn (như Outlook, thunderbird,…)
Xem thêm: Hướng dẫn cài đặt python 3.9 trên ubuntu
Tạo kết nối SMTP không an toàn
Như mình đã đề cập, Python tích hợp sẵn thư viện smtplib sẵn rồi – nên nó có thể thực hiện xử lý được rất nhiều phần khác nhau của giao thức như: xác thực, tạo kết nối, xác minh và gửi email.
Khi sử dụng thư viện này, có một số cách khác nhau để bạn có thể tạo một kết nối tới mail server. Trong phần này, chúng ta sẽ chỉ tập trung vào việc tạo một kết nối đơn giản, kết nối không an toàn (kết nối không được mã hóa) mặc định sẽ sử dụng port 25. Tuy nhiên, giao thức gửi email thực sự sử dụng port 587 – là những gì mà mình sẽ sử dụng.
Những kết nối này thực sự rất đơn giản để tạo với smtplib. Phiên bản kết nối SMTP không được mã hóa có thể tạo được bằng dòng code sau đây:
import smtplib
try:
server = smtplib.SMTP('smtp.gmail.com', 587)
server.ehlo()
except:
print ('Something went wrong...')
Đoạn code trên thực tế chỉ thực hiện việc tạo kết nối và gọi hàm ehlo(). Từ đây bạn đã có thể gửi email tới gmail server thông qua kết nối không an toàn được rồi đấy.
Lưu ý: Gmail áp dụng một số hạn chế đối với các kết nối SMTP như thế này. Xem phần “Xác thực với gmail” ở phía bên dưới để hiểu rõ hơn.
Tạo kết nối SMTP an toàn
Khi một kết nối SMTP an toàn được bảo vệ thông qua SSL/TLS, nó sẽ thực hiện việc kết nối thông qua port 465 và thường được gọi là SMTPS. Không cần phải nói thì bạn sẽ biết, hiện tại hầu hết tất cả các website trên internet đều sử dụng kết nối an toàn (https khi truy cập website và google cũng khuyến cáo nên sử dụng SSL để tối ưu SEO cho website) và bạn cũng nên luôn sử dụng kết nối an toàn như vậy.
Xem thêm: SSL là gì
Có một số cách nhau mà bạn có thể dùng để sử dụng một kênh kết nối SMTP an toàn trong thư viện smtplib. Cách đầu tiên là tạo một kết nối không an toàn và sau đó là nâng cấp lên TLS bằng cách sử dụng method startls().
import smtplib
try:
server = smtplib.SMTP('smtp.gmail.com', 587)
server.ehlo()
server.starttls()
# ...send emails
except:
print 'Something went wrong...'
Lưu ý là mặc dù đoạn code trên trông có vẻ giống với đoạn code tạo kết nối không an toàn, nhưng trong này mình có sử dụng hàm starttls() để nâng cấp lên thành kết nối an toàn.
Cách khác là bạn có thể tạo một kết nối an toàn ngay từ đầu. Trong trường hợp này, bạn sẽ sử dụng method SMTP_SSL() ngay từ đầu.
import smtplib
try:
server_ssl = smtplib.SMTP_SSL('smtp.gmail.com', 465)
server_ssl.ehlo() # optional
# ...send emails
except:
print ('Something went wrong...')
Đoạn code trên chỉ có một chút khác biệt so với các đoạn code ở trên nữa ở chỗ, đoạn code này sử dụng SMTPS có port mặc định là 465, và bạn có thể bỏ trống tham số này trong đoạn code.
Như vậy, mình đã có thể tạo được một kênh kết nối SMTP an toàn rồi, tiếp theo sẽ tiến hành tạo một email.
Tạo email
Về cơ bản, Email chỉ là một chuỗi văn bản được nối lại với nhau bởi ký tự newline. Hầu hết email sẽ có ít nhất các fields sau đây: FROM, TO, Subject và các fields body. Ví dụ:
From: you@gmail.com
To: me@gmail.com, vsudo@gmail.com
Subject: OMG Super Important Message
Hey, what's up?
- WTF
Như bạn có thể thấy bên trên, mỗi dòng chứa một field mới với data của nó. Nó không phải là binary, json, hay XML, đơn giả nó chỉ là một chuỗi chứa các dòng được phân cách.
Một ví dụ đơn giản trong python khi áp dụng các fields này để gửi email
sent_from = 'you@gmail.com'
to = ['me@gmail.com', 'vsudo@gmail.com']
subject = 'OMG Super Important Message'
body = 'Hey, what's up?\n\n- You'
email_text = """\
From: %s
To: %s
Subject: %s
%s
""" % (sent_from, ", ".join(to), subject, body)
Ở đoạn code trên, mình chỉ truyền giá trị để tạo ra chuỗi email_text rồi chuyển cho smtplib, tiếp theo mình sẽ giải thích rõ hơn.
Authentication với Gmail
Có một số bước mà bạn cần phải làm trước khi bạn có thể gửi mail thông qua Gmail với SMTP. Nếu bạn đang sử dụng Gmail làm nhà cung cấp dịch vụ email chính, bạn sẽ cần phải yêu cầu Google cho phép bạn kết nối qua SMTP – được coi là phương pháp “kém an toàn“.
Bạn không thể nào đổ lỗi cho Google khi bạn gửi email theo cách này vì ứng dụng của bạn sẽ cần password ở dạng plain-text để hoạt động – nghĩa là không phải một ý tưởng hay. Nó không giống như giao thức OAuth nơi token có thể được thu hồi, như vậy họ phải tìm cách khác để đảm bảo không có third-party nào có thể xâm nhập trái phép vào dữ liệu của bạn.
Đối với nhiều nhà cung cấp dịch vụ email trên thị trường hiện nay, bạn sẽ không cần phải thực hiện bất kỳ thủ tục nào như mình đã đề cập bên trên.
Đầu tiên, bạn sẽ cần phải cho phép các ứng dụng ít an toàn truy cập vào tài khoản của bạn.
Xem chi tiết: https://support.google.com/accounts/answer/6010255
Nếu bạn đã bật xác minh 2 bước trên tài khoản của mình, bạn sẽ cần phải tạo mật khẩu dành riêng cho các ứng dụng kém an toàn như này. Trong trường hợp này, bạn cần phải làm theo hướng dẫn bằng đường link bên dưới:
Xem chi tiết: https://support.google.com/accounts/answer/185833
Và cuối cùng, nếu bạn vẫn nhận được exception SMTPAuthenticationError với mã lỗi 534, bạn sẽ cần phải thực hiện thêm một số bước nữa để đoạn code hoạt động trơn tru.
Để unlock Captcha: https://accounts.google.com/DisplayUnlockCaptcha
Cuối cùng, đoạn code hoàn chỉnh để đăng nhập Gmail như sau:
import smtplib
gmail_user = 'you@gmail.com'
gmail_password = 'P@ssword!'
try:
server = smtplib.SMTP_SSL('smtp.gmail.com', 465)
server.ehlo()
server.login(gmail_user, gmail_password)
except:
print 'Something went wrong...'
Tiến hành gửi email
Như vậy, bạn đã thiết lập kết nối SMTP và ủy quyền truy cập ứng dụng của mình với Google thành công rồi, cuối cùng là tiến hành gửi mail qua Gmail bằng Python.
Sử dụng chuỗi email_text như ở trên và object server đã được kết nối/xác thực và gọi hàm .sendmail() để gửi mail. Dưới đây là full code không che hoàn chỉnh.
import smtplib
gmail_user = 'you@gmail.com'
gmail_password = 'P@ssword!'
sent_from = gmail_user
to = ['me@gmail.com', 'bill@gmail.com']
subject = 'OMG Super Important Message'
body = 'Hey, what's up?\n\n- You'
email_text = """\
From: %s
To: %s
Subject: %s
%s
""" % (sent_from, ", ".join(to), subject, body)
try:
server = smtplib.SMTP_SSL('smtp.gmail.com', 465)
server.ehlo()
server.login(gmail_user, gmail_password)
server.sendmail(sent_from, to, email_text)
server.close()
print ('Email sent!')
except:
print ('Something went wrong...')
Kết luận
Ngoài các bước ủy quyền truy cập dành riêng cho Gmail (liên quan đến các ứng dụng kém an toàn), đoạn code trên cũng sẽ hoạt động đối với bất kỳ nhà cung cấp dịch vụ email nào khác có hỗ trợ truy cập SMTP, kể cả khi bạn tự dựng mail server và mở port SMTP là được.
Lưu ý khi sử dụng gmail: Gmail có áp dụng một số quy tắc hạn chế đối với những người gửi email bằng SMTP gmail. Tài khoản miễn phí sẽ chỉ có thể gửi được giới hạn 500 email mỗi ngày và bị giới hạn tốc độ vào khoảng 20 email mỗi giây thôi.