马上就520了不知道各位有没有为女朋友/男朋友准备好礼物呢,反正北轨我呢除了准备了一份礼物外还发挥自己的专业能力,为女朋友送上一点小惊喜,那就是今天的主题——如何实现用python自动发送一封爱意满满的邮件

拒绝造轮子

当你遇到一个问题的时候第一反应一定不要是自己想办法解决,一定要问度娘!!!!所以当我遇到脑海之中产生给小苏一个特别的礼物的想法的时候,我第一时间询问了度娘的意见,结果是“满载而归”,度娘并没有给我一个满意的答案(轮子),这时我把目光投向了github,在github中我找到了腹黑前辈的一个项目名叫love_mail。在我看完这个项目的readme之后我发现这个项目不就是我梦中的那个它嘛😍感谢腹黑哥哥

旧轮子介绍

在我找到这个程序之后第一时间对程序进行了测试,程序的逻辑并不难理解,首先开头除去必要模块之外,就是下面的配置信息,依次填好

在配置完基本信息后是六个定义函数,分别用来计算纪念日天数(get_day())、获取天气信息(get_weathertip())、获取彩虹屁(get_chp())和理出获取到的最近三天的天气信息(get_weather())、获取图片(get_image())、获取今天日期(get_today())。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
def get_day():
d1 = datetime.datetime.strptime(special_day, '%Y-%m-%d')
d2 = datetime.datetime.strptime(datetime.datetime.now().strftime('%Y-%m-%d'), '%Y-%m-%d')
delta = d2 - d1
return delta.days

def get_weathertip():
url = "https://tianqi.moji.com/weather/china/%s/%s"%(province,city)
resp = requests.get(url)
soup = BeautifulSoup(resp.text,"html5lib",from_encoding="utf-8")
all_tertiaryconsumers = soup.find_all(class_='wea_tips clearfix')
for tertiaryconsumer in all_tertiaryconsumers:
return re.search('<em>(.+?)</em>',str(tertiaryconsumer)).group(1)

def get_chp():
url = "https://api.shadiao.app/chp"
resp = requests.get(url)
a=resp.json()
data_trunk = a['data']
text = data_trunk['text']
return text

def get_weather():
url = "https://tianqi.moji.com/weather/china/%s/%s"%(province,city)
resp = requests.get(url)
soup = BeautifulSoup(resp.text,"html5lib",from_encoding="utf-8")
all_tertiaryconsumers = soup.find_all(class_='days clearfix')
html = ''
for tertiaryconsumer in all_tertiaryconsumers:
day = tertiaryconsumer.find(name='a').text
url = re.search('src="(.+?)"',str(tertiaryconsumer)).group(1)
weather = re.search('<img alt="(.+?)"',str(tertiaryconsumer)).group(1)
temperature = re.search('(\w+° \/ \w+°)',str(tertiaryconsumer)).group(1)
if 'level_1' in str(tertiaryconsumer):
WindLevel = tertiaryconsumer.find(class_='level_1').text.strip()
color = '#8fc31f'
if 'level_2' in str(tertiaryconsumer):
WindLevel = tertiaryconsumer.find(class_='level_2').text.strip()
color = '#d7af0e'
if 'level_3' in str(tertiaryconsumer):
WindLevel = tertiaryconsumer.find(class_='level_3').text.strip()
color = '#f39800'
if 'level_4' in str(tertiaryconsumer):
WindLevel = tertiaryconsumer.find(class_='level_4').text.strip()
color = '#e2361a'
if 'level_5' in str(tertiaryconsumer):
WindLevel = tertiaryconsumer.find(class_='level_5').text.strip()
color = '#5f52a0'
if 'level_6' in str(tertiaryconsumer):
WindLevel = tertiaryconsumer.find(class_='level_6').text.strip()
color = '#631541'
html += """<div style="display: flex;margin-top:5px;height: 30px;line-height: 30px;justify-content: space-around;align-items: center;">
<span style="width:15%%; text-align:center;">%s</span>
<div style="width:10%%; text-align:center;">
<img style="height:26px;vertical-align:middle;" src='%s' alt="">
</div>
<span style="width:25%%; text-align:center;">%s</span>
<div style="width:35%%; ">
<span style="display:inline-block;padding:0 8px;line-height:25px;color:%s; border-radius:15px; text-align:center;">%s</span>
</div>
</div>
""" % (day, url, temperature, color, WindLevel)
return html

def get_image():
url = "http://wufazhuce.com/"
resp = requests.get(url)
soup = BeautifulSoup(resp.text,"html5lib",from_encoding="utf-8")
return re.search('src="(.+?)"',str(soup.find(class_='fp-one-imagen'))).group(1)

def get_today():
i = datetime.datetime.now()
date = "%s/%s/%s" % (i.year, i.month, i.day)
return date

在定义完这几个函数之后下面是一段html代码用来使画面更美观,在html代码后面加上定义的函数使主体程序完成,最后是段发送代码用来实现对邮件的自动发送

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
def send_mail(to_list,sub,content):
me=name+"<"+mail_user+">"
msg = MIMEText(content,_subtype='html',_charset='utf-8')
msg['Subject'] = sub
msg['From'] = me
msg['To'] = ",".join(mailto_list)
try:
server = smtplib.SMTP_SSL(mail_host, 465)
server.login(mail_user,mail_pass)
server.sendmail(me, to_list, msg.as_string())
server.close()
return True
except Exception as e:
print(str(e))
return False
if __name__ == '__main__':
if send_mail(mailto_list,mail_title,mail_content):
print("发送成功")
else:
print("发送失败")

这段代码使用了smtplibemail.mime.text两个库来进行处理。

本来这是一段很完美的代码,但是却因为是2021年的最后修改的原因,导致彩虹屁这个函数所调用的接口失效,会导致发送邮件的返回值出现奇怪的信息😒于是就开始改造轮子。

image-20220514223508538

旧轮子改造计划

在发现彩虹屁的接口被废除之后,我访问显示的网站准备获取新的API,但是目标网站并没有把API放在明面上,正在我准备寻找新的接口时,我想到或许我可以用抓包的方式,抓到网站的请求接口

image-20220515102055483

不知道是我这边浏览器的问题还是网站进行了限制,我使用在火狐上挂代理无法在burp抓到对接口的请求包,所以我使用burp自带的浏览器重新请求,成功抓到数据。我们可以发现他的请求接口时api.shadiao.app/chp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
GET /chp HTTP/2
Host: api.shadiao.app
Sec-Ch-Ua: "(Not(A:Brand";v="8", "Chromium";v="100"
Accept: application/json, text/plain, */*
Sec-Ch-Ua-Mobile: ?0
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.60 Safari/537.36
Sec-Ch-Ua-Platform: "Windows"
Origin: https://chp.shadiao.app
Sec-Fetch-Site: same-site
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: https://chp.shadiao.app/
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9

由于获取到的数据时json数据和原本腹黑从原接口请求到的返回值不同,所以我们这里需要重新改造一下get_chp()

1
2
3
4
5
6
7
#api返回值
{
"data": {
"type": "彩虹屁",
"text": "原以为我已经不会再爱上任何一个人,但看到你的一瞬间我才发现自己错了。你是我的梦中女神,我承认自己心动了"
}
}
1
2
3
4
5
#原代码
def get_chp():
url = "https://chp.shadiao.app/api.php"
resp = requests.get(url)
return resp.text

原代码用requests库获取到信息后直接取其返回值,但由于新接口时json数据,所以直接返回值的话就会出现如图情况

image-20220515113524569

所以我在这边重构一下这个函数,使其可以正常返回彩虹屁,下面是我重构后的函数,最后实现完整效果

1
2
3
4
5
6
7
8
9
# 重构后代码
def get_chp():
url = "https://api.shadiao.app/chp"
resp = requests.get(url)
a=resp.json() #读取url中的json信息
data_trunk = a['data'] #讲json中的data数据提取出来
text = data_trunk['text'] #讲json中的text数据提取出来
return text #返回文本彩虹屁文本信息
#效果如下图所示

image-20220515114533531

tips

源码配置文件中使用企业微信邮箱开启smtp功能实现自动发送文件的功能,普通QQ邮箱无法实现此功能,但是由于我没有企业微信所以我使用网易邮箱来作为替代品。

另外需要注意的是,配置文件中出现的密码,是网易邮箱对设备的授权码而不是邮箱密码,用如图所示方法来获取授权码

image-20220515115131757