Send Email By Python

在 Pi 中使用 Python 来将传感器的数据发送至邮箱。

Use Python

SMTP (Simple Mail Transfer Protocol)

邮件传送代理 (Mail Transfer Agent,MTA) 程序使用 SMTP 协议来发送电邮到接收者的邮件服务器。SMTP 协议只能用来发送邮件,不能用来接收邮件。大多数的邮件发送服务器 (Outgoing Mail Server) 都是使用 SMTP 协议。SMTP 协议的默认 TCP 端口号是 25。

SMTP 协议的一个重要特点是它能够接力传送邮件。它工作在两种情况下:一是电子邮件从客户机传输到服务器;二是从某一个服务器传输到另一个服务器。

POP3 (Post Office Protocol) & IMAP (Internet Message Access Protocol)

POP 协议和 IMAP 协议是用于邮件接收的最常见的两种协议。几乎所有的邮件客户端和服务器都支持这两种协议。

POP3 协议为用户提供了一种简单、标准的方式来访问邮箱和获取电邮。使用 POP3 协议的电邮客户端通常的工作过程是:连接服务器、获取所有信息并保存在用户主机、从服务器删除这些消息然后断开连接。POP3 协议的默认 TCP 端口号是 110。

IMAP 协议也提供了方便的邮件下载服务,让用户能进行离线阅读。使用 IMAP 协议的电邮客户端通常把信息保留在服务器上直到用户显式删除。这种特性使得多个客户端可以同时管理一个邮箱。IMAP 协议提供了摘要浏览功能,可以让用户在阅读完所有的邮件到达时间、主题、发件人、大小等信息后再决定是否下载。IMAP 协议的默认 TCP 端口号是 143。

邮件格式 (RFC 2822)

每封邮件都有两个部分:邮件头和邮件体,两者使用一个空行分隔。

邮件头每个字段 (Field) 包括两部分:字段名和字段值,两者使用冒号分隔。有两个字段需要注意:From 和 Sender 字段。From 字段指明的是邮件的作者,Sender 字段指明的是邮件的发送者。如果 From 字段包含多于一个的作者,必须指定 Sender 字段;如果 From 字段只有一个作者并且作者和发送者相同,那么不应该再使用 Sender 字段,否则 From 字段和 Sender 字段应该同时使用。

邮件体包含邮件的内容,它的类型由邮件头的 Content-Type 字段指明。RFC 2822 定义的邮件格式中,邮件体只是单纯的 ASCII 编码的字符序列。
MIME (Multipurpose Internet Mail Extensions) (RFC 1341)

MIME 扩展邮件的格式,用以支持非 ASCII 编码的文本、非文本附件以及包含多个部分 (multi-part) 的邮件体等。

Python Email模块

  1. class email.message.Message
    __getitem__,__setitem__ 实现obj[key]形式的访问。
    Msg.attach(playload): 向当前 Msg 添加 playload。
    Msg.set_playload(playload): 把整个 Msg 对象的邮件体设成 playload。
    Msg.add_header(_name, _value, **_params): 添加邮件头字段。
  2. class email.mime.base.MIMEBase(_maintype, _subtype, **_params)
    所有 MIME 类的基类,是 email.message.Message 类的子类。
  3. class email.mime.multipart.MIMEMultipart()
    在3.0版本的email模块 (Python 2.3-Python 2.5) 中,这个类位于email.MIMEMultipart.MIMEMultipart。
    这个类是MIMEBase的直接子类,用来生成包含多个部分的邮件体的MIME对象。
  4. class email.mime.text.MIMEText(_text)
    使用字符串_text来生成MIME对象的主体文本

例子

发文本内容

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
#!/usr/bin/env python
# coding=utf-8


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

# 第三方 SMTP 服务
mail_host = "smtp.163.com" # SMTP服务器
mail_user = "asdf@163.com" # 用户名
mail_pass = "****************" # 授权密码,非登录密码

sender = 'asdf@163.com' # 发件人邮箱(最好写全, 不然会失败)
receivers = ['*******@163.com'] # 接收邮件,可设置为你的QQ邮箱或者其他邮箱

content = '我用Python发邮件'
title = '人生苦短,闲来无事发邮件' # 邮件主题

def sendEmail():

message = MIMEText(content, 'plain', 'utf-8') # 内容, 格式, 编码
message['From'] = "{}".format(sender)
message['To'] = ",".join(receivers)
message['Subject'] = Header(title, 'utf-8') # 内容,编码

try:
smtpObj = smtplib.SMTP_SSL(mail_host, 994) # 启用SSL发信, 端口一般是465,25为明文端口,163邮箱的TLS使用465和994端口
smtpObj.login(mail_user, mail_pass) # 登录验证
smtpObj.sendmail(sender, receivers, message.as_string()) # 发送
print("mail has been send successfully.")
except smtplib.SMTPException as e:
print(e)

def send_email2(SMTP_host, from_account, from_passwd, to_account, subject, content):
email_client = smtplib.SMTP(SMTP_host)
email_client.login(from_account, from_passwd)
# create msg
msg = MIMEText(content, 'plain', 'utf-8')
msg['Subject'] = Header(subject, 'utf-8') # subject
msg['From'] = from_account
msg['To'] = to_account
email_client.sendmail(from_account, to_account, msg.as_string())

email_client.quit()

if __name__ == '__main__':
sendEmail()
# receiver = '***'
# send_email2(mail_host, mail_user, mail_pass, receiver, title, content)

发送附件

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
#!/usr/bin/env python3
#coding: utf-8

import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.image import MIMEImage

sender = '***'
receiver = '***'
subject = 'python email test'
smtpserver = 'smtp.163.com'
username = '***'
password = '***'

msgRoot = MIMEMultipart('related')
msgRoot['Subject'] = 'test message'

#构造附件
att = MIMEText(open('h:\\python\\1.jpg', 'rb').read(), 'base64', 'utf-8')
att["Content-Type"] = 'application/octet-stream'
att["Content-Disposition"] = 'attachment; filename="1.jpg"'
msgRoot.attach(att)

smtp = smtplib.SMTP()
smtp.connect('smtp.163.com')
smtp.login(username, password)
smtp.sendmail(sender, receiver, msgRoot.as_string())
smtp.quit()

带图片的HTML

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
#!/usr/bin/env python3
#coding: utf-8

import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.image import MIMEImage

sender = '***'
receiver = '***'
subject = 'python email test'
smtpserver = 'smtp.163.com'
username = '***'
password = '***'

msgRoot = MIMEMultipart('related')
msgRoot['Subject'] = 'test message'

msgText = MIMEText('<b>Some <i>HTML</i> text</b> and an image.<br><img src="cid:image1"><br>good!','html','utf-8')
msgRoot.attach(msgText)

fp = open('h:\\python\\1.jpg', 'rb')
msgImage = MIMEImage(fp.read())
fp.close()

msgImage.add_header('Content-ID', '<image1>')
msgRoot.attach(msgImage)

smtp = smtplib.SMTP()
smtp.connect('smtp.163.com')
smtp.login(username, password)
smtp.sendmail(sender, receiver, msgRoot.as_string())
smtp.quit()

HTML形式的邮件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#!/usr/bin/env python3
#coding: utf-8

import smtplib
from email.mime.text import MIMEText

sender = '***'
receiver = '***'
subject = 'python email test'
smtpserver = 'smtp.163.com'
username = '***'
password = '***'

msg = MIMEText('<html><h1>你好</h1></html>','html','utf-8')

msg['Subject'] = subject

smtp = smtplib.SMTP()
smtp.connect('smtp.163.com')
smtp.login(username, password)
smtp.sendmail(sender, receiver, msg.as_string())
smtp.quit()

FAQ

  • Question: ImportError,No module named header.
    1
    2
    3
    4
    5
    6
    7
    8
    Traceback (most recent call last):
    File "/home/i/Desktop/email.py", line 5, in <module>
    import smtplib
    File "/usr/lib/python2.7/smtplib.py", line 46, in <module>
    import email.utils
    File "/home/i/Desktop/email.py", line 6, in <module>
    from email.header import Header
    ImportError: No module named header
  • Answer: Do you have a file (or directory) called email.py in the directory where you are running the script? Or is your script even called email.py?
    please changing your script or directory’s name!!!
    The email is a python module!!!!
    Don’t use the name email as the name of the file!!!!

use command sendEmail

example

1
2
3
4
5
6
7
8
9
10
11
:~$ sendemail -f asdf@163.com -t asdf@189.cn -u "from sendemail" -m aaaaaaaaaaaaaaaaaaaaaaa -o tls=no -s smtp.163.com -o username=haven200 -o password=******
Jan 02 22:01:57 kali sendemail[2544]: Email was sent successfully!
# read content from file
~$ cat filename | sendemail -f @163.com -t @163.com -u "cat from file" -s smtp.163.com -xu haven200 -xp ****** -o message-charset=utf8
Reading message body from STDIN because the '-m' option was not used.
If you are manually typing in a message:
- First line must be received within 60 seconds.
- End manual input with a CTRL-D on its own line.

Jan 03 00:04:34 kali sendemail[4442]: Message input complete.
Jan 03 00:04:43 kali sendemail[4442]: Email was sent successfully!

Command

1
2
3
4
5
6
7
8
9
-f asdf@163.com                 发件人邮箱
-s smtp.163.com 发件人邮箱的smtp服务器
-u "邮件主题" 邮件的标题
-o message-content-type=html 邮件内容的格式,html表示它是html格式
-o message-charset=utf8 邮件内容编码
-xu asdf@163.com 发件人邮箱的用户名
-xp ****** 发件人邮箱授权码,非邮箱密码
-m "我是邮件内容" 邮件的具体内容
-o tls=no 关闭tls加密,以明文发送消息