python利用STMP發送gmail、QQ郵件錯誤及筆記總結

引言

本篇博文是一個我之前沒有想要去關注,但就是這樣看似輕鬆的事情,卻出現了很多的bug,我嘗試用了163,、QQ以及谷歌郵箱去發送郵件,中間遇到了很多波折,其中一根谷歌的小號郵箱也被封了,所以想在這裏總結下我的一些錯誤與筆記。

STMP協議介紹

對於Python來說,需要編寫腳本調用郵件服務器來發送郵件,使用的協議是SMTP。接收郵件,使用的協議是POP3和IMAP。這三種的區別與用處後面再提,其實在上述三種郵箱的使用中,差別並不是很大,比如說QQ郵箱的STMP / IMAP是共用一個授權碼,而谷歌郵箱則是按照端口劃分。

在python中,提供收發郵件的庫爲smtplib、poplib和imaplib,而我們常用的爲smtplib,下面爲stmplib的一些常規方法介紹:

方法 描述
SMTP.set_debuglevel(level) 設置輸出debug調試信息,默認不輸出
SMTP.docmd(cmd[, argstring]) 發送一個命令到SMTP服務器
SMTP.connect([host[, port]]) 連接到指定的SMTP服務器
SMTP.helo([hostname]) 使用helo指令向SMTP服務器確認你的身份
SMTP.ehlo(hostname) 使用ehlo指令像ESMTP(SMTP擴展)確認你的身份
SMTP.ehlo_or_helo_if_needed() 如果在以前的會話連接中沒有提供ehlo或者helo指令,這個方法會調用ehlo()或helo()
SMTP.has_extn(name) 判斷指定名稱是否在SMTP服務器上
SMTP.verify(address) 判斷郵件地址是否在SMTP服務器上
SMTP.starttls([keyfile[, certfile]]) 使SMTP連接運行在TLS模式,所有的SMTP指令都會被加密
SMTP.login(user, password) 登錄SMTP服務器
SMTP.sendmail(from_addr, to_addrs, msg, mail_options=[], rcpt_options=[]) 發送郵件
from_addr:郵件發件人
to_addrs:郵件收件人
msg:發送消息
SMTP.quit() 關閉SMTP會話
SMTP.close() 關閉SMTP服務器連接

以上表格轉自https://blog.51cto.com/lizhenliang/1875330,那麼我們就可以寫一個範例如下:

#!/usr/bin/python
# -*- coding: UTF-8 -*-
 
import smtplib
from email.mime.text import MIMEText
from email.utils import formataddr
 
my_sender='[email protected]'    # 發件人郵箱賬號
my_pass = 'xxxxxxxxxx'              # 發件人郵箱密碼
my_user='[email protected]'      # 收件人郵箱賬號,我這邊發送給自己
def mail():
    ret=True
    try:
        msg=MIMEText('successful!','plain','utf-8')
        msg['From']=formataddr(["FromRunoob",my_sender])  # 括號裏的對應發件人郵箱暱稱、發件人郵箱賬號
        msg['To']=formataddr(["FK",my_user])              # 括號裏的對應收件人郵箱暱稱、收件人郵箱賬號
        msg['Subject']="放假通知"                # 郵件的主題,也可以說是標題
 
        server=smtplib.SMTP_SSL("smtp.qq.com", 465)  # 發件人郵箱中的SMTP服務器,端口是25
        server.login(my_sender, my_pass)  # 括號中對應的是發件人郵箱賬號、郵箱密碼
        server.sendmail(my_sender,[my_user,],msg.as_string())  # 括號中對應的是發件人郵箱賬號、收件人郵箱賬號、發送郵件
        server.quit()  # 關閉連接
    except Exception:  # 如果 try 中的語句沒有執行,則會執行下面的 ret=False
        ret=False
    return ret
 
ret=mail()
if ret:
    print("郵件發送成功")
else:
    print("郵件發送失敗")

將其保存爲py文件,然後運行就能去郵箱內查看到當前郵件了,但自己發給自己並不能說明是遵循STMP協議才發送或收到,所以我們需要將其放入一個固定任務中,將收件人當成我們要傳的參數,然後檢測是否能收到郵件纔算是驗證成功,這裏的這種定時任務,可以簡單的使用django和celery達到想要的效果,並且django中也有提供內置的mail模塊,但沒有stmplib好用,感覺bug有點多省略很多東西,所以還是以stmplib來構造函數:

# settings.py文件中:
EMAIL_USE_TLS = False
EMAIL_USE_SSL = False

EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'smtp.163.com'
#MAIL_USE_DEBUG = True
#MAIL_USE_TLS = True

# EMAIL_HOST = 'smtp.maxhichina.com'
EMAIL_PORT = 25
# 發送郵件的郵箱
EMAIL_HOST_USER = '[email protected]'
# 在郵箱中設置的客戶端授權密碼
EMAIL_HOST_PASSWORD = 'xxxx'
# 收件人看到的發件人
# EMAIL_FROM = '[email protected]'
EMAIL_FROM = 'xxx<[email protected]>'

然後創建一個celery的模塊包,裏面的task.py文件寫下發郵件的腳本:

#from django.core.mail import send_mail
from celery.main import app
from django.conf import settings
import random

import smtplib
from email.mime.text import MIMEText
from email.header import Header


@app.task(name='send_verify_mail', bing=True)
def send_verify_mail(to_email, code):
    sender = settings.EMAIL_HOST_USER

    receiver = [to_email]
    subject = '放假通知'
    username = settings.EMAIL_HOST_USER
    password = settings.EMAIL_HOST_PASSWORD
    msg = MIMEText('hello code %s' % code,
                   _charset='utf-8')  #中文需參數‘utf-8',單字節字符不需要
    msg['Subject'] = Header(subject, 'utf-8')
    msg['From'] = settings.EMAIL_FROM
    msg['To'] = to_email
    smtp = smtplib.SMTP_SSL()
    smtp.connect(settings.EMAIL_HOST)
    smtp.login(username, password)
    smtp.sendmail(sender, receiver, msg.as_string())
    smtp.quit()

然後後面如果要發送郵件,只要調用這個函數,並啓動celery就行了,但這裏就出現了非常多的坑是各種郵箱完全不相同的驗證,另外還有相關的HOST和PORT,這裏將關於POP3/STMP/IMAP三種對應端口的情況列成一張表格爲:

郵箱類型 服務器名稱 服務器地址 SSL協議端端口 端口
QQ郵箱 POP3 pop.qq.com 110
SMTP smtp.qq.com 465 25
IMAP imap.qq.com 993
163郵箱 POP3 pop.163.com 110
SMTP smtp.163.com 465 25
IMAP imap.163.com 993
gmail郵箱 POP3 pop.gmail.com 995
SMTP smtp.gmail.com 587 / 465 25(我試驗是可以)
IMAP imap.gmail.com 993
yahoo郵箱 POP3 pop.mail.yahoo.com 110
SMTP smtp.mail.yahoo.com 25
IMAP imap.mail.yahoo.com 993

表中列舉到的,除了谷歌郵箱,其它都是用的國內的郵箱賬號,我們主要是看STMP協議這一項,如果不做設置,可能會出現如下的情況:
在這裏插入圖片描述
這裏就需要我們去開啓自己郵箱裏的相關設置,這裏我詳細說的是QQ和谷歌,因爲這兩個也是我試驗得最久的兩個了。

QQ郵箱設置

  • 進入郵箱,點擊設置-賬戶
    在這裏插入圖片描述
  • 在賬戶欄目下,往下拉,找到POP3/IMAP/SMTP/Exchange/CardDAV/CalDAV服務所在位置
    在這裏插入圖片描述
    然後將上述兩個服務開啓,一般會提示要驗證綁定郵箱的一些東西,比如說向某個號碼發短信,或者是密保令牌獲取驗證碼,完成這其中的一步操作後才能看到QQ郵箱爲每個賬號設置的QQ授權碼,然後如果是作爲發信息,那麼有用的爲第二個,那麼代碼中可以修改爲:
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'smtp.qq.com'
MAIL_USE_DEBUG = True
MAIL_USE_TLS = True

# EMAIL_HOST = 'smtp.maxhichina.com'
EMAIL_PORT = 465
# 發送郵件的郵箱
EMAIL_HOST_USER = '[email protected]'
# 在郵箱中設置的客戶端授權密碼
EMAIL_HOST_PASSWORD = 'xxx'
# 收件人看到的發件人
EMAIL_FROM = 'Tim<[email protected]>'

一般如果照上面這麼寫基本上是沒啥問題的,可能Email_port改成25也能發送郵件,那表示是未加密的網關,可能就會自動判別爲垃圾郵件了,如果是465的話更保險些。QQ郵箱還是沒有啥太大的坑,最多全部都是退信,但Gmail的套路就可能是一環接一環了。

谷歌郵箱設置

上述國內郵箱的設置還能用Foxmail軟件直接設置,QQ郵箱中就有一個很詳細的說明:Foxmail常用郵件客戶端軟件設置

gmail郵箱是沒有授權碼的,應該說國外的郵箱好像很少要授權碼,都是直接拿密碼當成授權碼用,但這個裏面的坑就多了。

其實我寫這篇文章的原因也是我在gmail郵箱上試了很長一段時間,原因是這個坑有點深,也可能我的搜索方式不對。當我直接用密碼房授權碼時,發送郵件的時候會報錯爲:
在這裏插入圖片描述
b"5.7.8 username or password not accept,那麼也能說明這個密碼是無效的。在此之前,還遇到的bug是newwork is unreachable,即是網絡達不到要求,那個是因爲之前用的阿里雲國內服務器,然後用的25網關,但找了半天,發現好像不適用,阿里雲的STMP服務地址爲:
在這裏插入圖片描述
所以在這裏提一下,因爲當時我是拿我自己服務器測試,發現谷歌連接不上了。但項目是在國外服務器上,所以我直接在國外服務器上把郵箱替換成谷歌郵箱,然後回到這個錯誤:b"5.7.8 username or password not accept

然後翻遍了Stack Overflow還有一些國外怎麼設置谷歌郵箱的帖子,發現了三步:

將【安全性較低的應用程式存儲權限】設爲【啓用】

我們可以進入https://myaccount.google.com/lesssecureapps界面將設置開起來,最好是用gmail瀏覽器已經進行過登錄,那麼就能跳過一些繁瑣的登錄。
在這裏插入圖片描述

開啓後,重新以STMP協議發送郵件,然後這個時候可能就會報錯爲:b"5.7.7,圖沒有截了,大致意思應該說是驗證密碼錯誤,也就是說開了這個後不能登錄上去,如果沒用,那麼就下一步。

解除人機驗證鎖定

進入https://accounts.google.com/b/0/DisplayUnlockCaptcha 頁面中,Google 可能會在您透過新的裝置或應用程式登入時,要求您完成這項額外步驟。如要授權存取,請按下方的 [繼續] 按鈕。然後我們就進入到了授權碼的選擇界面,

在這裏插入圖片描述

一路選是就OK了,之後可以再次嘗試,如果還是不成功,這裏報啥錯忘了,如果還是不成功,那麼就最後一步。

開啓兩步驗證

第三步也是最後一步,如果還是不行,那隻能採用這種冒險的方式,爲啥說冒險呢,如果真的要做這步,就很有可能被封號幾天,我當時是啥也不懂,看到有人這樣搞,然後我也去搞了一波,但結果就不太好了。

這一步需要我們進入網址:https://myaccount.google.com/signinoptions/two-step-verification/enroll-welcome?utm_source=google-account&utm_medium=web,然後手動去獲取一個授權碼,並將密碼設置爲授權碼的方式。
在這裏插入圖片描述

中間過程我就不再說了,當時配置的時候沒有截圖,這裏默認有五種可以選,有windows、mac、Android、iPhone還有其它,這裏選擇其它直接確定,就能拿到授權碼,然後把密碼改完後感覺挺好的,但第二天就被封號了,可能我一次申請的有點多,也可能是其它各種原因,所以奉勸看到這裏的朋友,別輕易嘗試這種,如果上述兩步沒用,那麼就可以考慮換郵箱了,下面是我的慘痛教訓:
在這裏插入圖片描述
然後是申請複查,天地良心,我還沒開始搞啥破壞呢。。。
在這裏插入圖片描述

中間還有 b"5.7.14b"5.7.4b"5.7.7,我記得其中分別是認證密碼錯誤,stmp連接錯誤等,如果走完上面兩步還出現這類問題,第一是看拼寫有沒有問題,如果和我上面寫的QQ格式基本一致,端口是587或者465沒用,那可以嘗試重新走一遍上述步驟,可能中間哪裏沒有開起來,或者按第三步來,但我實驗是被封了兩天。

總結

後來還嘗試了雅虎還有網易企業郵箱,我都不知道我爲什麼要試這麼多種郵箱,因爲最近一個項目是海外項目,老闆搞了各種郵箱,但一直想換,那沒辦法咯,我就只能陪着他換。企業郵箱的話就是要做域名解析,如果是阿里雲服務器的話,這裏就不再說了,直接後臺改就完事了。感覺這篇文章算一個科普性質的,科普我從錯誤裏的經驗,還有就是郵箱咋設置,我感覺現在啥郵箱都能搞一搞了。。。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章