章节十八:收发邮件
从刚开始不知道Python是什么,到知识入门、做过了三个小项目、建立了更宏观的编程思维,再到可以自建模块,你真的走过了一条不断攀升、不断克服困难的旅程。
旅程还在继续,这一关,我们就要来到一个全新的实操项目,而且与以往的项目不同,这个是实实在在的与日常职场相关的编程项目。
项目实操流程相信你已经了然于心:
第一步,我们就来看看在这个项目中,我们应该树立怎样的目标。
1. 明确项目目标
这一关的主题,其实最早是来源于往期学员的一个问题:学员小贾是一名外贸人员,每到了节假日,要给客户发祝福邮件。
虽然现在群发邮件比较方便,但还是要每次手动添加收件人的邮箱。小贾想:要是能够自动发送邮件给这些人就好了。他找到了助教求助。
之前跟你聊过,希望学习Python知识,是能够帮助大家解决实际问题的。而小贾的问题,就是一个可以完美地被Python解决的需求,而且在职场中也是一个普遍存在的需求。
我们就将以这个需求为例,来学习邮件模块,所以我们的第1个项目目标是:用Python群发邮件。
除了要学习这个模块相关的知识之外,我认为更重要的仍是掌握【学习模块的方式】,也可以理解为掌握【学习新知识的方式】。
这也是我们的另一个项目目标:学会自学新模块。
毕竟编程是一个开源的世界,每时每刻都有新的知识产生,要想进步,就要学会学习。
而且我也没有办法每个知识点都教给大家,为了解决这种无力感,能想到的办法就是:提高大家学习知识的能力。
所以在学习模块的时候,希望大家能把注意力分一点到另一条隐形的主线【学习方法】上。
好,明确了项目目标之后,我们就来进行第二步:
2. 分析过程,拆解项目
由于这是第一次实操新的模块,就由我先给出版本拆解过程:
版本1.0,主要是根据需求,自己寻找和学习相关的模块,然后给自己发一封最简单的邮件。
版本2.0呢,还是给自己发邮件,但邮件应该更完整,包括邮件头(就是发件人、邮件标题等),和正文内容。
版本3.0呢,主要是从单一收件人,变成多收件人,也就是群发一封完整的邮件。
三个版本的难度稍有递进,接下来就是闯关时间了。
3. 逐步执行,代码实现
首先,我们来看看版本1.0。
3.1 版本1.0:学习模块,发一封简单邮件
在编程世界中,我们不需要什么知识都一把抓,而是遇到问题之后,产生了某种需求,才会去找对应的解决方案。
这个方案可能是某个模块,也可能是某个函数~
就像在第一个PK小游戏项目中,我们想:要是能够随机生成某个区间内的数字,替代固定的血量值,可能会好一点。于是我们就学习并引入了random模块。
这一关的课程,也是同样的道理。
其实,只要搜索关键词“发送邮件 python”,就能找到解决方案。
下面,请你务必打开一个浏览器的标签页,跟着我去做。
搜索后你看到这样的页面,不用逐一点进去看就能知道:1.Python可以解决这个问题;2.方法是smtplib
,email
这两个模块。
而且还会知道:smtplib是用来发送邮件用的,email是用来构建邮件内容的。这两个都是Python内置模块。
为了方便自己理解,我们可以尝试把它画下来:
负责发送邮件的smtplib模块,和负责构造邮件内容的email模块,就是我们今天要学习的新模块~
总结一下:到底学习什么模块,关键在于你的需求,这样我们才能从需求出发找到对应的解决方案,解决自己的问题。
另外,我们还要学会关键词搜索的方式:比如“自动发邮件”,不过如果只是输入这个关键词,电脑会反馈大量无用的信息。
一般,提供的关键字越多,搜索引擎返回的结果越精确。我们可以使用+
号或者空格
连接关键词,也可使用之前学习的and
和or
来连接,其含义和之前学习的一样,分别表示“并且”和“或者”。
找到解决方案并大致了解模块之后,重头戏来了——学习这两个模块:
昨天我们已经提供了一些学习的渠道,我们以smtplib模块
为例来实操一下:
对于smtplib模块
,我们想要去查看它的官方文档,也只需要在浏览器里搜索关键词“smtplib python”就好。
你能看到,第1个搜索结果是Python 3.7.2版本的smtplib模块介绍。打开这个网址,你看到满屏的英文:
如果你英文够好,那就可以去阅读;如果你英文不太好,也可以使用谷歌浏览器自带的“谷歌翻译”功能,将页面翻译成中文再阅读。
这个文档提供的内容有:需要向smtplib模块输入什么;smtplib模块能做什么;smtplib模块返回的是什么;常见的报错;SMTP对象有哪些方法及如何使用;一个应用实例。
如果是这么讲,你会发现逻辑还是蛮清晰的。但它依然对新手蛮不友好,读起来比较痛苦。
如果你能够接受这种难度的文档,那么照着这个去写代码是最好不过的,毕竟它是官方文档。但如果你不能接受呢,还有一些方案。
重新搜索一次,关键词换成 “smtplib 教程” ,你就能看到好多好多中国人编写的内容。在可读性上,是要比官方文档好一些的,但缺点在于良莠不齐。你可以自行挑选适合自己的去阅读。
上一关我们总结了模块三问:函数;属性或方法;格式。今天学习的两个模块比较简单,我们就不一一回答了,我们直接带着两个问题去学。
这两个问题就是:1.这两个模块分别有什么方法,2.模块的方法怎么用。
关于第1个问题,需要你现在点开搜索到的内容看看,你可以看别人的简介;也可以直接看代码,多对比不同人写的代码,那些重复出现的代码可能就是我们要找的方法。
给大家一分钟时间!
欢迎回来,如果你真的点了几个链接看了,你就能看到类似这样的内容:
总结一下这些内容,如果我们要发送邮件,就需要用到smtplib模块
的以下方法:
import smtplib
server = smtplib.SMTP()
server.connect(host, port)
server.login(username, password)
server.sendmail(sender, to_addr, msg.as_string())
server.quit()
第一行,我们懂,是引入smtplib模块
。
第三行,server
是一个变量,smtplib.SMTP()
是变量server的值。我们已经知道了smtplib
是模块的名称,那SMTP
是什么意思呢?
请你先在自己电脑的VS Code上新建一个.py文件。注意:这个.py文件不能命名为email.py
,而且你存放这个py文件的文件夹里,也不能有email.py
同名文件。这是由于我们后面要调用email模块,如果将文件也命名为email,会造成报错。
好啦,现在请你把上面的代码复制进去。按住Ctrl键并点击SMTP
,会看到对SMTP
的解释:
可以看到,SMTP 是一个类(class),再往下面滑可以看到其中包含了很多函数,第二到第五行的函数就是 SMTP 类下的方法。
要想调用 smtplib 模块下、SMTP 类下的方法,应该这样写:smtplib.SMTP.方法
。
我们看到的这段代码,则是为了减少代码冗余,已经将需要重复出现的smtplib.SMTP
赋值给了变量,可见,在编程的世界有多讨厌重复了。所以还是采用这种写法吧:
import smtplib
server = smtplib.SMTP()
server.connect(host, port)
server.login(username, password)
server.sendmail(from_addr, to_addr, msg.as_string())
server.quit()
这样我们就能够调用需要的函数了,那这些函数有什么含义,要怎么用呢?继续探索。
其实我们只要知道SMTP是什么就能够明白代码的含义了,现在赶紧去搜索一下 “SMTP” 吧。
对,SMTP (Simple Mail Transfer Protocol)翻译过来是“简单邮件传输协议”的意思,SMTP 协议是由源服务器到目的地服务器传送邮件的一组规则。
可以简单理解为:我们需要通过SMTP指定一个服务器,这样才能把邮件送到另一个服务器。
import smtplib
server = smtplib.SMTP()
server.connect(host, port)
第四行代码,就是干这个工作的,连接(connect)指定的服务器。
host是指定连接的邮箱服务器,你可以指定服务器的域名。通过搜索“xx邮箱服务器地址”,就可以找到。
port 是“端口”的意思。端口属于计算机网络知识里的内容,你可以自行搜索了解,现在我们只要知道它是一个【整数】即可。
我们需要指定SMTP服务使用的端口号,一般情况下SMTP默认端口号为25。
如果25行不通,你可以通过搜索或者去邮箱设置里面查看端口。比如,如果我打算用自己的企业邮箱来发邮件,登录邮箱后,在【设置-选项-POP和IMAP】里面可以看到这些信息:
包括服务器名称,端口和加密方式。服务器名称是mail.forchange.tech
,端口是587
。你可以登录自己的邮箱,查看这些信息,接下来跟着我一起写代码。
现在参数确定了,代码写出来应该长这样:
import smtplib
server = smtp.SMTP()
server.connect('mail.forchange.tech',587)
因为大家一般都有QQ邮箱,所以接下来的实操,我会以QQ邮箱为例来展示操作。我们可以搜索“QQ邮箱 smtp设置”得到host
和port
这两个参数。
两个参数都有了。SMTP服务器地址是:smtp.qq.com
,端口是465
或587
,那就让我们去写代码吧!
这里有两种写法,一是使用默认端口:25。
import smtplib
server = smtplib.SMTP()
server.connect('smtp.qq.com', 25)
二是,尝试搜索到的端口,比如465。这时会有些不同,QQ 邮箱采用的加密方式是SSL,我们需要写成这样:
import smtplib
server = smtplib.SMTP_SSL()
#如果端口是用SSL加密,请这样写代码。其中server是变量名
server.connect('smtp.qq.com', 465)
#如果出现编码错误UnicodeDecodeError,你可以这样写:server.connect('smtp.qq.com', 465,'utf-8')
后面,我们采用的是第二种写法。(大家可以先尝试第一种默认端口,如果不行则需要查找服务器对应的端口。)
提醒!QQ 邮箱一般默认关闭SMTP
服务,我们得先去开启它。请打开https://mail.qq.com/,登录你的邮箱。然后点击位于顶部的【设置】按钮,选择【账户设置】,然后下拉到这个位置。
就像上面的一样,把第一项服务打开。需要用密保手机发送短信,完成之后,QQ 邮箱会提供给你一个授权码,授权码的意思是,你可以不用QQ的网页邮箱或者邮箱客户端来登录,而是用邮箱账号+授权码获取邮箱服务器的内容。
如果你打算用QQ邮箱自动发邮件,请保存好这个授权码。在你使用SMTP服务登录邮箱时,要输入这个授权码作为密码登录,而【不是】你的邮箱登录密码。
若在设置和开启SMTP服务的过程中有遇到不可逾越的障碍……比如记不清密保手机,那么欢迎你……换个邮箱试试看。只是要重新查找服务器地址,端口和加密方式。这个过程中,保持耐心~
现在,我们假设你已经排除万难,那就跟着我继续往下走。
理解了SMTP的含义,下面几行代码的作用也就好理解了:
import smtplib
server = smtplib.SMTP()
server.connect(host, port)
server.login(username, password)
server.sendmail(from_addr, to_addr, msg.as_string())
server.quit()
第五行代码,login是登录的意思,也就是登录你指定的服务器用的,需要输入两个参数:登录邮箱和授权码。
server.login(username, password)
#username:登录邮箱的用户名
#password:授权码
把两个参数放在最前面。这部分的代码我们也可以写出来了:
username = 'xxx@qq.com'
password = '你的授权码数字'
server.login(username, password)
#username:登录邮箱的用户名
#password:登录密码/授权码
另外,为了运行代码的方便,我们暂时就大大咧咧地将授权码放在代码里,不过为了安全起见,更好的做法还是利用input()
函数来输入,而不是直接展示哟。
继续看原来的代码:
import smtplib
server = smtplib.SMTP()
server.connect(host, port)
server.login(username, password)
server.sendmail(from_addr, to_addr, msg.as_string())
server.quit()
第六行代码sendmail
是“发送邮件”的意思,是发送邮件用的,sendmail()
方法需要三个参数:发件人,收件人和邮件内容。
这里的发件人from_addr
与上面的username
是一样的,都是你的登录邮箱,所以只用设置一次。
server.sendmail(from_addr, to_addr, msg.as_string())
#from_addr:邮件发送地址,就是上面的username
#to_addr:邮件收件人地址
#msg.as_string():为一个字符串类型
msg.as_string()
是一个字符串类型:as_string()
是将发送的信息msg
变为字符串类型。
同样,我们也可以写出这个代码了:
from_addr = 'xxx@qq.com'
to_addr = 'xxx@qq.com'
server.sendmail(from_addr, to_addr, msg.as_string())
#from_addr:邮件发送者地址。
#to_addr:邮件收件人地址。
#msg.as_string():为一个字符串类型
这样写还不完整,因为电脑不知道你要发送的信息msg
是什么,我们也没有定义。这就需要用到email 模块
,后面会解决的。
最后一行代码,quit
是“退出”的意思,就是退出服务器。
server.quit()
#退出服务器,结束SMTP会话
现在,我们就学习完smtplib模块
的方法了,也能够知道这个模块的具体功能,也就是这些方法怎么用。
感觉怎么样,如果给你一个新模块,能按照这样的方式去学个八九不离十吗?我们待会可以试试。
现在我们可以把上面写好的代码放在一起,以QQ邮箱为例:
# smtplib 用于邮件的发信动作
import smtplib
# 发信方的信息:发信邮箱,QQ邮箱授权码
from_addr = 'xxx@qq.com'
password = '你的授权码数字'
# 收信方邮箱
to_addr = 'xxx@qq.com'
# 发信服务器
smtp_server = 'smtp.qq.com'
# 开启发信服务,这里使用的是加密传输
server = smtplib.SMTP_SSL()
server.connect(smtp_server,465)
# 登录发信邮箱
server.login(from_addr, password)
# 发送邮件
server.sendmail(from_addr, to_addr, msg.as_string())
# 关闭服务器
server.quit()
请你在本地编辑器上写一遍,并把相关信息填写好。
现在我们已经搞定了一半,接下来我们就会开始构建邮件的正文内容,让这个程序能跑起来。
当然,我们就要用到构建邮件内容的email 模块
。学习email 模块的过程我就不再展示了,请你自己动手。在这之前,有必要先回顾一下之前学习的渠道,以及学习的方式:带着目的学习。
那我们就直接进行实操阶段啦。email 模块
:也就是用来写邮件内容的模块。这个内容可以是纯文本、HTML内容、图片、附件等多种形式。
每种形式对应的导入方式是这样的:
from email.mime.text import MIMEText
from email.mime.image import MIMEImage
from email.mime.multipart import MIMEMultipart
from … import …
语句上一关我们已经学习过了。与直接导入整个 smtplib 模块(import smtplib
)不同,这里我们只是从模块中导入一个或几个函数的做法。
同样,你可以复制上面的内容到本地的编辑器上,按住 ctrl 点击查看含义。
请你按住ctrl同时点击mime
,你会看到一个名为init.py
的空文件,这说明 email是其实是一个“包”。当然,并不是“包治百病”的那个“包”。
这就要谈到“模块”和“包”的区别了,模块(module)一般是一个文件,而包(package)是一个目录,一个包中可以包含很多个模块,可以说包是“模块打包”组成的。
但为什么看到那个空文件,就能知道email是包呢?这是因为Python中的包都必须默认包含一个init.py
的文件。
init.py
控制着包的导入行为。假如这个文件为空,那么我们仅仅导入包的话,就什么都做不了。所以直接import email
是行不通的。
所以,我们就需要使用from … import …
语句,从email包
目录下的【某个文件】引入【需要的对象】。比如从email包
下的text文件中引入MIMEText
方法。中文说起来有点复杂,看代码就懂了:
from email.mime.text import MIMEText
# 引入email包中构建文本内容的方法
下面,我们先从【构建纯文本的邮件内容】开始,也就是我们版本1.0的功能:发一封最简单的一句话文本邮件。
通过简单的学习,我们就能发现MIMEText()
方法需要输入三个参数:文本内容,文本类型和文本编码。
MIMEText(msg,type,chartset)
# msg:文本内容,可自定义
# type:文本类型,默认为plain(纯文本)
# chartset:文本编码,中文为“utf-8”
文本类型和文本编码,我们默认用'plain
'和'utf-8
'。文本内容,我就写一句最简单的“send by python”吧,你可以写自己想写的话啦。
现在,我们可以写代码了:
from email.mime.text import MIMEText
msg = MIMEText('send by python','plain','utf-8')
是不是还挺轻松的。好,现在终于可以把用email构建的正文内容,用smtplib模块发送啦。
我又要提醒你,在开始码这段代码之前,最好再梳理梳理代码结构。这是我建议的结构:
也就是这样:
我们在看别人代码的时候,也可以寻找结构清晰的代码作为参考,这样我们找到有用知识的概率也会提高。
按照这个结构,代码就能够码出来了。这是我的版本,你可以同样在自己的编辑器上写一写。记得准备好 QQ 邮箱和授权码(如果不是QQ邮箱,记得替换端口port参数),收件邮箱就写你自己的邮箱:
# smtplib 用于邮件的发信动作
import smtplib
from email.mime.text import MIMEText
# email 用于构建邮件内容
# 发信方的信息:发信邮箱,QQ 邮箱授权码
from_addr = 'xxx@qq.com'
password = '你的授权码数字'
# 收信方邮箱
to_addr = 'xxx@qq.com'
# 发信服务器
smtp_server = 'smtp.qq.com'
# 邮箱正文内容,第一个参数为内容,第二个参数为格式(plain 为纯文本),第三个参数为编码
msg = MIMEText('send by python','plain','utf-8')
# 开启发信服务,这里使用的是加密传输
server = smtplib.SMTP_SSL()
server.connect(smtp_server,465)
# 登录发信邮箱
server.login(from_addr, password)
# 发送邮件
server.sendmail(from_addr, to_addr, msg.as_string())
# 关闭服务器
server.quit()
你收到邮件了吗?希望这个过程还顺利~
如果发生报错的话,也没关系。这里我收集了几种典型报错信息:
例如这个报错信息:ValueError: server_hostname cannot be an empty string or start with a leading dot.
看到报错,不要慌,小场面。在学习 Python 的过程当中,我们难免会遇到这种报错,这并非坏事,有报错才有成长。当出现报错的时候,我们去阅读报错,读不懂就去搜索。
直接在网络搜索这个报错信息,我们会得到结论:如果你的Python版本是3.7,很可能发生这种报错。因为Python 3.7修改了ssl.py,导致smtplib.SMTP_SSL也连带产生了问题。
而解决方法也很简单:
# 改动这句代码
server = smtplib.SMTP_SSL()
# 在括号内加入host参数
server = smtplib.SMTP_SSL(smtp_server)
加入host参数(邮箱服务器地址后),这个问题应该就迎刃而解了。
如果你用的是QQ以外的其他邮箱,也可能会出现一种报错。比如利用我的企业邮箱的话,就会出现如下报错信息:
smtplib.SMTPException: No suitable authentication method found
搜索后,我们很快就会发现,解决方案是:在登录(login
)之前调用starttls()
方法就可以了。也就是在代码中加入这样一行:
server.starttls()
这些问题基本上都能通过搜索解决。如果你搞不清楚是什么问题的话,试一试将端口参数改一下,使用默认端口25。
# 把这两行代码改掉
server = smtplib.SMTP_SSL()
server.connect(smtp_server,465)
# 改掉port参数,并去掉_SSL
server = smtplib.SMTP()
server.connect(smtp_server,25)
经过一番缠斗,现在你应该可以去查收你用 Python 发出的第一封邮件了!
# smtplib 用于邮件的发信动作 # email 用于构建邮件内容
import smtplib
from email.mime.text import MIMEText
# 发信方的信息:发信邮箱,QQ 邮箱授权码
from_addr = '1773464408@qq.com'
password = 'erioxlarebwzceic'
# 收信方邮箱
to_addr = '17310658206@163.com'
# 发信服务器
smtp_server = 'smtp.qq.com'
# 邮箱正文内容,第一个参数为内容,第二个参数为格式(plain 为纯文本),第三个参数为编码
msg = MIMEText('send by python','plain','utf-8')
# 开启发信服务,这里使用的是加密传输
server = smtplib.SMTP_SSL()
server.connect(smtp_server,465)
# 登录发信邮箱
server.login(from_addr, password)
# 发送邮件
server.sendmail(from_addr, to_addr, msg.as_string())
# 关闭服务器
server.quit()
在收件箱,你看到的内容大概是这样的:
你给自己发送了一封最简单的邮件,也完成了邮件模块的初步学习。版本1.0,成功~
但是,现在还不能算是一封完整的邮件,没有发件人信息,没有主题,正文内容太简单......这就是我们接下来要干的事情:
3.2 版本2.0:给自己发一封完整邮件
这个“完整”可以分为两部分,我们一步步来:
我们先来丰富邮件头:
邮件头(header,没错它也叫header)是这一块区域,包括主题、发件人、收件人等信息:
现在你可以直接去搜索别人的代码看看,请打开浏览器搜索“发邮件 python”并浏览查看其他人的代码,看和我们之前写好的代码存在哪些区别。
你应该发现了,区别就在于,别人的代码多了这几行:
from email.header import Header
msg['From'] = Header('xxx')
msg['To'] = Header('xxx')
msg['Subject'] = Header('xxx')
第一行代码,从email包引入Header()方法
。Header()
是用来构建邮件头的。
标准邮件需要三个头部信息:From , To 和 Subject ,第三到五行代码就提供了这三个信息。
这里我们可以自定义,比如from发件人邮箱地址,to收件人邮箱地址,主题“python test”。
from email.header import Header
msg['From'] = Header(from_addr)
msg['To'] = Header(to_addr)
msg['Subject'] = Header('python test')
请你在自己编辑器的代码里,补充关于邮件头的内容。给你五分钟~
写好了吗?那来看看我的答案:
# smtplib 用于邮件的发信动作
import smtplib
from email.mime.text import MIMEText
# email 用于构建邮件内容
from email.header import Header
# 用于构建邮件头
# 发信方的信息:发信邮箱,QQ 邮箱授权码
from_addr = '1773464408@qq.com'
password = 'erioxlarebwzceic'
# 收信方邮箱
to_addr = '17310658206@163.com'
# 发信服务器
smtp_server = 'smtp.qq.com'
# 邮箱正文内容,第一个参数为内容,第二个参数为格式(plain 为纯文本),第三个参数为编码
msg = MIMEText('send by python','plain','utf-8')
# 邮件头信息
msg['From'] = Header(from_addr)
msg['To'] = Header(to_addr)
msg['Subject'] = Header('python test')
# 开启发信服务,这里使用的是加密传输
server = smtplib.SMTP_SSL(smtp_server)
server.connect(smtp_server,465)
# 登录发信邮箱
server.login(from_addr, password)
# 发送邮件
server.sendmail(from_addr, to_addr, msg.as_string())
# 关闭服务器
server.quit()
很棒,我们完成了丰富邮件头的操作!现在收到邮件就有点不一样了:
除了可以直接用邮箱地址之外,你还可以自定义内容,比如:
from email.header import Header
msg['From'] = Header('杨文博')
msg['To'] = Header('小可爱')
msg['Subject'] = Header('来自杨文博的问候')
你可以根据自己的想法来自定义邮件头。接下来,我们要想想怎么丰富邮件内容了。
原本邮件内容是写在这里:
msg = MIMEText('send by python','plain','utf-8')
如果你想要写很长的内容,建议先设置一个变量text用来放正文内容。
text = 'send by python'
msg = MIMEText(text,'plain','utf-8')
一般,邮件正文需要换行,不想一大串文字直接显示,我们可以在正文内容前后用''',还记得三引号的用法吧!
所以,我们的正文内容可以这样写:
text = '''亲爱的小伙伴,你好!
我是杨文博,能遇见你很开心。
希望学习Python对你不是一件困难的事情!
人生苦短,我用Python
'''
msg = MIMEText(text,'plain','utf-8')
到这里,就请你使用这个方式,来更新原来代码中的正文内容,写一段长一点儿的话。
下面是我写的,供你参考:
# smtplib 用于邮件的发信动作
import smtplib
from email.mime.text import MIMEText
# email 用于构建邮件内容
from email.header import Header
# 用于构建邮件头
# 发信方的信息:发信邮箱,QQ邮箱授权码)
from_addr = '1773464408@qq.com'
password = 'erioxlarebwzceic'
# 收信方邮箱
to_addr = '17310658206@163.com'
# 发信服务器
smtp_server = 'smtp.qq.com'
# 邮箱正文内容,第一个参数为内容,第二个参数为格式(plain 为纯文本),第三个参数为编码
text = '''亲爱的小伙伴,你好!
我是杨文博,能遇见你很开心。
希望学习Python对你不是一件困难的事情!
人生苦短,我用Python
'''
msg = MIMEText(text,'plain','utf-8')
# 邮件头信息
msg['From'] = Header(from_addr)
msg['To'] = Header(to_addr)
msg['Subject'] = Header('python test')
# 开启发信服务,这里使用的是加密传输
server = smtplib.SMTP_SSL(smtp_server)
server.connect(smtp_server,465)
# 登录发信邮箱
server.login(from_addr, password)
# 发送邮件
server.sendmail(from_addr, to_addr, msg.as_string())
# 关闭服务器
server.quit()
看下执行结果:
之前也提到了,出于保护隐私的目的,我们可以把收发件人,和授权码这些信息用input()
变成需要输入的模式。
这很简单,相信你能做到,我直接展示一下这几行代码就好。
# 发信方的信息:发信邮箱,QQ邮箱授权码)
from_addr = input('请输入登录邮箱:')
password = input('请输入邮箱授权码:')
# 收信方邮箱
to_addr = input('请输入收件邮箱:')
我在版本3.0中,也将在一些地方用到这样的方式去改写代码,让整个程序更有“互动性”。好啦,终于要开始版本3.0。截止目前为止都还顺利吗?我们终于要开始群发邮件了!
3.3 版本3.0:群发完整邮件
只要你搜索一下“Python 群发邮件”,就有海量的信息供你参考。我这里讲三种群发的方式:
一,是将收件人信箱的变量设置成一个可以装多个内容的列表:
to_addrs = ['wufeng@qq.com','kaxi@qq.com']
需要注意的是,to_addrs
变量也将作为参数被传入Header
方法中:
msg['To'] = Header(to_addrs)
直接运行程序的话,这里就会发生错误:AttributeError: 'list' object has no attribute 'decode'
。
如果你有查看官方文档的好习惯,那么你会发现这是因为Header
接受的第一个参数的数据类型必须要是字符串或者字节,列表不能解码。
也就是说,我们要将to_addrs
变成一个字符串,怎么做呢?好像没有学过?其实,我们只需要对这行代码做一个这样的操作:
msg['to'] = Header(",".join(to_addrs))
是否觉得眼熟?在关于文件读写的关卡中,有一个哈利波特的课堂小练习,其中我们简单地提到了join()
函数,它的功能是把字符串合并:
join()
的用法是str.join(sequence)
,str代表在这些字符串之中你要用什么字符串来连接,你可以用逗号,空格,下划线等等。要将列表的元素合并,当然我们就直接使用逗号来连接了。
来打印一下转换前后的数据类型,验证一下。
OK了吧,现在就没问题了,请你也把自己的代码修改完整。
# smtplib 用于邮件的发信动作
import smtplib
from email.mime.text import MIMEText
# email 用于构建邮件内容
from email.header import Header
# 用于构建邮件头
# 发信方的信息:发信邮箱,QQ邮箱授权码)
from_addr = '1773464408@qq.com'
password = 'erioxlarebwzceic'
# 收信方邮箱
to_addrs = ['17310658206@163.com','2659911387@qq.com']
# 发信服务器
smtp_server = 'smtp.qq.com'
# 邮箱正文内容,第一个参数为内容,第二个参数为格式(plain 为纯文本),第三个参数为编码
text='''亲爱的小伙伴,你好!
我是杨文博,能遇见你很开心。
希望学习Python对你不是一件困难的事情!
人生苦短,我用Python
'''
msg = MIMEText(text,'plain','utf-8')
# 邮件头信息
msg['From'] = Header(from_addr)
msg['To'] = Header(",".join(to_addrs))
msg['Subject'] = Header('python test')
# 开启发信服务,这里使用的是加密传输
server = smtplib.SMTP_SSL(smtp_server)
server.connect(smtp_server,465)
# 登录发信邮箱
server.login(from_addr, password)
# 发送邮件
server.sendmail(from_addr, to_addrs, msg.as_string())
# 关闭服务器
server.quit()
查看一下运行结果:
这里有个小细节需要注意:我们将收件人变量名从to_addr改成了复数to_addrs之后,后面用到这个变量的地方,也要进行修改!
第一种方式,我们就讲到这里。
第二种方法是采用询问“是否继续输入邮箱地址”的方式,并用while循环来实现多个收件人的功能。
由于我们要存储输入的内容,供发邮件的时候使用。所以需要定义一个空列表to_addrs
,用来存放收件人邮箱地址。输入邮箱地址的时候,地址会被追加写进列表。
因为循环次数不固定,所以我们选择while循环来做。我的这段代码是这样的,加了一个print()函数来确认结果:
to_addrs = []
while True:
a=input('请输入收件人邮箱:')
#输入收件人邮箱
to_addrs.append(a)
#写入列表
b=input('是否继续输入,n退出,任意键继续:')
#询问是否继续输入
if b == 'n':
break
print(to_addrs)
下面,我们就可以尝试第二种方法来群发邮件了。除了收件人,我们把发件人和授权码也改成input模式。你先自己写一写,试试看能否收到邮件。然后来这里点击运行玩一玩:
# smtplib 用于邮件的发信动作
import smtplib
from email.mime.text import MIMEText
# email 用于构建邮件内容
from email.header import Header
# 用于构建邮件头
# 发信方的信息:发信邮箱,QQ 邮箱授权码
from_addr = input('请输入登录邮箱:')
password = input('请输入邮箱授权码:')
# 收信方邮箱
to_addrs = []
while True:
a=input('请输入收件人邮箱:')
to_addrs.append(a)
b=input('是否继续输入,n退出,任意键继续:')
if b == 'n':
break
# 发信服务器
smtp_server = 'smtp.qq.com'
# 邮箱正文内容,第一个参数为内容,第二个参数为格式(plain 为纯文本),第三个参数为编码
text='''亲爱的小伙伴,你好!
我是杨文博,能遇见你很开心。
希望学习Python对你不是一件困难的事情!
人生苦短,我用Python
'''
msg = MIMEText(text,'plain','utf-8')
# 邮件头信息
msg['From'] = Header(from_addr)
msg['To'] = Header(",".join(to_addrs))
msg['Subject'] = Header('python test')
# 开启发信服务,这里使用的是加密传输
server = smtplib.SMTP_SSL(smtp_server)
server.connect(smtp_server,465)
# 登录发信邮箱
server.login(from_addr, password)
# 发送邮件
server.sendmail(from_addr, to_addrs, msg.as_string())
# 关闭服务器
server.quit()
看下执行结果:
好啦,第二种方法也讲完啦,是不是挺简单的?接下来是最后一种方法:
用上一关学习的csv模块,将收件人邮箱写入csv文件,在发邮件时读取csv文件。
将邮箱地址写入csv模块的方法是write()
,步骤是:1.引入csv模块;2.提供需要写入csv文件的数据,3.建文件并写入。
import csv
#引用csv模块。
data = [['wufeng ', 'wufeng@qq.com'],['kaxi', 'kaxi@qq.com']]
#待写入csv文件的内容
with open('to_addrs.csv', 'w', newline='') as f:
writer = csv.writer(f)
for row in data:
writer.writerow(row)
第一行,引入模块。第四行是等待写入csv文件的数据。
但是我们没有这样的文件,所以还需要新建一个to_addrs.csv
文件。我们使用的是with语句新建文件,这样做的好处是:到达语句末尾时,会自动关闭文件,不需要close()。
紧接着,我们定义了一个变量writer进行写入,将刚才的文件变量传进来。
之后就是进行数据写入,写入的方法是writerow()
。通过遍历列表data将数据一行行写到了to_addrs.csv
文件中。
读取的过程就异曲同工了。利用的是read()
方法。步骤是:1.引入csv模块;2.打开csv文件;3.读取需要的数据。
import csv
#引用csv模块。
with open('to_addrs.csv', 'r') as f:
reader = csv.reader(f)
for row in reader:
to_addrs=row[1]
row1表示csv文件中第1列的数据。想一想:wufeng和kaxi属于第0列的数据,邮箱地址则属于第1列的数据,所以第1列数据才是我们需要的!
好啦,接下来要做的就是把取出来的内容赋值给变量to_addrs,并在发送邮件时使用。先提个醒,注意哪些代码需要在for循环内部缩进。
我的完整代码如下:
import smtplib
# smtplib 用于邮件的发信动作
from email.mime.text import MIMEText
# email 用于构建邮件内容
from email.header import Header
# 用于构建邮件头
import csv
# 引用csv模块,用于读取邮箱信息
# 发信方的信息:发信邮箱,QQ邮箱授权码
# 方便起见,你也可以直接赋值
from_addr = input('请输入登录邮箱:')
password = input('请输入邮箱授权码:')
# 发信服务器
smtp_server = 'smtp.qq.com'
# 邮件内容
text='''亲爱的小伙伴,你好!
我是杨文博,能遇见你很开心。
希望学习python对你不是一件困难的事情!
人生苦短,我用Python
'''
# 待写入csv文件的收件人数据:人名+邮箱
# 记得替换成你要发送的名字和邮箱
data = [['杨文博的163邮箱 ', '17310658206@163.com'],['杨文博的QQ邮箱', '2659911387@qq.com']]
# 写入收件人数据
with open('to_addrs.csv', 'w', newline='') as f:
writer = csv.writer(f)
for row in data:
writer.writerow(row)
# 读取收件人数据,并启动写信和发信流程
with open('to_addrs.csv', 'r') as f:
reader = csv.reader(f)
for row in reader:
to_addrs=row[1]
msg = MIMEText(text,'plain','utf-8')
msg['From'] = Header(from_addr)
msg['To'] = Header(to_addrs)
msg['Subject'] = Header('python test')
server = smtplib.SMTP_SSL(smtp_server)
server.connect(smtp_server,465)
server.login(from_addr, password)
server.sendmail(from_addr, to_addrs, msg.as_string())
# 关闭服务器
server.quit()
查看一下执行结果:
如果报错(报错是很正常的事情啦),记得排查我们之前说过的几个问题。
这就是用csv模块读取多个收件人邮箱的方式啦!虽然读取稍微有点麻烦,但如果我们事先建了这样一个存储邮箱的csv文件,之后就可以一直复用它。从长期来看,还是利大于弊的。
而且在更高级的Python爬虫精进课程里面,我们也同样会用到csv模块,早点熟悉它会更好。
此外,你还可以为这个程序加一段异常处理代码,也就是try...except...语句来帮助你更好地处理你遇到的问题。
try:
server.sendmail(from_addr, to_addrs, msg.as_string())
print('恭喜,发送成功')
except:
print('发送失败,请重试')
终于呀,用了好几种方法解决了群发的问题。到现在群发内容比较丰富的邮件的目的,就达成了!
4. 习题练习
4.1 习题一
1.练习目标:
我们会通过今天的作业,学习一个新的模块:MyQR,制作一个动态二维码。
2.练习要求:
在昨天的练习里,你已经可以通过自己的学习,完成代码的升级。
所以,今天我们不妨多做些新的尝试。
需求很明确:请你在本地编辑器(如vscode、Pycharm),用 Python 制作一个动态二维码。
3.代码实操
你应该已经知道了这个模块是:MyQR,也能读懂相关的代码。
请你自学相关模块知识后,自行下载一个gif,然后在本地编辑器(如vscode、Pycharm)完成代码吧。
对了,不要忘了先安装myqr模块,Windows终端里运行'pip install myqr',Mac终端运行'pip3 install myqr'
# 先导入模块
from MyQR import myqr
myqr.run(
words='http://weixin.qq.com/r/kzlje9TEE4lsrZAY92yB',
# 扫描二维码后,显示的内容,或是跳转的链接
version=5, # 设置容错率
level='H', # 控制纠错水平,范围是L、M、Q、H,从左到右依次升高
picture='C://Users//17310//Desktop//ceshi//she.gif', # 图片所在目录,可以是动图
colorized=True, # 黑白(False)还是彩色(True)
contrast=1.0, # 用以调节图片的对比度,1.0 表示原始图片。默认为1.0。
brightness=1.0, # 用来调节图片的亮度,用法同上。
save_name='C://Users//17310//Desktop//ceshi//Python.gif', # 控制输出文件名,格式可以是 .jpg, .png ,.bmp ,.gif
)
4.2 习题二
1.练习目标:
通过这个练习,你至少可以用3种方法去求某个数的绝对值。
2.练习要求:
请你通过搜索和步骤中的提示,用不同的方法求绝对值。
建议将其封装成函数,如“abs_value1()”。
3.挑战3种方法解题
请根据下侧的关键词提示以及网上搜索,用3种不同的方法求绝对值。
import math
def abs_value1():
a = float(input('1.请输入一个数字:'))
if a >= 0:
a = a
else:
a = -a
print('绝对值为:%f' % a)
def abs_value2():
a = float(input('2.请输入一个数字:'))
a = abs(a)
print('绝对值为:%f' % a)
def abs_value3():
a = float(input('3.请输入一个数字:'))
a = math.fabs(a)
print('绝对值为:%f' % a)
abs_value1()
abs_value2()
abs_value3()