计网-邮件收发-python3.6POP3课设

轮子就不造了,你们直接看链接总体的概念,但是这个编码是python2的
关于邮件收发【两个图稍微理解一下】在这里插入图片描述
在这里插入图片描述

我的课设是POP3的实现,所以主要写这个部分我遇到的问题,关于下文的MIMEMultipart,Attachment,可以取看看上面链接的SMTP部分的发送带附件的邮件
1.了解一下背景知识:py2-》py3

  • raw_input----input
  • unicode----str
    str通过encode()方法可以编码为指定的bytes。反过来,当从网络或磁盘上读取了字节流,那么读到的数据就是bytes。要把bytes变为str,就需要用decode()方法。反之,则使用encode()方法即可!
    所以在编码过程中要进行信息编码方式的转变
  • print(’\n’.join(letterlist)) #letterlist是个列表 意思是一下输出每个元素,然后每个元素之间用 \n(换行)填充,这样就不用for循环来执行

2.心酸路程

  • 一开始用QQ邮箱,一直连接不上,说超时,设port也没有用,最终换成163.com成功连接上了服务器
  • 连上服务器之前,发现了一种错误,在python “”.join()那行,报错TypeError: sequence item 0: expected string, int found,于是我想了想,强制转换str;然后,在print(’%sText: %s’ % (’ ’ * indent, content + ‘…’))这行,又一次报错TypeError: can’t concat str to bytes,于是我把’…'删了(后来发现可以不用删),把输出固定indent的与输出content的部分分开来,就ok了。
  • 成功不在报错之后,却发现什么From,To,什么内容都没有,然后我输出msg,呵,我猜是编码方式的问题导致print_info匹配不到,最后我确认,就是编码的问题,然后我就把列表里的东西都先转换了编码方式utf-8,嗯,舒服输出了
  • 然后,结果显示text的内容是错的,我仔细看了看msg,怀疑了一下加密问题,base64,上网发现,python3.6已经带好了这个“君子加密方式”的加解密函数
  • 解密完,发现似乎text内容前面一直多一个符号b,于是有了这行代码content=base64.b64decode(content).decode(charset)
  • 后来,我加了个查看时间Date的输出

3.具体代码及解说
关于POP3协议
在这里插入图片描述

  • 需要调用Python3.6的poplib库,Parser库,decode_header库,parseaddr库。
  • 首先是实例化一个pop3对象,获取控制台的输入,确认连入到163邮箱;
  • 然后就是输入邮箱号和邮箱SMTP/POP3的授权码,利用函数pop3.user()和pop3.pass_()确认登录;
  • 接着,利用pop3.list()抓取所有邮件的编号和大小;
  • 利用pop3.retr()定位和获取邮件的具体信息,本次实验我们只选用最新的那个邮件,并将邮件的内容(这里还是一个列表类型)通过函数join()衔接在一起;
  • 最后利用Parser().parsestr()把邮件内容解析为Message对象,Message对象本身可能是一个MIMEMultipart对象,即包含嵌套的其他MIMEBase对象,嵌套可能还不止一层。
  • 所以我们要递归地打印出Message对象的层次结构,即函数print_info();
  • 其中利用decode_str()获取邮件内容的编码方式,之后用于邮件内容的转码;
  • 不得不提,这里的163.com对邮件内容有进行加密,其加密方式是base64,本次实验利用函数base64.b64decode()对邮件内容进行解密,再转换位为对应的编码方式;
  • 利用函数guess_charset()截取第一个邮件地址,避免群发邮件产生困扰。
import poplib
from email.parser import Parser
from email.header import decode_header
from email.utils import parseaddr
import base64

def guess_charset(msg):
    # 先从msg对象获取编码:
    charset = msg.get_charset()
    if charset is None:
        # 如果获取不到,再从Content-Type字段获取:【在主函数里把msg输出看看就知道为什么了】
        content_type = msg.get('Content-Type', '').lower()
        pos = content_type.find('charset=')
        if pos >= 0:
            charset = content_type[pos + 8:].strip()
    return charset

def decode_str(s):
    #邮件的Subject或者Email中包含的名字都是经过编码后的str,要正常显示,就必须decode
    #decode_header()可能会返回一个list,这里我们只取第一个元素
    value, charset = decode_header(s)[0]
    if charset:
        value = value.decode(charset)
    return value

# 递归地打印出Message对象的层次结构
# indent用于缩进显示:
def print_info(msg, indent=0):
    if indent == 0:
        # 邮件的From, To, Subject存在于根对象上:
        for header in ['From', 'To', 'Subject','Date']:
            value = msg.get(header,'')
            #print(value)
            if value:
                if header=="Subject":
                    # 需要解码Subject字符串:
                    value = decode_str(value)#只取了第一个邮件地址
                elif header=='Date':
                    value = u'%s %s' % (header,value[0:24])
                else:
                    # 需要解码Email地址:
                    hdr, addr = parseaddr(value) #解析函数
                    name = decode_str(hdr)#只取了第一个邮件地址
                    value = u'%s <%s>' % (name, addr)
            print('%s%s: %s' % (' ' * indent, header, value))
    if (msg.is_multipart()):
        # 如果邮件对象是一个MIMEMultipart,
        # get_payload()返回list,包含所有的子对象:
        parts = msg.get_payload()
        for n, part in enumerate(parts):
            print('%spart %s' % ('  ' * indent, n))
            print('%s--------------------' % (' ' * indent))
            # 递归打印每一个子对象
            print_info(part, indent + 1)
    else:
        # 邮件对象不是一个MIMEMultipart,
        # 就根据content_type判断:
        content_type = msg.get_content_type()
        if content_type=='text/plain' or content_type=='text/html':
            # 纯文本或HTML内容:
            content = msg.get_payload(decode=True)
            # 要检测文本编码:
            charset = guess_charset(msg)
            if charset:
                content = content.decode(charset)
                #可选:输出加密状态下的text内容
                #print(content)
                content=base64.b64decode(content).decode(charset,'ignore')
            print('%sText: %s' %(' '*indent,content))
        else:
            # 不是文本,作为附件处理:
            print('%sAttachment: %s' %(' ' * indent,content_type))


# 输入邮件地址, 口令和POP3服务器地址:
email =input('Email: ')#确定过,QQ邮箱不能用,163邮箱可以,其他还没探索过
password =input('Password: ')#输入的是邮箱的授权码!=登录邮箱的密码
pop3_server =input('POP3 server: ')#例如'pop3.163.com'
# 连接到POP3服务器:例如pop.163.com
server = poplib.POP3_SSL(pop3_server)
#server = poplib.POP3(pop3_server)
#可以打开或关闭调试信息:
#server.set_debuglevel(1)
#可选:打印POP3服务器的欢迎文字:
print(server.getwelcome().decode('utf-8'))
#身份认证:
server.user(email)
server.pass_(password)
# stat()返回邮件数量和占用空间:
print('Messages: %s. Size: %s' % server.stat())
# list()返回所有邮件的编号:
resp, mails, octets = server.list()
# 可以查看返回的列表类似['1 82923', '2 2184', ...][转成utf-8模式]
#返回列表的原形式[b'1 82923' , b'2 2184' , ...]
mails2=[]
for i in mails:
    j=str(i.decode('utf-8'))
    mails2.append(j)
print(mails2)

# 获取最新一封邮件, 注意索引号从1开始:
index=len(mails)
# lines存储了邮件的原始文本的每一行,它是个列表
resp, lines, octets = server.retr(index)

#转成utf-8和字符串
lines2=[]
for i in lines:
    j=str(i.decode('utf-8'))
    lines2.append(j)

#通过join衔接,可以获得整个邮件的原始文本:
msg_content = '\t\n'.join(lines2)
#print(msg_content)
# 解析邮件:这里输出msg是个乱的,还没有真正的解析
# 把邮件内容解析为Message对象
msg = Parser().parsestr(msg_content)
#可选:打印msg方便确定具体编码方式和text加密设置
#print(msg)

# 慎重:将直接从服务器删除邮件:
# server.dele(len(mails))
# 打印邮件内容:
print_info(msg)

# 关闭连接:
server.quit()

4.结果
收到文本邮件的
在这里插入图片描述
收到图片附件的
在这里插入图片描述
5.第一次用python(其实自己没写什么东西),毕竟python都自己写好了相关函数,只是参考廖老师的,调用调用库函数就没了,但是我更希望去分享我作为一个python新手,去写pop3收邮件这个部分遇到的问题,以及解决的办法,如果有什么写不到位的,欢迎留言。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值