Python 15.2 POP3 收取郵

POP3收取郵件

收取郵件就是編寫一個MUA作為客戶端,從MDA把郵件獲取到用戶的電腦或者手機上。收取郵件最常用的就是POP3協議。

Python內置了一個poplib模塊,實現了POP3協議,可以直接用來收取郵件。

注意,POP3收取不是一個可以直接閱讀的郵件本身,而是郵件的原始文本,這和SMTP很像,SMTP也是發送一大堆經過編碼的文本。要把POP3收取的文本變成可閱讀的郵件,還需要用email模塊提供的各種類來解析原始文本,變成可閱讀的對象。

所以,收取郵件分為兩部分: 1、用poplib把郵件的原始文本下載到本地。

2、用email解析原始文本,還原為郵件對象。

通過POP3下載郵件

POP3協議本身很簡單,以下面代碼為例,我們來獲取最新的一封郵件內容: import poplib

email =imput('Email:')

password =input('Password:')

pop3_server =input('POP3 Server:')

server =poplib.POP3(pop3_server)

server.set_debuglevel(1)

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()

#可以查看返回的列表,類似[b'1 82923',b'2 2184'…]

print(mails)

#獲取最新一封郵件,注意索引號從1開始: index =len(mails)

resp,lines,octets =server.retr(index)

#lines存儲了原始文本郵件的每一行

#可以獲得整個原始文本的內容

msg_content =b'rn',join(lines).decode('utf-8')

#稍後解析出郵件

msg =Parser().parsestr(msg_content)

#可以根據索引直接從服務器上刪除郵件: server.dele(index)

#關閉連接: server.close()

用POP3獲取郵件其實很簡單,要獲取所有郵件,只需要循環使用retr()把每一封郵件內容拿到即可。真正麻煩的是把拿到的原始文本解析為可閱讀的郵件對象。

解析郵件

解析郵件過程和上一節構造郵件過程正相反,因此,先倒入必要模塊: from email.parser import Parser

from email.header import decode_header

from email.utils import parseaddr

import poplib

只需要一行代碼就可以把郵件內容解析為Message對象: msg =Parser().parsestr(msg_content)

但是這個Message對象本身可能是一個MessageMultipart對象,即包含嵌套的其他MIMEBase對象。嵌套可能還不止一層: 所以我們要遞歸的打印出Message對象的層次結構: #indent 用於縮進顯示: def print_info(msg,indent =0):

    if indent ==0:         for header in ['From','To','Subject']:            value =msg.get(header,'')

           if value:

                if header =='Subject':

                    value =decode_str(value)

                else :                     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()):

        parts =msg.get_payload()

        for n,part in enumerate(parts):

            print('%s part %s'% ('  ' * indent ,n))

            print('%s————-'% ('  '  * indent))

            print_info(part,indent +1)

   else:

        content_type =msg.get_content_type()

        if content_type =='text/plain' or content_type =='text/html':

            content =msg.get_payload(decode =True)

            charset =guess_charset(msg)

            if charset :

                content =content.decode(charset)

            print('%s Text: %s' % ('  ' * indent,content + '…'))

        else:

            print('%s Attachment: %s' % ('  ' * indent ,content_type))

郵件的Subject或者Email中包含的名字都是經過編碼後的str,要正常顯示必須decode: def decode_str(s):

    value,charset =decode_header(s)[0]

    if charset: 

        value =value.decode(charset)

   return value

decode_header()返回一個list,因為像Cc,Bcc這樣的字段可能包含多個郵件地址,所以解析出來的會有很多個元素。上面的代碼我們偷了個懶,只取了第一個元素。

文本郵件的正文也是str,還需要檢測編碼,否則,非UTF-8編碼的郵件都無法正常顯示: def guess_charset(msg):

    charset =msg.get_charset()

    if charset is None:         content_type =msg.get('Content-Type','').lower()

        pos =content_type.find('charset =')

        if poss >= 0:

            charset =content_type[pos + 8:].strip()

    return charset

把上面的代碼整理好,我們就可以收取一封郵件了。

+OK Welcome to coremail Mail Pop3 Server (163coms[...])  Messages: 126. Size: 27228317    From: Test <[email protected]>To: Python愛好者 <[email protected]>Subject: 用POP3收取郵件  part 0  --------------------    part 0    --------------------      Text: Python可以使用POP3收取郵件……...    part 1    --------------------      Text: Python可以<a href="...">使用POP3</a>收取郵件……...  part 1  --------------------    Attachment: application/octet-stream

我們從大陰中可以看出,這封郵件是一個MIMEMultiport,它包含兩部分,第一部分又是一個MIMEMultiport,第二部分是個附件。而內嵌的MIMEMultipart和一個HTML格式的MIMEText。

小結: 用poplib模塊收取郵件分兩步:第一部分使用POP3協議將郵件獲取到本地,第二步使用email模塊把原始郵件解析為Message對象,然後用適當的形式顯示給用戶即可。