網(wǎng)絡(luò)編程
網(wǎng)絡(luò):
網(wǎng)絡(luò)協(xié)議: 一套規(guī)則
-
網(wǎng)絡(luò)模型:
- 七層模型-七層-理論
- 物理層
- 數(shù)據(jù)鏈路層
- 網(wǎng)絡(luò)層
- 傳輸層
- 會話層
- 表示層
- 應(yīng)用層
- 四層模型-實(shí)際應(yīng)用
- 鏈路層
- 網(wǎng)絡(luò)層
- 傳輸層
- 引用層
- 七層模型-七層-理論
每一層都有相應(yīng)的協(xié)議負(fù)責(zé)交換信息或者協(xié)同工作
TCP/IP 協(xié)議族
-
IP地址:負(fù)責(zé)在網(wǎng)絡(luò)上唯一定位一個(gè)機(jī)器
- IP地址分ABCDE類
- 是由四個(gè)數(shù)字段組成,每個(gè)數(shù)字段的取值是0-255
- 192.168.xxx.xxx: 局域網(wǎng)ip
- 127.0.0.1: 本機(jī)
- IPv4, IPv6
-
端口
- 范圍: 0-65535
- 知名端口:0-1023
- 非知名端口:1024-
- 范圍: 0-65535
ICP/UDP協(xié)議
-
UDP: 非安全的不面向鏈接的傳輸
- 安全性差
- 大小限制64kb
- 沒有順序
- 速度快
-
TCP
- 基于鏈接的通信
-
SOCKET編程
- socket(套接字):是一個(gè)網(wǎng)絡(luò)通信的端點(diǎn)蚂斤,能實(shí)現(xiàn)不同主機(jī)的進(jìn)程通信谈为,網(wǎng)絡(luò)大多基于socket通信
- 通過IP+端口定位對方并發(fā)送消息的通信機(jī)制
- 分為UDP和TCP
- 客戶端Client,發(fā)起訪問的一方
- 服務(wù)器端Server焦匈,接受訪問的一方
-
UDP編程
- Server端流程
- 建立socket遥倦,socket是負(fù)責(zé)具體通信的一個(gè)實(shí)例
- 綁定忱叭,為創(chuàng)建的socket指派固定的端口和ip地址
- 接受對方發(fā)送內(nèi)容
- 給對方發(fā)送反饋,此步驟為非必須步驟
- Client端流程
- 建立通信的socket
- 發(fā)送內(nèi)容到指定服務(wù)器
- 接受服務(wù)器給定的反饋內(nèi)容
- 服務(wù)器案例v01
''' Server端流程 1. 建立socket轿曙,socket是負(fù)責(zé)具體通信的一個(gè)實(shí)例 2. 綁定弄捕,為創(chuàng)建的socket指派固定的端口和ip地址 3. 接受對方發(fā)送內(nèi)容 4. 給對方發(fā)送反饋,此步驟為非必須步驟 ''' # socket模塊負(fù)責(zé)socket編程 import socket # 模擬服務(wù)器的函數(shù) def serverFunc(): # 1. 建立socket # socket.AF_INET: 使用ipv4協(xié)議族 # socket.SOCK_DGRAM: 使用UDP通信 sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # 2. 綁定ip和port # 127.0.0.1: 這個(gè)ip地址代表的是機(jī)器本身 # 7852: 隨機(jī)指定的端口號 # 地址是一個(gè)tuple類型导帝,(ip, port) addr = ("127.0.0.1", 7852) sock.bind(addr) # 3. 接受對方消息 # 等待方式為死等守谓,沒有其他可能性 # recvfrom接受的返回值是一個(gè)tuple,前一項(xiàng)表示數(shù)據(jù)您单,后一項(xiàng)表示地址 # 參數(shù)的含義是緩沖區(qū)大小 # rst = sock.recvfrom(500) data, addr = sock.recvfrom(500) print(data) print(type(data)) # 發(fā)送過來的數(shù)據(jù)是bytes格式斋荞,必須通過解碼才能得到str格式內(nèi)容 # decode默認(rèn)參數(shù)是utf8 text = data.decode() print(type(text)) print(text) # 給對方返回的消息 rsp = "Ich hab keine Hunge" # 發(fā)送的數(shù)據(jù)需要編碼成bytes格式 # 默認(rèn)是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. 發(fā)送內(nèi)容到指定服務(wù)器 3. 接受服務(wù)器給定的反饋內(nèi)容 ''' def clientFunc(): sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) text = "I love you" # 發(fā)送的數(shù)據(jù)必須是bytes格式 data = text.encode() # 發(fā)送 sock.sendto(data, ("127.0.0.1", 7852)) data, addr = sock.recvfrom(200) data = data.decode() print(data) if __name__ == '__main__': clientFunc()
- 服務(wù)器程序要永久運(yùn)行,一般用死循環(huán)處理
- 改造的服務(wù)器版本v03
''' Server端流程 1. 建立socket虐秦,socket是負(fù)責(zé)具體通信的一個(gè)實(shí)例 2. 綁定平酿,為創(chuàng)建的socket指派固定的端口和ip地址 3. 接受對方發(fā)送內(nèi)容 4. 給對方發(fā)送反饋,此步驟為非必須步驟 ''' # socket模塊負(fù)責(zé)socket編程 import socket # 模擬服務(wù)器的函數(shù) def serverFunc(): # 1. 建立socket # socket.AF_INET: 使用ipv4協(xié)議族 # socket.SOCK_DGRAM: 使用UDP通信 sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # 2. 綁定ip和port # 127.0.0.1: 這個(gè)ip地址代表的是機(jī)器本身 # 7852: 隨機(jī)指定的端口號 # 地址是一個(gè)tuple類型悦陋,(ip, port) addr = ("127.0.0.1", 7852) sock.bind(addr) # 3. 接受對方消息 # 等待方式為死等蜈彼,沒有其他可能性 # recvfrom接受的返回值是一個(gè)tuple,前一項(xiàng)表示數(shù)據(jù)俺驶,后一項(xiàng)表示地址 # 參數(shù)的含義是緩沖區(qū)大小 # rst = sock.recvfrom(500) data, addr = sock.recvfrom(500) print(data) print(type(data)) # 發(fā)送過來的數(shù)據(jù)是bytes格式幸逆,必須通過解碼才能得到str格式內(nèi)容 # decode默認(rèn)參數(shù)是utf8 text = data.decode() print(type(text)) print(text) # 給對方返回的消息 rsp = "Ich hab keine Hunge" # 發(fā)送的數(shù)據(jù)需要編碼成bytes格式 # 默認(rèn)是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編程
- 面向鏈接的傳輸,即每次傳輸之前需要先建立一個(gè)鏈接
- 客戶端和服務(wù)端兩個(gè)程序需要編寫
- Server端的編寫流程
- 建立socket負(fù)責(zé)具體通信,這個(gè)socket其實(shí)只負(fù)責(zé)接受對方的請求还绘,真正通信的是鏈接后重新建立的socket
- 綁定端口和地址
- 監(jiān)聽接入的訪問socket
- 接受訪問的socket楚昭,可以理解接受訪問即建立了一個(gè)通訊的鏈接通路
- 接受對方的發(fā)送內(nèi)容,利用接收到的socket接受內(nèi)容
- 如果有必要拍顷,給對方發(fā)送反饋信息
- 關(guān)閉鏈接通路
- Client端的編寫流程
- 建立通信socket
- 鏈接對方哪替,請求跟對方建立通路
- 發(fā)送內(nèi)容到對方服務(wù)器
- 接受對方的反饋
- 關(guān)閉鏈接通路
- 案例v04
import socket def tcp_srv(): # 1. 建立socket負(fù)責(zé)具體通信,這個(gè)socket其實(shí)只負(fù)責(zé)接受對方的請求菇怀,真正通信的是鏈接后重新建立的socket # 需要用到兩個(gè)參數(shù) # AF_INET: 含義同UDP一致 # SOCK_STREAM: 表明使用的是TCP進(jìn)行通信 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 2. 綁定端口和地址 # 此地址信息是一個(gè)元組類型內(nèi)容,元組分兩部分晌块,第一部分為字符串爱沟,代表ip,第二部分為端口匆背,是一個(gè)整數(shù)呼伸,推薦大于10000 addr = ("127.0.0.1", 8998) sock.bind(addr) # 3. 監(jiān)聽接入的訪問socket sock.listen() while True: # 4. 接受訪問的socket,可以理解為接受訪問即建立了一個(gè)通訊的鏈接通路 # accept返回的元組第一個(gè)元素賦值給skt钝尸,第二個(gè)賦值給addr skt, addr = sock.accept() # 5. 接受對方的發(fā)送內(nèi)容括享,利用接受到的socket接受內(nèi)容 # 500代表接受使用的buffersize # msg = skt.receive(500) msg = skt.recv(500) # 接受到的是bytes格式內(nèi)容 # 想得到str格式,需要進(jìn)行解碼 msg = msg.decode() rst = "Receive msg: {0} from {1}".format(msg, addr) print(rst) # 6. 如果有必要珍促,給對方發(fā)送反饋信息 skt.send(rst.encode()) # 7. 關(guān)閉鏈接通路 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. 發(fā)送內(nèi)容到對方服務(wù)器 msg = "I love you" sock.send(msg.encode()) # 4. 接受對方的反饋 rst = sock.recv(500) print(rst.decode()) # 5. 關(guān)閉鏈接通路 sock.close() if __name__ == '__main__': tcp_clt()
FTP編程
FTP(FileTransferProtoacal)文件傳輸協(xié)議
用途:定制一些特殊的上傳下載文件的服務(wù)
-
用戶分類:登錄FTP服務(wù)器必須有一個(gè)賬號
- Real賬戶:注冊賬戶
- Guest賬戶:可能臨時(shí)對某一類人的行為進(jìn)行授權(quán)
- Anonymous賬戶:匿名賬戶,允許任何人
-
FIP工作流程
- 客戶端鏈接遠(yuǎn)程主機(jī)上的FTP服務(wù)器
- 客戶端輸入用戶名和密碼(或者“anonymous”和電子郵件地址)
- 客戶端和服務(wù)器進(jìn)行各種文件傳輸和信息查詢操作
- 客戶端從遠(yuǎn)程FTP服務(wù)器退出猪叙,結(jié)束傳輸
-
FTP文件表示
- 分三段表示FTP服務(wù)器上的文件
- HOST:主機(jī)地址娇斩,類似于 ftp.mozilla.org, 以 ftp 開頭
- DIR:目錄,表示文件所在本地的路徑穴翩,例如 pub/android/focus/1.1-RC1
- File:文件名稱犬第,例如 Klar-1.1-RC1.apk
- 如果想完整精確表示ftp上某一文件,需要上述三部分組合到一起
- 案例v06
# 需要導(dǎo)入相應(yīng)包芒帕,主要是ftplib import ftplib # 關(guān)于FTP的操作都在這個(gè)包里邊 import os import socket # 三部分精確表示在ftp服務(wù)器上的某一個(gè)文件 # 好多公開ftp服務(wù)器訪問會出錯(cuò)或者沒有反應(yīng) HOST = "ftp.acc.umu.se" DIR = 'Public/EFLIB/' FILE = 'README' # 1. 客戶端鏈接遠(yuǎn)程主機(jī)上的FTP服務(wù)器 try: f = ftplib.FTP() # 通過設(shè)置調(diào)試級別可以方便調(diào)試 f.set_debuglevel(2) # 鏈接主機(jī)地址 f.connect(HOST) except Exception as e: print(e) exit() print("***Connected to host {0}".format(HOST)) # 2. 客戶端輸入用戶名和密碼(或者“anonymous”和電子郵件地址) try: # 登錄如果沒有輸入用戶信息歉嗓,則默認(rèn)使用匿名登錄 f.login() except Exception as e: print(e) exit() print("***Logged in as 'anonymous'") # 3. 客戶端和服務(wù)器進(jìn)行各種文件傳輸和信息查詢操作 try: # 更改當(dāng)前目錄到指定目錄 f.cwd(DIR) except Exception as e: print(e) exit() print("*** Changed dir to {0}".format(DIR)) try: # 從FTP服務(wù)器上下載文件 # 第一個(gè)參數(shù)是ftp命令 # 第二個(gè)參數(shù)是回調(diào)函數(shù) # 此函數(shù)的意思是,執(zhí)行RETR命令背蟆,下載文件到本地后鉴分,運(yùn)行回調(diào)函數(shù) f.retrbinary('RETR {0}'.format(FILE), open(FILE, 'wb').write) except Exception as e: print(e) exit() # 4. 客戶端從遠(yuǎn)程FTP服務(wù)器退出,結(jié)束傳輸 f.quit()
Mail編程
電子郵件的歷史
-
起源
- 1969 Leonard K. 教授發(fā)給同事的 “LO”
- 1971 美國國防部自主的阿帕網(wǎng)(Arpanet)的通訊機(jī)制
- 通訊地址里用@
- 1987年中國的第一份電子郵件
“Across the Great Wall we can reach every corner in the world”
-
管理程序
- Euroda使郵件普及
- Netscape带膀,outlook冠场,forxmail后來居上
- Hotmail使用瀏覽器發(fā)送郵件
-
參考資料
郵件工作流程
MUA(MailUserAgent) 郵件用戶代理
MTA(MailTransferAgent) 郵件傳輸代理
MDA(MailDeliveryAgent) 郵件投遞代理
laoshi@qq.com,老師本砰,北京海淀
xuesheng@sina.com碴裙,學(xué)生,上海江岸區(qū)
-
流程
- MUA->MTA,郵件已經(jīng)在服務(wù)器上了
- qq MTA->........-> sina MTA, 郵件在新浪的服務(wù)器上
- sina MTA-> sina MDA, 此時(shí)郵件已經(jīng)在你的郵箱里了
- sina MDA -> MUA(Foxmail/Outlook), 郵件下載到本地電腦
-
編寫程序
- 發(fā)送: MUA->MTA with SMTP: SimpleMailTransferProtocal, 包含MTA->MTA
- 接受: MDA->MUA with POP3 and IMAP: PostOfficeProtocal v3 and InternetMessageAccessProtocal v4
-
準(zhǔn)備工作
- 注冊郵箱(以qq郵箱為例)
- 第三方郵箱需要特殊設(shè)置舔株,以qq郵箱為例
- 進(jìn)入設(shè)置中心
- 取得授權(quán)碼
-
Python for mail
- SMTP協(xié)議負(fù)責(zé)發(fā)送郵件
-
使用email模塊構(gòu)建郵件
- 純文本郵件
- 案例v07
# 導(dǎo)入相應(yīng)的包 import smtplib from email.mime.text import MIMEText # MIMEText三個(gè)主要參數(shù) # 1. 郵件內(nèi)容 # 2. MIME子類型莺琳,在此案例我們用plain表示text類型 # 3. 郵件編碼格式 msg = MIMEText("Hello, i am xxxx", "plain", "utf-8") # 發(fā)送email地址 from_addr = "1441865605@qq.com" # 此處密碼是經(jīng)過申請?jiān)O(shè)置后的授權(quán)碼 from_pwd = "ajwvzqdlfigahiae" # 授權(quán)碼 # 收件人信息 # 此時(shí)使用qq郵箱 to_addr = "1441865605@qq.com" # 輸入SMIP服務(wù)器地址 # 此處根據(jù)不同的郵件服務(wù)商有不同的值 # 現(xiàn)在基本任何一家郵件服務(wù)商,如果采用第三方收發(fā)郵件载慈,都需要開啟授權(quán)選項(xiàng) # 騰訊qq郵箱所用的SMTP地址是 smtp.qq.com smtp_srv = "smtp.qq.com" try: # 兩個(gè)參數(shù) # 第一個(gè)是服務(wù)器地址惭等,但一定是bytes格式,所以需要編碼 # 第二個(gè)參數(shù)是服務(wù)器的接受訪問端口 srv = smtplib.SMTP_SSL(smtp_srv.encode(), 465) # SMTP 協(xié)議默認(rèn)端口25 # 登錄郵箱發(fā)送 srv.login(from_addr, from_pwd) # 發(fā)送郵件 # 三個(gè)參數(shù) # 1. 發(fā)送地址 # 2. 接受地址办铡,必須是list格式 # 3. 發(fā)送內(nèi)容辞做,作為字符串發(fā)送 srv.sendmail(from_addr, [to_addr], msg.as_string()) srv.quit() except Exception as e: print(e)
-
HTML格式郵件發(fā)送
- 準(zhǔn)備HTML代碼作為內(nèi)容
- 把郵件的subtype設(shè)為html
- 發(fā)送
- 案例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") # 構(gòu)建發(fā)送者地址和登錄信息 from_addr = "1441865605@qq.com" # 此處密碼是經(jīng)過申請?jiān)O(shè)置后的授權(quán)碼 from_pwd = "ajwvzqdlfigahiae" # 授權(quán)碼 # 構(gòu)建郵件接受者信息 to_addr = "971254246@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)
-
發(fā)送帶附件的郵件
- 可以把郵件看作是一個(gè)文本郵件和一個(gè)附件的合體
- 一封郵件如果涉及多個(gè)部分,需要使用MIMEMultipart格式構(gòu)建
- 添加一個(gè)MIMEText正文
- 添加一個(gè)MIMEBase或者M(jìn)IMEText作為附件
- 案例v09
from email.mime.text import MIMEText # 構(gòu)建附件使用 from email.mime.multipart import MIMEBase, MIMEMultipart # 構(gòu)建基礎(chǔ)郵件使用 mail_mul = MIMEMultipart() # 構(gòu)建郵件正文 mail_text = MIMEText("Hello, i am xxxx", "plain", "utf-8") # 把構(gòu)建好的郵件正文附加入郵件中 mail_mul.attach(mail_text) # 構(gòu)建附加 # 構(gòu)建附件寡具,需要從本地讀入附件 # 打開一個(gè)本地文件 # 以rb格式打開 with open("02.html", "rb") as f: s = f.read() # 設(shè)置附件的MIME和文件名 m = MIMEText(s, 'base64', "utf-8") m["Content-Type"] = "application/octet-stream" # 需要注意 # 1. attachment后分號為英文狀態(tài) # 2. filename 后面需要用引號包裹秤茅,注意與外面引號錯(cuò)開 m["Content-Disposition"] = "attachment; filename='02.html'" # 添加到MIMEMultipart mail_mul.attach(m) # 發(fā)送email地址 from_addr = "1441865605@qq.com" # 此處密碼是經(jīng)過申請?jiān)O(shè)置后的授權(quán)碼 from_pwd = "ajwvzqdlfigahiae" # 授權(quán)碼 # 收件人信息 # 此時(shí)使用qq郵箱 to_addr = "1441865605@qq.com" # 輸入SMIP服務(wù)器地址 # 此處根據(jù)不同的郵件服務(wù)商有不同的值 # 現(xiàn)在基本任何一家郵件服務(wù)商,如果采用第三方收發(fā)郵件童叠,都需要開啟授權(quán)選項(xiàng) # 騰訊qq郵箱所用的SMTP地址是 smtp.qq.com smtp_srv = "smtp.qq.com" try: import smtplib # 兩個(gè)參數(shù) # 第一個(gè)是服務(wù)器地址框喳,但一定是bytes格式,所以需要編碼 # 第二個(gè)參數(shù)是服務(wù)器的接受訪問端口 srv = smtplib.SMTP_SSL(smtp_srv.encode(), 465) # SMTP 協(xié)議默認(rèn)端口25 # 登錄郵箱發(fā)送 srv.login(from_addr, from_pwd) # 發(fā)送郵件 # 三個(gè)參數(shù) # 1. 發(fā)送地址 # 2. 接受地址厦坛,必須是list格式 # 3. 發(fā)送內(nèi)容五垮,作為字符串發(fā)送 srv.sendmail(from_addr, [to_addr], mail_mul.as_string()) srv.quit() except Exception as e: print(e)
-
添加郵件頭,抄送等信息
- mail["From"] 表示發(fā)送者信息杜秸,包括姓名和郵件
- 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編碼是因?yàn)楹芸赡軆?nèi)容包含非英文字符 header_from = Header("從A發(fā)送出去的<A@qq.cn>", "utf-8") msg['From'] = header_from # 填寫接受者信息 header_to = Header("去往B<B@sina.com>", "utf-8") msg['To'] = header_to header_sub = Header("這是主題", "utf-8") msg['Subject'] = header_sub # 構(gòu)建發(fā)送者地址和登錄信息 from_addr = "1441865605@qq.com" from_pwd = "ajwvzqdlfigahiae" # 構(gòu)建郵件接受者信息 to_addr = "1441865605@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)
-
同時(shí)支持html和text格式
- 構(gòu)建一個(gè)MIMEMultipart格式郵件
- MIMEMultipartd額subtype設(shè)置成alternative格式
- 添加HTML和Text郵件
- 案例v11
from email.mime.text import MIMEText from email.mime.multipart import MIMEMultipart # 構(gòu)建一個(gè)MIMEMultipart郵件 msg = MIMEMultipart("alternative") # 構(gòu)建一個(gè)HTML郵件內(nèi)容 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) # 發(fā)送email地址 from_addr = "1441865605@qq.com" from_pwd = "ajwvzqdlfigahiae" # 收件人信息: # 此處使用我的qq郵箱 to_addr = "1441865605@qq.com" # 輸入SMTP服務(wù)器地址: # 此地址根據(jù)每隔郵件服務(wù)商有不同的值,這個(gè)是發(fā)信郵件服務(wù)商的smtp地址 # 我用的是qq郵箱發(fā)送,此處應(yīng)該填寫騰訊qq郵箱的smtp值,即smtp.163.com, # 需要開啟授權(quán)碼撬碟, smtp_srv = "smtp.qq.com" try: import smtplib # 加密傳輸 #server = smtplib.SMTP_SSL(smtp_srv.encode(), 465) # SMTP協(xié)議默認(rèn)端口是25 # qq郵箱要求使用 TLS加密傳輸 server = smtplib.SMTP(smtp_srv.encode(), 25) # SMTP協(xié)議默認(rèn)端口是25 server.starttls() # 設(shè)置調(diào)試級別 # 通過設(shè)置調(diào)試等級匙监,可以清楚的看到發(fā)送郵件的交互步驟 server.set_debuglevel(1) # 登錄發(fā)送郵箱 server.login(from_addr, from_pwd) server.sendmail(from_addr, [to_addr], msg.as_string()) server.quit() except Exception as e: print(e)
使用smtplib模塊發(fā)送郵件
-
- POP3協(xié)議負(fù)責(zé)接受郵件
- 本質(zhì)上是MDA到MUA的一個(gè)過程
- 從 MDA 下載下來的是一個(gè)完整的郵件結(jié)構(gòu)體,需要解析才能得到每個(gè)具體可讀的內(nèi)容
- 步驟:
- 用poplib下載郵件結(jié)構(gòu)體原始內(nèi)容
- 準(zhǔn)備相應(yīng)的內(nèi)容(郵件地址小作,密碼亭姥,POP3實(shí)例)
- 身份認(rèn)證
- 一般會先得到郵箱內(nèi)郵件的整體列表
- 根據(jù)相應(yīng)序號,得到某一封信的數(shù)據(jù)流
- 利用解析函數(shù)進(jìn)行解析出相應(yīng)的郵件結(jié)構(gòu)體
- 用email解析郵件的具體內(nèi)容
- 用poplib下載郵件結(jié)構(gòu)體原始內(nèi)容
- 案例v12
# 導(dǎo)入相關(guān)包 # poplib負(fù)責(zé)從MDA到MUA下載 import poplib # 以下包負(fù)責(zé)相關(guān)郵件結(jié)構(gòu)解析 from email.parser import Parser from email.header import decode_header from email.utils import parseaddr # 得到郵件的原始內(nèi)容 # 這個(gè)過程主要負(fù)責(zé)從MDA到MUA的下載并使用Parse粗略解析 def getMsg(): # 準(zhǔn)備相應(yīng)的信息 email = "1441865605@qq.com" # 郵箱的授權(quán)碼 pwd = "ajwvzqdlfigahiae" # pop3服務(wù)器地址 pop3_srv = "pop.qq.com" # 端口995 # ssl代表是安全通道 srv = poplib.POP3_SSL(pop3_srv) # user代表email地址 srv.user(email) # pass_代表密碼 srv.pass_(pwd) # 以下操作根據(jù)具體業(yè)務(wù)具體使用 # stat返回郵件數(shù)量和占用空間 # 注意stat返回一個(gè)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負(fù)責(zé)返回一個(gè)具體索引號的一封信的內(nèi)容,此內(nèi)容不具有可讀性 # lines 存儲郵件的最原始文本的每一行 rsp, lines, octets = srv.retr(index) # 獲得整個(gè)郵件的原始文本 msg_count = b'\r\n'.join(lines).decode("utf-8") # 解析出郵件整個(gè)結(jié)構(gòu)體 # 參數(shù)是解碼后的郵件整體 msg = Parser().parsestr(msg_count) #關(guān)閉鏈接 srv.quit() return msg # 詳細(xì)解析得到的郵件內(nèi)容 # msg代表是郵件的原始內(nèi)容 # idnent代表的是郵件嵌套的層級 def parseMsg(msg, indent=0): ''' 1. 郵件完全可能是有嵌套格式 2. 郵件只有一個(gè)From静秆,To粮揉,Subject之類的信息 :param msg: :param indent: 描述郵件里面有幾個(gè)郵件MIMEXXX類型的內(nèi)容,展示的時(shí)候進(jìn)行相應(yīng)縮進(jìn) :return: ''' # 想辦法提取出頭部信息 # 只有在第一層的郵件中才會有相關(guān)內(nèi)容, # 此內(nèi)容只有一個(gè) if indent == 0: for header in ['From', "To", 'Subject']: # 使用get可以避免如果沒有相關(guān)關(guān)鍵字報(bào)錯(cuò)的可能性 # 如果沒有 關(guān)鍵字”From“抚笔, 我們使用 msg["From"]會報(bào)錯(cuò) value = msg.get(header, '') if value: # Subject中的內(nèi)容直接解碼就可以扶认,他是字符串類型 if header == 'Subject': value = decodeStr(value) # 如果是From和To字段,則內(nèi)容大概是 "我的郵箱<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)) # 下面代碼關(guān)注郵件內(nèi)容本身 # 郵件內(nèi)容中殊橙,有可能是multipart類型辐宾,也有可能是普通郵件類型 # 下面的解析使用遞歸方式 if (msg.is_multipart()): # 如果是multipart類型狱从,則調(diào)用遞歸解析 # 得到多部分郵件的一個(gè)基礎(chǔ)郵件部分 parts = msg.get_payload() # enumerate 函數(shù)是內(nèi)置函數(shù) # 作用是將一個(gè)列表,此處是parts叠纹,生成一個(gè)有索引和parts原內(nèi)容構(gòu)成的新的列表 # 例如 enumerate(['a', 'b', 'c']) 結(jié)果是: [(1,'a'), (2, 'b'), (3, 'c')] for n,part in enumerate(parts): # 一個(gè)字符串乘以一個(gè)數(shù)字的意思是對這個(gè)字符串進(jìn)行n倍擴(kuò)展 # 比如 ”aa" * 2 -> "aaaa" print("{0}spart: {1}".format(' '*indent, n)) parseMsg(part, indent+1) else: # 基礎(chǔ)類型 # get_content_type是系統(tǒng)提供函數(shù)季研,得到內(nèi)容類型 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: #不是文本內(nèi)容,則應(yīng)該是附件 print('{0}Attachment: {1}'.format(indent, content_type)) def decodeStr(s): ''' s代表一封郵件中From誉察,To与涡,Subject中的任一項(xiàng) 對s進(jìn)行解碼身堡,解碼是編碼的逆過程 :param s: :return: ''' value, charset = decode_header(s)[0] # charset完全可能為空 if charset: # 如果指定編碼鲤桥,則用指定編碼格式進(jìn)行解碼 value = value.decode(charset) return value def guessCharset(msg): ''' 猜測郵件的編碼格式 :param msg: :return: ''' # 調(diào)用現(xiàn)成的函數(shù) charset = msg.get_charset() if charset is None: # 找到內(nèi)容類型俊鱼,并轉(zhuǎn)換成小寫 content_type = msg.get("Content-Type", "").lower() pos = content_type.find("charset=") if pos >= 0: # 如果包含chraset谁榜,則內(nèi)容形如 charset=xxxx charset = content_type[pos+8:].strip() return charset if __name__ == "__main__": # 得到郵件的原始內(nèi)容 msg = getMsg() print(msg) # 精確解析郵件內(nèi)容 parseMsg(msg, 0)
- SMTP協(xié)議負(fù)責(zé)發(fā)送郵件