1. 程式人生 > 其它 >python接收163郵件以及下載附件(以163郵箱為例)

python接收163郵件以及下載附件(以163郵箱為例)

技術標籤:案例接收郵件python

python利用pop3接收郵件以及下載附件(以163郵箱為例)

1、首先需要引入相關的模組,主要就是兩個模組:pop3(接收郵件)和email(解析郵件內容)

# POP3(Post Office Protocol 3),即郵局協議的第3個版本,
#是電子郵件的第一個離線協議標準。該協議把郵件下載到本地計算機,
#不與伺服器同步,缺點是更易丟失郵件或多次下載相同的郵件。
import poplib
# 引入用來解析郵件相關資訊的模組
from email.parser import Parser
from email.header import decode_header
from email.utils import parseaddr
#引入相關時間庫
from datetime import datetime
#引入專門處理時間和日期的模組,arrow是一個輕量級Python庫
import arrow

2、再定義一些與郵件相關的全域性變數

# 輸入自己163的郵箱地址。
user_email_address = '[email protected]'
# 郵箱的授權碼,注意:不是登入密碼
user_password = 'SMSRDEQCIUKYODOR'
# 這個是163郵箱的pop3的伺服器地址,各個公司的郵箱平臺的POP3的伺服器地址都是不同的,自己網上查詢下即可
#例如:qq郵箱的pop3伺服器地址是:pop.qq.com
pop_server_host = 'pop.163.com'
# 郵箱對應的pop伺服器的監聽埠
#(如果設定POP3的SSL加密方式連線的話,則埠為:995),否則就是埠為110
pop_server_port = 995

3、定義郵箱連線校驗的函式

def connect_email_by_pop3():
    try:
        # 連線pop伺服器。如果沒有使用SSL,將POP3_SSL()改成POP3(),且監聽埠改為:110即可
        email_server = poplib.POP3_SSL(host=pop_server_host, port=pop_server_port, timeout=10)
        print("連線pop伺服器-------正常,開始驗證使用者郵箱")
    except:
        print("連線pop伺服器-------異常,退出")
        exit(1)

    try:
        # 驗證使用者郵箱
        email_server.user(user_email_address)
        print("使用者郵箱驗證-------正常,開始驗證郵箱授權碼")
    except:
        print("使用者郵箱驗證-------異常,退出")
        exit(1)

    try:
        # 驗證郵箱密碼是否正確,注意不是登入密碼,是授權碼
        email_server.pass_(user_password)
        print("郵箱授權碼驗證-------正常,開始接受郵箱以及附件")
    except:
        print("郵箱授權碼驗證-------異常,退出")
        exit(1)
    #開始處理郵箱相關資訊   
    parse_email_server(email_server)

注意:如果出現連線pop伺服器問題,可能是自己的郵箱沒有設定開啟POP3服務,如qq郵箱的。

4、定義處理郵箱相關資訊的函式

def parse_email_server(email_server):
    resp, mails, octets = email_server.list()
    num, total_size = email_server.stat()
    print("郵件數量為:" + str(num))
    # mails儲存了郵件編號列表,
    index = len(mails)
    # 倒序遍歷郵件
    for i in range(index, 0, -1):
        # 倒序遍歷郵件,這樣取到的第一封就是最新郵件
        resp, lines, octets = email_server.retr(i)
        # lines儲存了郵件的原始文字的每一行,
        # 郵件的原始文字:# lines是郵件內容,列表形式使用join拼成一個byte變數
        msg_content = b'\r\n'.join(lines).decode('utf-8')
        # 解析郵件:
        msg = Parser().parsestr(msg_content)
        # 郵件時間,解析時間格式
        mail_datetime = parse_mail_time(msg.get("date"))
        max_mail_time_str = arrow.get(mail_datetime).format("YYYY-MM-DD HH:mm")
        # 這個可以作為根據時間進行郵件的過濾解析,這個把時間寫死判斷,比較侷限,可以在第一個接收時,把最新的郵件接收時間寫入到自定義的檔案中,
        # 等第二次接收郵件時,再取檔案中的時間,進行判斷,用於過濾
        # if (max_mail_time_str > "2020-01-01 00:00:00"):
        #     continue
        print("郵件接收時間為:" + max_mail_time_str)
        # 解析郵件具體內容,包括正文,標題,和附件
        parser_content(msg, 0)
    # 別忘記退出    
    email_server.quit()

5、定義解析郵件具體內容

def parser_content(msg, indent):
    if indent == 0:
        # 郵件的From, To, Subject存在於根物件上:
        #呼叫解析郵件頭部內容的函式    
        parser_email_header(msg)
    # 下載附件
    for part in msg.walk():
        file_name = part.get_filename()  # 獲取附件名稱型別
        if file_name is None:
            continue
        # 說明不是文字,則作為附件處理
        filename = decode_str(file_name)  # 對附件名稱進行解碼
        data = part.get_payload(decode=True)  # 下載附件
        att_file = open('E:/emailFiles/' + filename, 'wb')  # 在指定目錄下建立檔案,注意二進位制檔案需要用wb模式開啟
        att_file.write(data)  # 儲存附件
        att_file.close()
        print("附件:" + filename + "儲存成功!")

    if (msg.is_multipart()):
        # 如果郵件物件是一個MIMEMultipart,
        # get_payload()返回list,包含所有的子物件:
        parts = msg.get_payload()
        for n, part in enumerate(parts):
            # 遞迴列印每一個子物件:
            return parser_content(part, indent + 1)
    else:
        # 解析正文
        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)
                print('%s正文內容為: %s' % ('  ' * indent, content))

6、定義解析頭部內容的相關函式

# 解析郵件
def parser_email_header(msg):
    # 解析郵件標題
    subject = msg['Subject']
    value, charset = decode_header(subject)[0]
    if charset:
        value = value.decode(charset)
    print('郵件標題: {0}'.format(value))

    # 解析傳送人資訊
    hdr, addr = parseaddr(msg['From'])
    # name 傳送人郵箱名稱, addr 傳送人郵箱地址
    name, charset = decode_header(hdr)[0]
    if charset:
        name = name.decode(charset)
    print('傳送人郵箱名稱: {0},傳送人郵箱地址: {1}'.format(name, addr))

    # 解析接收人資訊
    hdr, addr = parseaddr(msg['To'])
    # name 傳送人郵箱名稱, addr 傳送人郵箱地址
    name, charset = decode_header(hdr)[0]
    if charset:
        name = name.decode(charset)
    print('接收人郵箱名稱: {0},接收人郵箱地址: {1}'.format(name, addr))

7、其它相關定義函式

# 解碼
def decode_str(s):
    value, charset = decode_header(s)[0]
    if charset:
        value = value.decode(charset)
    return value


# 猜測字元編碼
def guess_charset(msg):
    # 先從msg物件獲取編碼:
    charset = msg.get_charset()
    if charset is None:
        # 如果獲取不到,再從Content-Type欄位獲取:
        content_type = msg.get('Content-Type', '').lower()
        for item in content_type.split(';'):
            item = item.strip()
            if item.startswith('charset'):
                charset = item.split('=')[1]
                break
    return charset

#郵件時間處理函式
def parse_mail_time(mail_datetime):
    GMT_FORMAT = "%a, %d %b %Y %H:%M:%S"
    GMT_FORMAT2 = "%d %b %Y %H:%M:%S"
    index = mail_datetime.find(' +0')
    if index > 0:
        mail_datetime = mail_datetime[:index]  # 去掉+0800
    formats = [GMT_FORMAT, GMT_FORMAT2]
    for ft in formats:
        try:
            mail_datetime = datetime.strptime(mail_datetime, ft)
            return mail_datetime
        except:
            pass
    raise Exception("郵件時間格式解析錯誤")

8、入口

# 比較規範寫法,象徵著程式入口
if __name__ == "__main__":
    connect_email_by_pop3()