Python-Net編程

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

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端流程
      1. 建立socket遥倦,socket是負(fù)責(zé)具體通信的一個(gè)實(shí)例
      2. 綁定忱叭,為創(chuàng)建的socket指派固定的端口和ip地址
      3. 接受對方發(fā)送內(nèi)容
      4. 給對方發(fā)送反饋,此步驟為非必須步驟
    • Client端流程
      1. 建立通信的socket
      2. 發(fā)送內(nèi)容到指定服務(wù)器
      3. 接受服務(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)
      
  • TCP編程

    • 面向鏈接的傳輸,即每次傳輸之前需要先建立一個(gè)鏈接
    • 客戶端和服務(wù)端兩個(gè)程序需要編寫
    • Server端的編寫流程
      1. 建立socket負(fù)責(zé)具體通信,這個(gè)socket其實(shí)只負(fù)責(zé)接受對方的請求还绘,真正通信的是鏈接后重新建立的socket
      2. 綁定端口和地址
      3. 監(jiān)聽接入的訪問socket
      4. 接受訪問的socket楚昭,可以理解接受訪問即建立了一個(gè)通訊的鏈接通路
      5. 接受對方的發(fā)送內(nèi)容,利用接收到的socket接受內(nèi)容
      6. 如果有必要拍顷,給對方發(fā)送反饋信息
      7. 關(guān)閉鏈接通路
    • Client端的編寫流程
      1. 建立通信socket
      2. 鏈接對方哪替,請求跟對方建立通路
      3. 發(fā)送內(nèi)容到對方服務(wù)器
      4. 接受對方的反饋
      5. 關(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工作流程

    1. 客戶端鏈接遠(yuǎn)程主機(jī)上的FTP服務(wù)器
    2. 客戶端輸入用戶名和密碼(或者“anonymous”和電子郵件地址)
    3. 客戶端和服務(wù)器進(jìn)行各種文件傳輸和信息查詢操作
    4. 客戶端從遠(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ū)

  • 流程

    1. MUA->MTA,郵件已經(jīng)在服務(wù)器上了
    2. qq MTA->........-> sina MTA, 郵件在新浪的服務(wù)器上
    3. sina MTA-> sina MDA, 此時(shí)郵件已經(jīng)在你的郵箱里了
    4. 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)容
      • 步驟:
        1. 用poplib下載郵件結(jié)構(gòu)體原始內(nèi)容
          1. 準(zhǔn)備相應(yīng)的內(nèi)容(郵件地址小作,密碼亭姥,POP3實(shí)例)
          2. 身份認(rèn)證
          3. 一般會先得到郵箱內(nèi)郵件的整體列表
          4. 根據(jù)相應(yīng)序號,得到某一封信的數(shù)據(jù)流
          5. 利用解析函數(shù)進(jìn)行解析出相應(yīng)的郵件結(jié)構(gòu)體
        2. 用email解析郵件的具體內(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)
        
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市龙优,隨后出現(xiàn)的幾起案子胖烛,更是在濱河造成了極大的恐慌产雹,老刑警劉巖谬莹,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異桩了,居然都是意外死亡附帽,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進(jìn)店門井誉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蕉扮,“玉大人,你說我怎么就攤上這事颗圣≡樱” “怎么了?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵在岂,是天一觀的道長奔则。 經(jīng)常有香客問我,道長蔽午,這世上最難降的妖魔是什么易茬? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任,我火速辦了婚禮及老,結(jié)果婚禮上抽莱,老公的妹妹穿的比我還像新娘。我一直安慰自己骄恶,他們只是感情好食铐,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著僧鲁,像睡著了一般虐呻。 火紅的嫁衣襯著肌膚如雪象泵。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天铃慷,我揣著相機(jī)與錄音单芜,去河邊找鬼。 笑死犁柜,一個(gè)胖子當(dāng)著我的面吹牛洲鸠,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播馋缅,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼扒腕,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了萤悴?” 一聲冷哼從身側(cè)響起瘾腰,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎覆履,沒想到半個(gè)月后蹋盆,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡硝全,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年栖雾,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片伟众。...
    茶點(diǎn)故事閱讀 37,997評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡析藕,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出凳厢,到底是詐尸還是另有隱情账胧,我是刑警寧澤,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布先紫,位于F島的核電站治泥,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏遮精。R本人自食惡果不足惜车摄,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望仑鸥。 院中可真熱鬧吮播,春花似錦、人聲如沸眼俊。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽疮胖。三九已至环戈,卻和暖如春闷板,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背院塞。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工遮晚, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人拦止。 一個(gè)月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓县遣,卻偏偏與公主長得像,于是被迫代替她去往敵國和親汹族。 傳聞我的和親對象是個(gè)殘疾皇子萧求,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評論 2 345

推薦閱讀更多精彩內(nèi)容