萌新记录某些日子的学习经历。
老师中午给我发了批改后的实验报告(.pdf),作为教师助理我需要把每份报告返还给对应的学生,还需要登记成绩。一个学期5+次报告,50+位学生,使用学校系统发送邮件,过程太繁琐了,要耗费很多时间精力。写一个脚本应该方便不少。
每一份报告pdf文件名都会出现学号,并且我们学校邮箱也是学号+后缀的统一格式。所以我确定了初步思路:遍历文件名,获取学号,编辑好邮件内容发送给对应学号的学生。作为这方面的萌新,请教了学长后得到肯定答复,于是有了以下内容。
摘要:Python 脚本 邮件 os re smtplib MIMEText MIMEMultipart MIMEApplication
以下是具体实现过程:
目录
使用os遍历文件
使用re获取文件名中的学号
发送邮件
Result
Whole Code
Reference
import os
path = "./" + labName
files = os.listdir(path)
print(type(files),len(files))
for filename in files:
需要使用os,os模块提供了非常丰富的方法用来处理文件和目录。[1]
os.listdir(path) 返回path指定的文件夹包含的文件或文件夹的名字的列表。
比如说我的代码的绝对路径是E:\A_scriptTools\dspMail,我保存文件的绝对路径是E:\A_scriptTools\dspMail\demo,文件保存为E:\A_scriptTools\dspMail\demo\*。此时我path是demo这个文件夹。这个函数返回的值是一个存储string的list。每个string是每个文件的名字,包括后缀。
至此我们得到了每个PDF文件的名字。
pattern = "Lab2 Report_(.*?)_.*?"
def getSID(filename):
information = re.match(pattern, filename)
SID = information.group(1)
return SID
需要使用re,正则表达式是一个特殊的字符序列,它能帮助我们方便的检查一个字符串是否与某种模式匹配。[2]
re.match(pattern, string, flags=0)
pattern是需要匹配的正则表达式,string是要匹配的字符串。flag标志位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等等。
比如我这里:
其中"11111111"是学号,"Lab 2 Report_" 和学号后的"_"是固定不变的。那么我的pattern设置为
pattern = "Lab2 Report_(.*?)_.*?"
.*? 可以视为任意字符串。返回的值是一个group(如果不存在这样格式的是none)。group[0]表示在string中找到的完整的pattern。group[i]表示需要替换第i个(.*?)(要有括号)的内容。比如说:
这里group[1]就是需要的学号。
从思路上来说发送邮件需要三个部分:设置参数(需要各种参数,包括host,用户名,密码,发送地址和接收地址,发送时间等等)、编辑邮件内容(主要包括正文和附件)、发送邮件(发送邮件这个过程需要先“登录邮箱”,并且还要确保发送这个过程是顺利的)。
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.application import MIMEApplication
需要借助两个包:
smtplib是关于 SMTP(简单邮件传输协议)的操作模块,在发送邮件的过程中起到服务器之间互相通信的作用[3]。用于登录邮箱、把编辑好的邮件发送出去。
三个3来自email.mime.*的工具用于编辑邮件(后缀大概能看出各自的作用):
MIMEMultipart可以说是粘合剂、框架。有了这个粘合剂,那么正文和附件都能往上填。
MIMEText主要用于处理文本信息,正文一般用这个来生成。
MIMEApplication是我用来处理PDF文件的。
那么代码实现如下,
1. 首先输入参数:
mail_host = 'smtp.exmail.qq.com'
my_sender = 'XXXX'
my_pass = 'xxxx'
receiver = SID + '@mail.XXX.edu.cn'
mail_host我这里是使用的腾讯企业邮箱,值得注意的:1.腾讯企业邮箱的登录密码不是邮箱密码,而是客户端密码。需要在网页版腾讯企业邮箱中打开:“设置-邮箱绑定-安全设置:开启安全登录”,如果不这样做,用smtplib登录腾讯企业邮箱没办法突破人工验证那一关,会报错:535, b'Error: authentication failed, system busy。2.腾讯企业邮箱需要 SSL 认证,所以我之后的代码会是SMTP_SSL,有的邮箱不需要SSL使用SMTP就好。详情请看Reference中知乎那篇专栏,写的比我好。
sender和receiver分别表示发送地址和接收地址。
2. 接下来,编辑邮件内容
message = MIMEMultipart()
message['From'] = my_sender
message['To'] = receiver
message['Subject'] = labName + ' 报告结果'
part1 = MIMEText('这是批改后的报告,请您查收。祝好。', 'plain', 'utf-8')
part2 = MIMEApplication(open(file, 'rb').read())
part2.add_header('Content-Disposition', 'attachment', filename=labName+'-'+SID+'.pdf')
message.attach(part1)
message.attach(part2)
新建一个message,From表示发件地址,To表示收件地址,Subject表示标题,part1是正文,Part2是对应的PDF文件。注意正文和PDF文件所调用的函数都有参数需要输入。需要根据实际情况调整。最后使用message.attach装入正文和附件。
3. 最后,发送邮件
try:
smtpObj = smtplib.SMTP_SSL(mail_host, port=465)
smtpObj.login(my_sender, my_pass)
smtpObj.sendmail(
my_sender, receiver, message.as_string())
smtpObj.quit()
print(SID + ' success')
except smtplib.SMTPException as e:
print(SID+'error', e)
如前文所说,腾讯企业邮箱需要SMTP_SSL,端口号是465,不同邮箱最好先查询一些具体端口号。
login登录邮箱,sendmail发送编辑好的邮件,最后quit退出邮箱。
大概流程以上,附实际效果图:
总结一下,都是很基础的工具。一个文件夹装好PDF文件,定位到路径用os读文件名;初步分析学号位置,用re在字符串中找到学号;email.mime.*写邮件,包括邮件标题、收件人、发件人,正文和附件需要各自创建好后组合在一起。用smtplib发邮件,登录-发送-登出,登录需要账号密码以及不被安全检测阻拦。
(By the way听说yagmail更适合python?
感谢阅读,请多指教!
import os
import re
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.application import MIMEApplication
labName = ""
pattern = ""
mail_host = ''
my_sender = ''
my_pass = ''
def getSID(filename):
information = re.match(pattern, filename)
SID = information.group(1)
return SID
def sendEmail(SID, file):
receiver = SID + '@mail.XXX.edu.cn'
## PDF
message = MIMEMultipart()
message['From'] = my_sender
message['To'] = receiver
message['Subject'] = labName + ' 报告结果'
part1 = MIMEText('这是批改后的报告,请您查收。祝好。', 'plain', 'utf-8')
part2 = MIMEApplication(open(file, 'rb').read())
part2.add_header('Content-Disposition', 'attachment', filename=labName+'-'+SID+'.pdf')
message.attach(part1)
message.attach(part2)
try:
smtpObj = smtplib.SMTP_SSL(mail_host, port=465)
smtpObj.login(my_sender, my_pass)
smtpObj.sendmail(
my_sender, receiver, message.as_string())
smtpObj.quit()
print(SID + ' success')
except smtplib.SMTPException as e:
print(SID+'error', e)
def sendPDF(labName):
path = "./" + labName
files = os.listdir(path)
print(type(files),len(files))
for filename in files:
SID = getSID(filename)
print(SID)
sendEmail(SID,path+'/'+filename)
if __name__ == "__main__":
sendPDF(labName)
[1] Python OS 文件/目录方法 | 菜鸟教程 (runoob.com)
[2] Python 正则表达式 | 菜鸟教程 (runoob.com)
[3] 简单三步,用 Python 发邮件 - 知乎 (zhihu.com)
[4] Python 简单发送邮件 / 发送带各种附件邮件_zqzwzd的博客-CSDN博客_python发送邮件带附件
感谢学长的帮助 手动@郭山车 (7条消息) guo_hao_rui的博客_郭山车_CSDN博客-数据科学中的统计分析领域博主