網絡程式設計
- 網絡:
- 網絡協定: 一套規則
- 網絡模型:
- 七層模型-七層-理論
- 實體層
- 資料鍊路層
- 網絡層
- 傳輸層
- 會話層
- 表示層
- 應用層
- 四層模型-實際應用
- 鍊路層
- 引用層
- 七層模型-七層-理論
- 每一層都有相應的協定負責交換資訊或者協同工作
- TCP/IP 協定族
- IP位址:負責在網絡上唯一定位一個機器
- IP位址分ABCDE類
- 是由四個數字段組成,每個數字段的取值是0-255
- 192.168.xxx.xxx: 區域網路ip
- 127.0.0.1: 本機
- IPv4, IPv6
- 端口
- 範圍: 0-65535
- 知名端口:0-1023
- 非知名端口:1024-
- 範圍: 0-65535
ICP/UDP協定
- UDP: 非安全的不面向連結的傳輸
- 安全性差
- 大小限制64kb
- 沒有順序
- 速度快
- TCP
- 基于連結的通信
- SOCKET程式設計
- socket(套接字):是一個網絡通信的端點,能實作不同主機的程序通信,網絡大多基于socket通信
- 通過IP+端口定位對方并發送消息的通信機制
- 分為UDP和TCP
- 用戶端Client,發起通路的一方
- 伺服器端Server,接受通路的一方
- UDP程式設計
- Server端流程
- 建立socket,socket是負責具體通信的一個執行個體
- 綁定,為建立的socket指派固定的端口和ip位址
- 接受對方發送内容
- 給對方發送回報,此步驟為非必須步驟
- Client端流程
- 建立通信的socket
- 發送内容到指定伺服器
- 接受伺服器給定的回報内容
- 伺服器案例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) # 3. 接受對方消息 # 等待方式為死等,沒有其他可能性 # 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 you" # 發送的資料必須是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
''' 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) # 3. 接受對方消息 # 等待方式為死等,沒有其他可能性 # 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)
- Server端流程
- TCP程式設計
- 面向連結的傳輸,即每次傳輸之前需要先建立一個連結
- 用戶端和服務端兩個程式需要編寫
- Server端的編寫流程
- 建立socket負責具體通信,這個socket其實隻負責接受對方的請求,真正通信的是連結後重建立立的socket
- 綁定端口和位址
- 監聽接入的通路socket
- 接受通路的socket,可以了解接受通路即建立了一個通訊的連結通路
- 接受對方的發送内容,利用接收到的socket接受内容
- 如果有必要,給對方發送回報資訊
- 關閉連結通路
- Client端的編寫流程
- 建立通信socket
- 連結對方,請求跟對方建立通路
- 發送内容到對方伺服器
- 接受對方的回報
- 案例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 = "Receive 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 you" sock.send(msg.encode()) # 4. 接受對方的回報 rst = sock.recv(500) print(rst.decode()) # 5. 關閉連結通路 sock.close() if __name__ == '__main__': tcp_clt()
FTP程式設計
- FTP(FileTransferProtoacal)檔案傳輸協定
- 用途:定制一些特殊的上傳下載下傳檔案的服務
- 使用者分類:登入FTP伺服器必須有一個賬号
- Real賬戶:注冊賬戶
- Guest賬戶:可能臨時對某一類人的行為進行授權
- Anonymous賬戶:匿名賬戶,允許任何人
- FIP工作流程
- 用戶端連結遠端主機上的FTP伺服器
- 用戶端輸入使用者名和密碼(或者“anonymous”和電子郵件位址)
- 用戶端和伺服器進行各種檔案傳輸和資訊查詢操作
- 用戶端從遠端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年中國的第一份電子郵件
- 管理程式
- Euroda使郵件普及
- Netscape,outlook,forxmail後來居上
- Hotmail使用浏覽器發送郵件
- 參考資料
郵件工作流程
- MUA(MailUserAgent) 郵件使用者代理
- MTA(MailTransferAgent) 郵件傳輸代理
- MDA(MailDeliveryAgent) 郵件投遞代理
- [email protected],老師,北京海澱
- [email protected],學生,上海江岸區
- 流程
- MUA->MTA,郵件已經在伺服器上了
- qq MTA->........-> sina MTA, 郵件在新浪的伺服器上
- sina MTA-> sina MDA, 此時郵件已經在你的郵箱裡了
- 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 xxxx", "plain", "utf-8") # 發送email位址 from_addr = "[email protected]" # 此處密碼是經過申請設定後的授權碼 from_pwd = "ajwvzqdlfigahiae" # 授權碼 # 收件人資訊 # 此時使用qq郵箱 to_addr = "[email protected]" # 輸入SMIP伺服器位址 # 此處根據不同的郵件服務商有不同的值 # 現在基本任何一家郵件服務商,如果采用第三方收發郵件,都需要開啟授權選項 # 騰訊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代碼作為内容
- 把郵件的subtype設為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 = "[email protected]" # 此處密碼是經過申請設定後的授權碼 from_pwd = "ajwvzqdlfigahiae" # 授權碼 # 建構郵件接受者資訊 to_addr = "[email protected]" 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作為附件
- 案例v09
from email.mime.text import MIMEText # 建構附件使用 from email.mime.multipart import MIMEBase, MIMEMultipart # 建構基礎郵件使用 mail_mul = MIMEMultipart() # 建構郵件正文 mail_text = MIMEText("Hello, i am xxxx", "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位址 from_addr = "[email protected]" # 此處密碼是經過申請設定後的授權碼 from_pwd = "ajwvzqdlfigahiae" # 授權碼 # 收件人資訊 # 此時使用qq郵箱 to_addr = "[email protected]" # 輸入SMIP伺服器位址 # 此處根據不同的郵件服務商有不同的值 # 現在基本任何一家郵件服務商,如果采用第三方收發郵件,都需要開啟授權選項 # 騰訊qq郵箱所用的SMTP位址是 smtp.qq.com smtp_srv = "smtp.qq.com" try: import smtplib # 兩個參數 # 第一個是伺服器位址,但一定是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], 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 world", "plain", "utf-8") # 用utf-8編碼是因為很可能内容包含非英文字元 header_from = Header("從A發送出去的<[email protected]>", "utf-8") msg['From'] = header_from # 填寫接受者資訊 header_to = Header("去往B<[email protected]>", "utf-8") msg['To'] = header_to header_sub = Header("這是主題", "utf-8") msg['Subject'] = header_sub # 建構發送者位址和登入資訊 from_addr = "[email protected]" from_pwd = "ajwvzqdlfigahiae" # 建構郵件接受者資訊 to_addr = "[email protected]" 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格式郵件
- MIMEMultipartd額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位址 from_addr = "[email protected]" from_pwd = "ajwvzqdlfigahiae" # 收件人資訊: # 此處使用我的qq郵箱 to_addr = "[email protected]" # 輸入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子產品發送郵件
- 使用email子產品建構郵件
- POP3協定負責接受郵件
- 本質上是MDA到MUA的一個過程
- 從 MDA 下載下傳下來的是一個完整的郵件結構體,需要解析才能得到每個具體可讀的内容
- 步驟:
- 用poplib下載下傳郵件結構體原始内容
- 準備相應的内容(郵件位址,密碼,POP3執行個體)
- 身份認證
- 一般會先得到郵箱内郵件的整體清單
- 根據相應序号,得到某一封信的資料流
- 利用解析函數進行解析出相應的郵件結構體
- 用email解析郵件的具體内容
- 用poplib下載下傳郵件結構體原始内容
- 案例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 = "[email protected]" # 郵箱的授權碼 pwd = "ajwvzqdlfigahiae" # 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字段,則内容大概是 "我的郵箱<[email protected]>“這種格式 else: hdr, addr = parseaddr(value) name = decodeStr(hdr) # 最終傳回形如 "我的郵箱<[email protected]>的格式 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)
- SMTP協定負責發送郵件