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没用,那可以尝试重新走一遍上述步骤,可能中间哪里没有开起来,或者按第三步来,但我实验是被封了两天。

总结

后来还尝试了雅虎还有网易企业邮箱,我都不知道我为什么要试这么多种邮箱,因为最近一个项目是海外项目,老板搞了各种邮箱,但一直想换,那没办法咯,我就只能陪着他换。企业邮箱的话就是要做域名解析,如果是阿里云服务器的话,这里就不再说了,直接后台改就完事了。感觉这篇文章算一个科普性质的,科普我从错误里的经验,还有就是邮箱咋设置,我感觉现在啥邮箱都能搞一搞了。。。

猜你喜欢

转载自blog.csdn.net/submarineas/article/details/102711722
今日推荐