网络编程
此篇文章简约的介绍一下Python中的网络编程,至于复杂的网络结构,通信原理暂不叙述。
网络
- 网络协议:一套规则
- 网络模型:
- 七层模型(理论)
- 物理层
- 数据链路层
- 网络层
- 传输层
- 会话层
- 表示层
- 应用层
- 四层模型(实际)
- 链路层
- 网络
- 传输层
- 应用层
- 七层模型(理论)
- 每一层都有相应的协议负责交换信息或者协同工作
- TCP/IP协议族
- IP地址:负责在网络上唯一定位一个机器
- IP地址分ABCDE类
- 是由四个数字段组成,每个数字段的取值是0-255
- 192.168.xxx.xxx:局域网ip
- 127.0.0.1:本机
- IPv4,IPv6
- IP地址:负责在网络上唯一定位一个机器
- 端口
- 范围:0-65535
- 知名端口:0-1023
- 非知名端口:1024-65535
- 范围:0-65535
- TCP/UDP协议
- UDP:非安全的不面向链接的传输
- 安全性差
- 大小限制64kb
- 大文件会以64kb为单位切割,没有顺序
- 速度快
- TCP:基于链接的通信
- 特点与上述UDP相反
- UDP:非安全的不面向链接的传输
套接字:通信端点(socket)
- socket(套接字):是一个网络通信的端点,能实现不同主机的进程通信
- 通过IP+端口定位对方并发送消息的通信机制
- 分为面向连接的(TCP)和无连接的(UDP)
- 客户端Client:发起访问的一方
- 服务器端Server:接收访问的一方(这种描述不是准确的,但是可以这么去理解)
Python中的网络编程
socket()模块函数
- 要创建套接字,必须使用socket.socket()函数,一般语法如下:
socket(socket_family, socket_type, protocol=0)
其中,socket_family是AF_UNIX或AF_INET,socket_type是SOCK_STREAM或SOCK_DGRAM;protocol通常省略,默认是0。 - 创建TCP/IP套接字:
tcpSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- 创建UDP/IP套接字:
tcpSock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
- 由于socket模块有很多属性,因此可以使用
from socket import *
调入,并且把socket引入命名空间,缩近代码长度。
套接字对象(内置)方法
名称 | 描述 |
---|---|
s.bind() | (服务器)将地址(主机名、端口号对)绑定到套接字上 |
s.listen() | (服务器)设置并启动TCP监听器 |
s.accept() | (服务器)被动接受TCP客户端连接,一直等待直到连接到达(阻塞) |
s.connect() | (客户端)主动发起TCP服务器连接 |
s.connect_ex() | (客户端)connect()的扩张版本,此时会以错误码的形式返回问题,而不是抛出异常 |
s.recv() | (普通)接收TCP消息 |
s.recv_into() | (普通)接收TCP消息到指定的缓冲区 |
s.send() | (普通)发送TCP消息 |
s.sendall() | (普通)完整地发送TCP消息 |
s.recvfrom() | (普通)接收UDP消息 |
s.recvfrom_into() | (普通)接收UDP消息到指定的缓冲区 |
s.sendto() | (普通)发送UDP消息 |
s.getpeername() | (普通)连接到套接字(TCP)的远程地址 |
s.getsockname() | (普通)当前套接字的地址 |
s.getsockopt | (普通)返回给定套接字选项的值 |
s.setsockopt() | (普通)设置给定套接字选项的值 |
s.shutdown() | (普通)关闭连接 |
s.close() | (普通)关闭套接字 |
s.detach() | (普通)在未关闭文件描述符的情况下关闭套接字,返回文件描述符 |
s.ioctl() | (普通)控制套接字的模式 |
s.setblocking() | (面对阻塞)设置套接字的阻塞或非阻塞模式 |
s.settimeout() | (面对阻塞)设置阻塞套接字操作的超时时间 |
s.gettimeout() | (面对阻塞)获取阻塞套接字操作的超时时间 |
s.fileno() | (面向文件)套接字的文件描述符 |
s.makefile() | (面向文件)创建与套接字关联的文件对象 |
s.family() | (数据属性)套接字家族 |
s.type() | (数据属性)套接字类型 |
s.proto() | (数据属性)套接字协议 |
TCP服务器/客户端
- 面向链接的传输,即每次传输之前需要先建立一个链接
- 客户端和服务器端两个程序需要编写
- Server端的编写流程
- 建立socket负责具体通信,这个socket其实只是负责接收对方的请求
- 绑定端口和地址
- 监听接入的访问socket
- 接收访问的socket,可以理解接收访问即建立了一个通讯的链接通路
- 接收对方的发送内容,利用接收到的socket接收内容
- 如果有必要,给对方发送反馈信息
- 关闭链接通路
import socket
def tcp_srv():
# 建立socket负责具体通信,这个socket其实只负责接受对方的请求,真正通信的是链接后
# 需要用到两个参数,AF_INET:含义同udp一样;SOCK_STREAM:表明使用的是tcp进行通信
sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
# 绑定端口和地址
# 此地址是一个元组类型的内容,元组分为两部分,第一部分为字符串,代表ip,第二部分为端口
addr = ("127.0.0.1",8998)
sock.bind(addr)
# 监听接入的访问socket
sock.listen()
while True:
# 接受访问的socket,可以理解为接受访问即建立了一个通讯的链接通路
# accept返回的元组第一个元素赋值给skt,第二个赋值给addr
skt,addr = sock.accept()
# 接受对方的发送内容,利用接受到的socket接受内容
# 500代表接受使用的buffersize
msg = skt.recv(500)
# 接受到的是bytes格式内容,想得到str格式,需要进行解码
msg = msg.decode()
rst = "Received msg: {0} from {1}".format(msg,addr)
print(rst)
# 如果有必要,给对方发送反馈信息
skt.send(rst.encode())
# 关闭链接通路
skt.close()
if __name__ == "__main__":
print("Starting tcp server.......")
tcp_srv()
print("Ending tcp server......")
- Client端流程
- 建立通信socket
- 链接对方,请求跟对方建立通路
- 发送内容到对方服务器
- 接收对方的反馈
- 关闭链接通路
import socket
def tcp_clt():
# 建立通信socket
sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
# 链接对方,请求跟对方建立通信
addr = ("127.0.0.1",8998)
sock.connect(addr)
# 发送内容到对方服务器
msg = "I jdksj sa asldk "
sock.send(msg.encode())
# 接受对方的反馈
rst = sock.recv(500)
# 关闭链路通路
sock.close()
if __name__ == "__main__":
tcp_clt()
UDP服务器/客户端
-
Server端流程
- 建立socket,socket是负责具体通信的一个实例
- 绑定,为创建的socket指派固定的端口和ip地址
- 接受对方发送的内容
- 给对方发送反馈,此步骤为非必须步骤
import socket # 模拟服务器的函数 def serverFunc(): # 建立socket # socket.AF_INET:使用IPv4协议族 # socket.SOCK_DGRAM:使用UDP通信 sock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # 绑定ip和port # 127.0.0.1:这个ip地址代表的是本机地址 # 7749:随手指定的端口号 # 地址是一个tuple类型,(ip,port) addr = ("127.0.0.1",7749) sock.bind(addr) # 接受对方消息 # 等待方式为死等,没有其他的可能性 # recvfrom接受的返回值是一个tuple,前一项表示数据,后一项表示地址 # 参数的含义是缓冲区大小 # rst = sock.recvfrom(500) data,addr = sock.recvfrom(500) print(data) print(type(data)) # 发送过来的数据是bytes格式,必须通过解码才能得到str格式内容 # decode默认参数是utf-8 text = data.decode() print(type(text)) print(text) # 给对方返回消息 rsp = "Ich hab Keine Hunge" # 发送的数据需要编码成bytes格式 # 默认是utf-8 data = rsp.encode() sock.sendto(data,addr) if __name__ == "__main__": print("Starting server........") serverFunc() print("Ending server.......")
-
Client端流程
- 建立通信的socket
- 发送内容到指定服务器
- 接受服务器给定的反馈内容
import socket # 客户端函数 def clientFunc(): sock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # 需要发送的文本信息 text = "i ssdka skdlask!" # 发送的数据必须是bytes格式 data = text.decode() # 发送 sock.sendto(data,("127.0.0.1",7749) data,addr = sock.recvfrom(200) data = data.decode() print(data) if __name__ == "__main__": clientFunc()
-
服务器程序要求永久运行,一般用死循环处理,将主程序进行修改
import time if __name__ == "__main__": print("Starting server........") while True: try: serverFunc() except Exception as e: print(e) time.sleep(1) print("Ending server.......")
FTP协议
-
FTP(FileTransferProtocal)文件传输协议
-
用途:定制一些特殊的上传下载文件的服务
-
用户分类:登录FTP服务器必须有一个账号
- Real账户:注册账户
- Guest账户:可能临时对某一类人的行为进行授权
- Anonymous账户:匿名账户,允许任何人
-
FTP工作流程
- 客户端链接远程主机上的FTP服务器
- 客户端输入用户名和密码(或者“anonymous”和电子邮件地址)
- 客户端和服务器进行各种文件传输和信息查询操作
- 客户端从远程FTP服务器推出,结束传输
-
FTP文件表示
- 分三段表示FTP服务器上的文件
- HOST:主机地址,类似于 ftp.mozilla.org,以ftp开头
- DIR:目录,表示文件所在本地的路径,例如pub/android/focus/klar-1.1-RC1.apk
- File:文件名称,例如klar-1.1-RC1.apk
- 如果完整精确表示ftp上某一个文件,需要上述三部分组合在一起
Python与FTP
- 回顾FTP的流程:连接服务器 -> 登录 -> 发出服务请求(希望得到响应) -> 退出
- 在使用Python的FTP支持时,所需要做的只是导入ftplib模块,并实例化一个ftplib.FTP类对象。所有的FTP操作都要使用这个对象完成。
ftplib.FTP类的方法
方法 | 描述 |
---|---|
login(user=‘anonymous’, passwd=", acct=") | 登录FTP服务器,所有参数都是可选的 |
pwd() | 获得当前工作目录 |
cwd(path) | 把当前工作目录设置为path所示的路径 |
dir ([path[,…[,cb]]) | 显示path目录里的内容,可选的参数cb是一个回调函数,会床底给retrlines()方法 |
nlst ([path[,…]]) | 与dir()类似,但返回一个文件名列表,而不是显示这些文件名 |
retrlins(cmd[,cb]) | 给定FTP命令(如"RETR filename"),用于下载文件。可选的回调函数cb用于处理文件的每一行 |
retrbinary(cmd, cb[,bs=8192[,ra]]) | 与retrlines()类似,只是这个指令处理二进制文件。回调函数cb用于处理每一块(块大小默认为8KB)下载的数据 |
storlines(cmd,f) | 给定FTP命令(如"STOR filename"),用来上传文本文件。要给定一个文件对象f |
storbinary(cmd, f[,bs=8192]) | 与storlines()类似,只是这个指令处理二进制文件。要给定一个文件对象f,上传块大小bs默认为8KB) |
rename(old, new) | 远程重命名 |
delete(path) | 删除位于path的远程文件 |
mkd(directory) | 创建远程目录 |
rmd(directory) | 删除远程目录 |
quit() | 关闭连接并退出 |
客户端FTP程序
# 需要导入相对应的包,主要是ftplib
# 关于FTP的操作都在这个包里面
import ftplib
import os
import socket
# 三部分精确表示在ftp服务器上的某一个文件
# 好多公开ftp服务器访问会出错或者没有反应
HOST = "ftp.acc.umu.se"
DIR = 'Public/EFLIB'
FILE = 'README'
# 客户端链接远程主机上的FTP服务器
try:
f = ftplib.FTP()
# 通过设置调试级别可以方便调试
f.set_debuglevel(2)
# 链接本地地址
f.connect(HOST)
except Exception as e:
print(e)
exit()
print("*** Connected to host {0}".format(HOST))
# 客户端和服务器进行各种文件传输和信息查询操作
try:
# 更改当前目录到指定目录
f.cwd(DIR)
except Exception as e:
print(e)
exit()
print("*** Changed dir to {0}".format(DIR))
try:
'''
从FTP服务器上下载文件
第一个参数是ftp命令
第二个参数是回调函数
此函数的意思是:执行RETR命令,下载文件到本地后,运行回调函数
'''
f.retrbinary('RETR {0}'.format(FILE),open(FILE,"wb").write)
except Exception as e:
print(e)
exit()
# 客户端从远程FTP服务器退出,结束传输
f.quit()
网络新闻
网络新闻协议(NNTP)
- 作为客户端/服务器架构的另一个例子,NNTP和FTP的操作方式类似。FTP中,登录、传输数据和控制需要使用不同的端口。NNTP只使用一个标准端口119来通信。
Python和NNTP
- 导入nntplib库,并实例化一个nttplib.NNTP类。
- 协议:
- 连接到服务器
- 登录(根据需要)
- 发出服务请求
- 退出
- 协议内容跟FTP协议极其类似,但是唯一的区别就是NNTP服务器的登录,这一步是可选的。
nntplib.NNTP类方法
方法 | 描述 |
---|---|
group(name) | 选择一个组的名字,返回一个元组(rsp, ct, fst, lst, group),分别表示服务器响应信息、文章数量、第一个和最后一个文章的编号、组名,所有数据都是字符串。(返回的group与传进去的name应该是相同的) |
xhdr(hdr, artrg[, ofile]) | 返回文章范围artrg(“头-尾”的格式)内文章hdr头的列表,或者把数据输出到文件ofile中 |
body(id[, ofile]) | 根据id获取文章正文,id可以是消息的ID(放在尖括号里),也可以是文章编号(以字符串形式表示),返回一个元组(rsp,anum,mid,data),分别表示服务器响应信息,文章编号(以字符串形式表示)、消息ID(放在尖括号里)、文章所有行的列表,或把数据输出到文件ofile中 |
head(id) | 与 body()类似,返回相同的元组,只是返回的行列表中只包括文章标题 |
article(id) | 同样与 body()类似,返回相同的元组,只是返回的行列表中同时包括文章标题和正文 |
stat(id) | 让文章的“指针”指向 id(即前面的消息 ID 或文章编号)。返回一个与 body()相同的元组(rsp, anum, mid),但不包含文章的数据 |
next() | 用法和 stat()类似,把文章指针移到下一篇文章,返回与 stat()相似的元组 |
last() | 用法和 stat()类似,把文章指针移到最后一篇文章,返回与 stat()相似的元组 |
post(ufile) | 上传 ufile文件对象里的内容(使用 ufile.readline()),并发布到当前新闻组中 |
quit() | 关闭连接并退出 |
客户端程序示例
import nntplib
import socket
HOST = "your.nntp.server"
GRNM = "comp.lang.python"
USER = "myuser"
PASS = "youpassword"
def main():
try:
n = nntplib.NNTP(HOST)
except socket.gaierror as e:
print("ERROR: cannot reach host '%s' " %HOST)
print("('%s')" % eval(str(e))[1])
return
except nntplib.NNTPPermanentError as e:
print("ERROR: access denied on '%s' " %HOST)
print("('%s')" % str(e))
return
print(" *** Connected to host %s" % GRNM)
try:
rsp, ct, fst, lst, grp = n.group(GRNM)
except nntplib.NNTPTemporaryError as ee:
print("ERROR: cannot load group '%s'" % GRNM)
print("(%s)" % str(e))
print("Server may require authentication")
print("Uncomment/edit login line above")
n.quit()
return
except nntplib.NNTPTemporaryError as ee:
print("ERROR: group '%s' unavailable" % GRNM)
print('("%s")' % str(e))
n.quit()
return
print(" *** Found newsgroup '%s'" % GRNM)
rng = '%s-%s' % (lst, lst)
rsp, frm = n.xhdr('from', rng)
rsp, sub = n.xhdr('subject', rng)
rsp, dat = n.xhdr('date', rng)
print(" *** Found last article (#%s): From: %s \n Subject: %s \n Date: %s \n " % (lst, from[0][1], sub[0][1], dat[0][1])
rsp, anum, mid, data = n.body(lst)
displayFirst20(data)
n.quit()
def displayFirst20(data):
count = 0
lines = (line.rstrip() for line in data)
lastBlank = True
for line in lines:
if line:
lower = line.lower()
if (lower.startswith('>') and not \
lower.startswith('>>>') or \
lower.startswith('|') or \
lower.startswith('in article') or \
lower.endswith('writes:') or \
lower.endswith('wrote:'):
continue
if not lastBlank or (lastBlank and line):
print('%s' % line)
if line:
count += 1
lastBlank = False
else:
lastBlank = True
if count == 20:
break
if __name__ == "__main__":
main()
Mail编程
电子邮件的历史
- 起源
- 1969 Leonard K.教授发给同事的“LO”
- 1971 美国国防部自主研发的阿帕网(Arpanet)的通讯机制
- 通讯地址用@
- 1987年中国的第一封电子邮件 “Across the Great Wall we can reach every corner in the world.”
- 管理程序
- Euroda使邮件普及
- Netscape,outlook,forxmail后来居上
- Hotmail 使用浏览器发送邮件
- 参考资料
- [官网] (https://docs.python.org/3/library/email.mime.html)
邮件工作流程
- MUA(MailUserAgent) 邮件用户代理
- MTA(MailTransferAgent)邮件传输代理
- MDA(MailDeliveryAgent)邮件投递代理
- laoshi@qq.com,老师,安徽合肥
- xuesheng@sina.com,学生,广东中山
- 流程
- MUA -> MTA,邮件已经在服务器上了
- qq MTA -> …-> sina MTA,邮件在新浪的服务器上
- sina MTA -> sina MDA,此时邮件已经在你邮箱里了
- sina MDA -> MUA(Foxmail/Outlook),邮件下载到本地电脑
- 编写程序
- 发送:MUA -> MTA with SMTP:SimpleMainTransferProtocal,包含MTA -> MTA
- 接收:MDA -> MUA with POP3 and IMAP: PostOfficeProtocal v3
- 准备工作
- 注册邮箱
- 第三方邮箱需要特殊设置
- 进入设置中心
- 取得授权码
Python和SMTP
- Python中也有一个 smtplib 模块和一个需要实例化的 smtplib.SMTP 类。
- 过程:
- 连接到服务器。
- 登录(根据需要)
- 发出服务请求。
- 退出。
- 像NNTP一样,登录是可选的,只有在服务器启动了SMTP(SMTP-AUTH)身份验证时才要登录。SMTP-AUTH在RFC 2554中定义。SMTP通信时也需要一个端口。
smtplib.SMTP类方法
方法 | 描述 |
---|---|
sendmail(from, to, msg[, mopts, ropts]) | 将msg从from发送到至to(以列表或元组表示),还可以选择性地设置ESMTP邮件(mopts)和收件人(ropts)选项 |
ehlo()或helo() | 使用EHLO或HELO初始化SMTP或ESMTP服务器的会话。这是可选的,因为sendmail()会在需要时自动调用相关内容 |
starttls(keyfile=None, certfile=None) | 让服务器启用 TLS 模式。如果给定了 keyfile 或 certfile,则它们用来创建安全套接字 |
set_debuglevel(level) | 为服务器通信设置调试级别 |
quit() | 关闭连接并退出 |
login(user, passwd) | 使用用户名和密码登录 SMTP 服务器 (只用于SMTP-AUTH) |
- Python for mail
- SMTP协议负责发送邮件
-
使用email模块构建邮件
- 纯文本邮件
import smtplib from email.mime.text import MIMEText """ MIMEText三个主要参数: 邮件内容 MIME子类型,在此案例使用plain表示text类型 邮件编码格式 """ msg = MIMEText("Hello, i am xxx","plain","utf-8") # 发送Email地址,此处地址直接使用qq邮箱,密码一般要临时输入 from_addr = "1235646466@qq.com" # 此处密码使经过申请设置后的授权码,并不是邮箱密码 from_pwd = "sdasdjsajdfkf" # 收件人信息 # 此处使用qq邮箱,给自己发送 to_addr = "1235646466@qq.com" # 输入SMTP服务器地址 # 此处根据不同的邮件服务器有不同的值 # 现在基本任何一家邮件服务商,如果采用第三方发邮件,都需要开启授权选项 # 腾讯qq邮件的SMTP地址是 smtp.qq.com smtp_srv = "smtp.qq.com" try: srv = smtplib.SMTP_SSL(smtp_srv.encode(),465) #smtp默认端口25 # 登录邮箱发送 srv.login(from_addr,from_pwd) """ 发送邮件,三个参数 发送地址 接收地址,必须是list形式 发送内容,作为字符串发送 """ srv.sendmail(from_addr,[to_addr],msg.as_string()) srv.quit() except Exception as e: print(e)
-
HTML格式邮件发送
- 准备HTML代码作为内容
- 把邮件的subtype设为html
- 发送
from email.mime.text import MIMEText mail_content = """ <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>Title</title> </head> <body> <h1> 这是一封HTML格式邮件</h1> </body> </html> """ msg = MIMEText(mail_content,"html","utf-8") # 构建发送者地址和登录信息 from_addr = "2467930184@qq.com" from_pwd = "sadjkasjdkasja" # 构建邮件接收者信息 to_addr = "2467930184@qq.com" smtp_srv = "smtp.qq.com" try: import smtplib srv = smtplib.SMTP_SSL(smtp_srv.encode(),465) srv.login(from_addr,from_pwd) srv.sendmail(from_addr,[to_addr],msg.as_string()) srv.quit() except Exception as e: print(e)
-
发送带有附件的邮件
- 可以把邮件看作是一个文本邮件和一个附件的合体
- 一封邮件如果涉及多个部分,需要使用MIMEMultipart格式创建
- 添加一个MIMEText正文
- 添加一个MIMEBase或者MIMEText作为附件
from email.mime.text import MIMEText from email.mime.multipart import MIMEBase,MIMEMultipart mail_mul = MIMEMultipart() # 构建邮件正文 mail_text = MIMEText("Hello, i am adkjks","plain","utf-8") # 把构建好的邮件正文附加入邮件中 mail_mul.attach(mail_text) # 构建附加 # 构建附件,需要从文本读入附件 # 打开一个本地文件 # 以rb格式打开 with open("02.html","rb") as f: s = f.read() # 设置组件的MIME和文件名 m = MIMEText(s,"base64","utf-8") m["Content-Type"] = "application/octet-stream" # 需要注意: # attachment后面的分号是英文状态 # filename 后面需要引号包裹,注意与外面引号错开 m["Content-Disposition"] = "attachment;filename='02.html" # 添加到MIMEMulipart mail_mul.attach(m) # 发送email地址,此处地址直接使用qq邮箱 from_addr = "2467930184@qq.com" # 此处密码使经过申请设置后的授权码,并不是邮箱密码 from_pwd = "sdasdjsajdfkf" # 收件人信息 # 此处使用qq邮箱,给自己发送 to_addr = "2467930184@qq.com" smtp_srv = "smtp.qq.com" try: srv = smtplib.SMTP_SSL(smtp_srv.encode(),465) #smtp默认端口25 # 登录邮箱发送 srv.login(from_addr,from_pwd) """ 发送邮件,三个参数 发送地址 接收地址,必须是list形式 发送内容,作为字符串发送 """ srv.sendmail(from_addr,[to_addr],msg.as_string()) srv.quit() except Exception as e: print(e)
-
添加邮件头,抄送等信息
- mail[“From”] 表示发送者信息,包括姓名和邮件
- mail[“To”] 表示接收者信息,包括姓名和邮件地址
- mail[“Subject”] 表示摘要或者主题信息
from email.mime.text import MIMEText from email.header import Header msg = MIMEText("Hello world","plain","utf-8") # 填写发送者信息 header_from = Header("这里是发送者邮箱<2467930184@qq.com>","utf-8") msg['From'] = header_from # 填写接收者信息 header_to = Header('这里是接收者邮箱<2467930184@qq.com>','utf-8') msg['to'] = header_to header_sub = Header("这是个演示",'utf-8') msg['Subject'] = header_sub # 构建发送者地址和登录信息 from_addr = "2467930184@qq.com" from_pwd = "sadjkasjdkasja" # 构建邮件接收者信息 to_addr = "2467930184@qq.com" smtp_srv = "smtp.qq.com" ...
-
同时支持html和text格式
- 构建一个MIMEMultipart格式邮件
- MIMEMultipart的subtype设置成alternative格式
- 添加Html和Text邮件
from email.mime.text import MIMEText from email.mime.multipart import MIMEMultipart # 构建一个MIMEMultipart邮件 msg = MIMEMultipart("alternative") # 构建一个HTML邮件内容 html_content = """ <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>这是一封HTML格式邮件</h1> </body> </html> """ msg_html = MIMEText(mail_content,"html","utf-8") msg.attach(msg_html) msg_text = MIMEText("Hello, i am xxx","plain","utf-8") msg.attach(msg_text) #后面都一样 ...
-
使用smtplib模块发送邮件
-
- POP3协议接收邮件
- 本质上是MDA到MUA的一个过程
- 从MDA下载下来的是一个完整的邮件结构体,需要解析才能得到每个具体邮件信息
- 用poplib下载邮件结构体原始内容
- 准备相应的内容(邮件地址,密码,POP3实例)
- 身份认证
- 一般会先得到邮箱内邮件的整体列表
- 根据相应序号,得到某一个邮件的数据流
- 利用解析函数进行解析出相应的邮件结构体
- 用email解析邮件的具体内容
- 用poplib下载邮件结构体原始内容
- SMTP协议负责发送邮件