13.python网络编程

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/zhizunmimi/article/details/83851893
# 网络编程
- 网络:
- 网络协议: 一套规则
- 网络模型:
    - 七层模型-七层
        - 物理层
        - 数据链路层
        - 网络层
        - 传输层
        - 会话层
        - 表示层
        - 应用层
    - 四层模型-实际应用
        - 链路层
        - 网络
        - 传输层
        - 应用层
        
- 每一层都有相应的协议负责交换信息或者协同工作
- TCP/IP 协议族
- IP地址:负责在网络上唯一定位一个机器
    - IP地址分ABCDE类
    - 是由四个数字段组成,每个数字段的取值是0-255
    - 192.168.xxx.xxx:局域网ip
    - 127.0.0.1:本机
    - IPv4, IPv6
    
- 端口
    - 范围: 0-65535
        - 知名端口:0-1023
        - 非知名端口:1024-
        
# TCP/UDP协议
- UDP:非安全的不面向链接的传输
    - 安全性差
    - 大小限制64kb
    - 没有顺序
    - 速度快
- TCP
    - 基于链接的通信
    
- SOCKET编程
    - socket(套接字): 是一个网络通信的端点, 能实现不同主机的进程通信,网络大多基于Socket通信
    - 通过IP+端口定位对方并发送消息的通信机制
    - 分为UDP和TCP
    - 客户端Client: 发起访问的一方
    - 服务器端Server:接受访问的一方
- UDP 编程
    - Server端流程
            1. 建立socket,socket是负责具体通信的一个实例
            2. 绑定,为创建的socket指派固定的端口和ip地址
            3. 接受对方发送内容
            4. 给对方发送反馈,此步骤为非必须步骤
    - Client端流程
            1. 建立通信的socket
            2. 发送内容到指定服务器
            3. 接受服务器给定的反馈内容
    - 服务器案例v01
'''
Server端流程
 1. 建立socket,socket是负责具体通信的一个实例
 2. 绑定,为创建的socket指派固定的端口和ip地址
 3. 接受对方发送内容
 4. 给对方发送反馈,此步骤为非必须步骤
'''


# socket模块负责socket编程
import socket

# 模拟服务器的函数
def serverFunc():
    # 1. 建立socket

    # socket.AF_INET:使用ipv4协议族
    # socket.SOCK_DGRAM: 使用UDP通信
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

    # 2. 绑定ip和port
    # 127.0.0.1: 这个ip地址代表的是机器本身
    # 7852: 随手指定的端口号
    # 地址是一个tuple类型,(ip, port)
    addr = ("127.0.0.1", 7852 )
    sock.bind( addr )


    # 接受对方消息
    # 等待方式为死等, 没有其他可能性
    # recvfrom接受的返回值是一个tuple,前一项表示数据,后一项表示地址
    # 参数的含义是缓冲区大小
    # rst = sock.recvfrom(500)
    data, addr = sock.recvfrom(500)

    print(data)
    print(type(data))

    # 发送过来的数据是bytes格式,必须通过解码才能得到str格式内容
    # decode默认参数是utf8
    text = data.decode()
    print(type(text))
    print(text)


    # 给对方返回的消息
    rsp = "Ich hab keine Hunge"

    # 发送的数据需要编码成bytes格式
    # 默认是utf8
    data = rsp.encode()
    sock.sendto(data, addr)


if __name__ == '__main__':
    print("Starting server.........")
    serverFunc()
    print("Ending server........")

    - 客户端案例v02
import socket

'''
- Client端流程
            1. 建立通信的socket
            2. 发送内容到指定服务器
            3. 接受服务器给定的反馈内容
'''


def clientFunc():

    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)


    text = "I love jingjing"

    # 发送的数据必须是bytes格式
    data = text.encode()

    # 发送
    sock.sendto(data,  ("127.0.0.1", 7852))


    data, addr  = sock.recvfrom(200)

    data = data.decode()

    print(data)


if __name__ == '__main__':
    clientFunc()


    - 服务器程序要求永久运行,一般用死循环处理
    - 改造的服务器版本v03
    


# socket模块负责socket编程
import socket

# 模拟服务器的函数
def serverFunc():
    # 1. 建立socket

    # socket.AF_INET:使用ipv4协议族
    # socket.SOCK_DGRAM: 使用UDP通信
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

    # 2. 绑定ip和port
    # 127.0.0.1: 这个ip地址代表的是机器本身
    # 7852: 随手指定的端口号
    # 地址是一个tuple类型,(ip, port)
    addr = ("127.0.0.1", 7852 )
    sock.bind( addr )


    # 接受对方消息
    # 等待方式为死等, 没有其他可能性
    # recvfrom接受的返回值是一个tuple,前一项表示数据,后一项表示地址
    # 参数的含义是缓冲区大小
    # rst = sock.recvfrom(500)
    data, addr = sock.recvfrom(500)

    print(data)
    print(type(data))

    # 发送过来的数据是bytes格式,必须通过解码才能得到str格式内容
    # decode默认参数是utf8
    text = data.decode()
    print(type(text))
    print(text)


    # 给对方返回的消息
    rsp = "Ich hab keine Hunge"

    # 发送的数据需要编码成bytes格式
    # 默认是utf8
    data = rsp.encode()
    sock.sendto(data, addr)


if __name__ == '__main__':
    import time
    while 1:
        try:
            serverFunc()
        except Exception as e:
            print(e)

        time.sleep(1)




- TCP编程
    - 面向链接的传输,即每次传输之前需要先建立一个链接
     - 客户端和服务器端两个程序需要编写
     - Server端的编写流程
         1. 建立socket负责具体通信,这个socket其实只负责接受对方的请求,真正通信的是链接后从新建立的socket
         2. 绑定端口和地址
         3. 监听接入的访问socket
         4. 接受访问的socket,可以理解接受访问即建立了一个通讯的链接通路
         5. 接受对方的发送内容,利用接收到的socket接收内容
         6. 如果有必要,给对方发送反馈信息
         7. 关闭链接通路
     - Client端流程
          1. 建立通信socket
          2. 链接对方,请求跟对方建立通路
          3. 发送内容到对方服务器
          4. 接受对方的反馈
          5. 关闭链接通路
     - 案例v04,
import socket


def  tcp_srv():
    # 1. 建立socket负责具体通信,这个socket其实只负责接受对方的请求,真正通信的是链接后从新建立的socket
    # 需要用到两个参数
    # AF_INET: 含义同udp一致
    # SOCK_STREAM: 表明是使用的tcp进行通信
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # 2. 绑定端口和地址
    # 此地址信息是一个元祖类型内容,元祖分两部分,第一部分为字符串,代表ip,第二部分为端口,是一个整数,推荐大于10000
    addr = ("127.0.0.1", 8998)
    sock.bind(addr)
    # 3. 监听接入的访问socket
    sock.listen()

    while True:
        # 4. 接受访问的socket,可以理解接受访问即建立了一个通讯的链接通路
        # accept返回的元祖第一个元素赋值给skt,第二个赋值给addr
        skt,addr = sock.accept()
        # 5. 接受对方的发送内容,利用接收到的socket接收内容
        # 500代表接收使用的buffersize
        #msg = skt.receive(500)
        msg = skt.recv(500)
        # 接受到的是bytes格式内容
        # 想得到str格式的,需要进行解码
        msg = msg.decode()

        rst = "Received msg: {0} from {1}".format(msg, addr)
        print(rst)
        # 6. 如果有必要,给对方发送反馈信息
        skt.send(rst.encode())

        # 7. 关闭链接通路
        skt.close()


if __name__ == "__main__":
    print("Starting tcp server.......")
    tcp_srv()
    print("Ending tcp server.......")
 v05
     
import socket

def tcp_clt():
    # 1. 建立通信socket
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # 2. 链接对方,请求跟对方建立通路
    addr = ("127.0.0.1", 8998)
    sock.connect(addr)
    # 3. 发送内容到对方服务器
    msg = "I love wangxiaojing"
    sock.send(msg.encode())
    # 4. 接受对方的反馈
    rst =  sock.recv(500)
    print(rst.decode())
    # 5. 关闭链接通路
    sock.close()


if __name__ == "__main__":
    tcp_clt()

# FTP编程
- FTP(FileTransferProtocal)文件传输协议
- 用途: 定制一些特殊的上传下载文件的服务
- 用户分类: 登陆FTP服务器必须有一个账号
    - Real账户: 注册账户
    - Guest账户: 可能临时对某一类人的行为进行授权
    - Anonymous账户: 匿名账户,允许任何人
- FTP工作流程
    1. 客户端链接远程主机上的FTP服务器
    2. 客户端输入用户名和密码(或者“anonymous”和电子邮件地址)
    3. 客户端和服务器进行各种文件传输和信息查询操作
    4. 客户端从远程FTP服务器退出,结束传输   
    
- FTP文件表示
    - 分三段表示FTP服务器上的文件
    - HOST: 主机地址,类似于 ftp.mozilla.org, 以ftp开头
    - DIR:目录, 表示文件所在本地的路径,例如 pub/android/focus/1.1-RC1/ 
    - File: 文件名称, 例如 Klar-1.1-RC1.apk
    - 如果想完整精确表示ftp上某一个文件,需要上述三部分组合在一起
    - 案例v06
    
# 需要导入相应包,主要是ftplib
import ftplib # 关于FTP的操作都在这个包里边
import os
import socket

# 三部分精确表示在ftp服务器上的某一个文件
# 好多公开ftp服务器访问会出错或者没有反应
HOST = "ftp.acc.umu.se"
DIR = 'Public/EFLIB/'
FILE = 'README'

# 1. 客户端链接远程主机上的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))



# 2. 客户端输入用户名和密码(或者“anonymous”和电子邮件地址)
try:
    # 登录如果没有输入用户信息,则默认使用匿名登录
    f.login()
except Exception as e:
    print(e)
    exit()
print("***Logged in as 'anonymous'")


# 3. 客户端和服务器进行各种文件传输和信息查询操作
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()

# 4. 客户端从远程FTP服务器退出,结束传输
f.quit()

# Mail编程
## 电子邮件的历史
- 起源
    - 1969 Leonard K. 教授发给同时的 “LO”
    - 1971 美国国防部自主的阿帕网(Arpanet)的通讯机制
    - 通讯地址里用@, 
    - 1987年中国的第一份电子邮件  
    “Across the Great Wall we can reach every corner in the world"

- 管理程序
    - Euroda使邮件普及 
    - Netscape,outlook,forxmail后来居上
    - Hotmal使用浏览器发送邮件i
- 参考资料
    - [官网](https://docs.python.org/3/library/email.mime.html)
    
## 邮件工作流程
- MUA(MailUserAgent)邮件用户代理
- MTA(MailTransferAgent)邮件传输代理
- MDA(MailDeliveryAgent)邮件投递代理
- laoshi@qq.com, 老师,北京海淀
- xuesheng@sina.com, 学生,上海江岸区
- 流程
    1. MUA->MTA, 邮件已经在服务器上了
    2. qq MTA->.........->sina MTA, 邮件在新浪的服务器上
    3. sina MTA-> sina MDA, 此时邮件已经在你的邮箱里了
    4. sina MDA -> MUA(Foxmail/Outlook), 邮件下载到本地电脑
   
- 编写程序
    - 发送:  MUA->MTA with SMTP:SimpleMailTransferProtocal,包含MTA->MTA
    - 接受:  MDA->MUA with POP3 and IMAP:PostOfficeProtocal v3 and  InternetMessageAccessProtocal v4
    
- 准备工作
    - 注册邮箱(以qq邮箱为例)
    - 第三方邮箱需要特殊设置, 以qq邮箱为例
        - 进入设置中心
        - 取得授权码
        
- Python for mail
    - SMTP协议负责发送邮件
        - 使用email模块构建邮件
            - 纯文本邮件
            - 案例v07
# 导入相应的包
import smtplib
from email.mime.text import MIMEText
# MIMEText三个主要参数
# 1. 邮件内容
# 2. MIME子类型,在此案例我们用plain表示text类型
# 3. 邮件编码格式

msg = MIMEText("Hello, i am beijing tulingxueyuan ", "plain", "utf-8")

# 发送email地址,此处地址直接使用我的qq有偶像,密码一般需要临时输入,此处偷懒
from_addr = "1366798119@qq.com"
# 此处密码是经过申请设置后的授权码,不是不是不是你的qq邮箱密码
from_pwd = "hjpovygcxmrshhcj"

# 收件人信息
# 此处使用qq邮箱,我给自己发送
to_addr = "1366798119@qq.com"


# 输入SMTP服务器地址
# 此处根据不同的邮件服务商有不同的值,
# 现在基本任何一家邮件服务商,如果采用第三方收发邮件,都需要开启授权选项
# 腾讯qq邮箱所的smtp地址是 smtp.qq.com

smtp_srv = "smtp.qq.com"

try:
    # 两个参数
    # 第一个是服务器地址,但一定是bytes格式,所以需要编码
    # 第二个参数是服务器的接受访问端口
    srv = smtplib.SMTP_SSL(smtp_srv.encode(), 465) #SMTP协议默认端口25
    #登录邮箱发送
    srv.login(from_addr, from_pwd)
    # 发送邮件
    # 三个参数
    # 1. 发送地址
    # 2. 接受地址,必须是list形式
    # 3. 发送内容,作为字符串发送
    srv.sendmail(from_addr, [to_addr], msg.as_string())
    srv.quit()
except Exception as e:
    print(e)

        - HTML格式邮件发送
            - 准备HTML代码作为内容
            - 把邮件的subtpye设为html
            - 发送
            - 案例v08
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 = "1366798119@qq.com"
from_pwd = "hjpovygcxmrshhcj"


# 构建邮件接受者信息
to_addr = "1366798119@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或者MEMEText作为附件
            - 案例v09
            
from email.mime.text import MIMEText #构建附件使用
from email.mime.multipart import MIMEBase, MIMEMultipart # 构建基础邮件使用


mail_mul = MIMEMultipart()
# 构建邮件正文
mail_text = MIMEText("Hello, i am liudana", "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"
    # 需要注意,
    # 1. attachment后分好为英文状态
    # 2. filename 后面需要用引号包裹,注意与外面引号错开
    m["Content-Disposition"] = "attachment; filename='02.html'"
    # 添加到MIMEMultipart
    mail_mul.attach(m)



# 发送email地址,此处地址直接使用我的qq有偶像,密码一般需要临时输入,此处偷懒
from_addr = "1366798119@qq.com"
# 此处密码是经过申请设置后的授权码,不是不是不是你的qq邮箱密码
from_pwd = "hjpovygcxmrshhcj"

# 收件人信息
# 此处使用qq邮箱,我给自己发送
to_addr = "1366798119@qq.com"


# 输入SMTP服务器地址
# 此处根据不同的邮件服务商有不同的值,
# 现在基本任何一家邮件服务商,如果采用第三方收发邮件,都需要开启授权选项
# 腾讯qq邮箱所的smtp地址是 smtp.qq.com

smtp_srv = "smtp.qq.com"

try:
    import smtplib
    srv = smtplib.SMTP_SSL(smtp_srv.encode(), 465) #SMTP协议默认端口25
    #登录邮箱发送
    srv.login(from_addr, from_pwd)
    # 发送邮件
    # 三个参数
    # 1. 发送地址
    # 2. 接受地址,必须是list形式
    # 3. 发送内容,作为字符串发送
    srv.sendmail(from_addr, [to_addr], mail_mul.as_string())
    srv.quit()
except Exception as e:
    print(e)

        - 添加邮件头, 抄送等信息
            - mail["From"] 表示发送着信息,包括姓名和邮件
            - mail["To"]  表示接收者信息,包括姓名和邮件地址
            - mail["Subject"] 表示摘要或者主题信息
            - 案例v10
from email.mime.text import MIMEText
from email.header import Header

msg = MIMEText("Hello wold",  "plain", "utf-8")
# 下面代码故意写错,说明,所谓的发送者的地址,只是从一个Header的第一个参数作为字符串构建的内容
# 用utf8编码是因为很可能内容包含非英文字符
header_from = Header("从图灵学院邮箱发出去的<TuLingXueYuan@qq.cn>", "utf-8")
msg['From'] = header_from

# 填写接受者信息
header_to = Header("去王晓静的地方<wangxiaojing@sina.com>", 'utf-8')
msg['To'] = header_to

header_sub = Header("这是图灵学院的主题", 'utf-8')
msg['Subject'] = header_sub



# 构建发送者地址和登录信息
from_addr = "1366798119@qq.com"
from_pwd = "hjpovygcxmrshhcj"


# 构建邮件接受者信息
to_addr = "1366798119@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)

        - 同时支持html和text格式
            - 构建一个MIMEMultipart格式邮件
            - MIMEMultipart的subtype设置成alternative格式
            - 添加HTML和text邮件
            - 案例v11
        
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(html_content, "html", "utf-8")
msg.attach(msg_html)


msg_text = MIMEText("just text content", "plain", "utf-8")
msg.attach(msg_text)



# 发送email地址,此处地址直接使用我的qq邮箱,密码临时输入
from_addr = "1366798119@qq.com"
#from_pwd = input('163邮箱密码: ')
from_pwd = "hjpovygcxmrshhcj"

# 收件人信息:
# 此处使用我注册的163邮箱
to_addr = "1366798119@qq.com"

# 输入SMTP服务器地址:
# 此地址根据每隔邮件服务商有不同的值,这个是发信邮件服务商的smtp地址
# 我用的是qq邮箱发送,此处应该填写腾讯qq邮箱的smtp值,即smtp.163.com,
# 需要开启授权码,
smtp_srv = "smtp.qq.com"

try:
    import smtplib
    # 加密传输
    #server = smtplib.SMTP_SSL(smtp_srv.encode(), 465) # SMTP协议默认端口是25
    # qq邮箱要求使用 TLS加密传输
    server = smtplib.SMTP(smtp_srv.encode(), 25) # SMTP协议默认端口是25
    server.starttls()
    # 设置调试级别
    # 通过设置调试等级,可以清楚的看到发送邮件的交互步骤
    server.set_debuglevel(1)
    # 登录发送邮箱
    server.login(from_addr, from_pwd)
    server.sendmail(from_addr, [to_addr], msg.as_string())
    server.quit()
except Exception as e:
    print(e)



        - 使用smtplib模块发送邮件
    
    - POP3协议接受邮件 
        - 本质上是MDA到MUA的一个过程
        - 从 MDA下载下来的是一个完整的邮件结构体,需要解析才能得到每个具体可读的内容
        - 步骤:
            1. 用poplib下载邮件结构体原始内容
                1. 准备相应的内容(邮件地址,密码,POP3实例)
                2. 身份认证
                3. 一般会先得到邮箱内邮件的整体列表
                4. 根据相应序号,得到某一封信的数据流
                5. 利用解析函数进行解析出相应的邮件结构体
            2. 用email解析邮件的具体内容
        - 案例v12
# 导入相关包
# poplib负责从MDA到MUA下载
import poplib

# 以下包负责相关邮件结构解析
from email.parser import Parser
from email.header import decode_header
from email.utils import parseaddr

# 得到邮件的原始内容
# 这个过程主要负责从MDA到MUA的下载并使用Parse粗略解析
def getMsg():
    # 准备相应的信息
    email = "1366798119@qq.com"
    # 邮箱的授权码
    pwd = "hjpovygcxmrshhcj"

    # pop3服务器地址
    pop3_srv = "pop.qq.com" # 端口995

    # ssl代表是安全通道
    srv = poplib.POP3_SSL(pop3_srv)

    # user代表email地址
    srv.user(email)
    # pass_代表密码
    srv.pass_(pwd)

    # 以下操作根据具体业务具体使用
    # stat返回邮件数量和占用空间
    # 注意stat返回一个tuple格式
    msgs, counts = srv.stat()
    print("Messages: {0}, Size: {1}".format(msgs, counts))

    # list返回所有邮件编号列表
    # mails是所有邮件编号列表
    rsp, mails, octets = srv.list()
    # 可以查看返回的mails列表类似[b'1 82923', b'2 2184', ...]
    print(mails)


    # 获取最新一封邮件,注意,邮件索引号是从1开始, 最新代表索引号最高
    index = len(mails)
    # retr负责返回一个具体索引号的一封信的内容,此内容不具有可读性
    # lines 存储邮件的最原始文本的每一行
    rsp, lines, octets = srv.retr(index)

    # 获得整个邮件的原始文本
    msg_count = b'\r\n'.join(lines).decode("utf-8")
    # 解析出邮件整个结构体
    # 参数是解码后的邮件整体
    msg = Parser().parsestr(msg_count)

    #关闭链接
    srv.quit()

    return msg


# 详细解析得到的邮件内容
# msg代表是邮件的原始内容
# idnent代表的是邮件嵌套的层级
def parseMsg(msg, indent=0):
    '''
    1. 邮件完全可能是有嵌套格式
    2. 邮件只有一个From,To,Subject之类的信息

    :param msg:
    :param indent: 描述邮件里面有几个邮件MIMEXXX类型的内容,展示的时候进行相应缩进
    :return:
    '''

    # 想办法提取出头部信息
    # 只有在第一层的邮件中才会有相关内容,
    # 此内容只有一个
    if indent == 0:
        for header in ['From', "To", 'Subject']:
            # 使用get可以避免如果没有相关关键字报错的可能性
            # 如果没有 关键字”From“, 我们使用 msg["From"]会报错
            value = msg.get(header, '')
            if value:
                # Subject中的内容直接解码就可以,他是字符串类型
                if header == 'Subject':
                    value = decodeStr(value)
                # 如果是From和To字段,则内容大概是 "我的邮箱<xxxxx@qq.com>“这种格式
                else:
                    hdr, addr = parseaddr(value)
                    name = decodeStr(hdr)
                    # 最终返回形如  "我的邮箱<xxx@qq.com>的格式
                    value = "{0}<{1}>".format(name, addr)
            print("{0}, {1}: {2}".format(indent, header, value))

    # 下面代码关注邮件内容本身
    # 邮件内容中,有可能是multipart类型,也有可能是普通邮件类型
    # 下面的解析使用递归方式
    if (msg.is_multipart()):
        # 如果是multipart类型,则调用递归解析

        # 得到多部分邮件的一个基础邮件部分
        parts = msg.get_payload()
        # enumerate 函数是内置函数
        # 作用是将一个列表,此处是parts,生成一个有索引和parts原内容构成的新的列表
        # 例如 enumerate(['a', 'b', 'c']) 结果是:  [(1,'a'), (2, 'b'), (3, 'c')]
        for n,part in enumerate(parts):
            # 一个字符串乘以一个数字的意思是对这个字符串进行n倍扩展
            # 比如 ”aa" * 2 -> "aaaa"
            print("{0}spart: {1}".format(' '*indent, n))
            parseMsg(part, indent+1)
    else: # 基础类型
        # get_content_type是系统提供函数,得到内容类型
        content_type = msg.get_content_type()
        # text/plain 或者 text/html是固定值
        if content_type == 'text/plain' or content_type == 'text/html':
            content = msg.get_payload(decode=True)
            charset = guessCharset(msg)
            if charset:
                content = content.decode(charset)
            print("{0}Text: {1}".format(indent, content))

        else: #不是文本内容,则应该是附件
            print('{0}Attachment: {1}'.format(indent, content_type))

def decodeStr(s):
    '''
    s代表一封邮件中From,To,Subject中的任一项
    对s进行解码,解码是编码的逆过程
    :param s:
    :return:
    '''
    value, charset = decode_header(s)[0]
    # charset完全可能为空
    if charset:
        # 如果指定编码,则用指定编码格式进行解码
        value = value.decode(charset)

    return value

def guessCharset(msg):
    '''
    猜测邮件的编码格式
    :param msg:
    :return:
    '''
    # 调用现成的函数
    charset = msg.get_charset()

    if charset is None:
        # 找到内容类型,并转换成小写
        content_type = msg.get("Content-Type", "").lower()
        pos = content_type.find("charset=")
        if pos >= 0:
            # 如果包含chraset,则内容形如 charset=xxxx
            charset = content_type[pos+8:].strip()

    return  charset




if __name__ == "__main__":
    # 得到邮件的原始内容
    msg = getMsg()
    print(msg)
    # 精确解析邮件内容
    parseMsg(msg, 0)

 

展开阅读全文

没有更多推荐了,返回首页