六、python網(wǎng)絡(luò)編程(socket與http請(qǐng)求的python實(shí)現(xiàn))

要說(shuō)python優(yōu)雅在何處,與其他語(yǔ)言相比最為明顯的,那一定是網(wǎng)絡(luò)操作了。python可以讓我們用最少的語(yǔ)句寫出功能強(qiáng)大的程序,網(wǎng)絡(luò)操作有相當(dāng)多的開源庫(kù)可以使用,而不用像其他語(yǔ)言一樣,步驟繁瑣。接下來(lái)從低到高介紹python網(wǎng)絡(luò)編程

  1. socket網(wǎng)絡(luò)編程接口
    • 基本概念
      可以這樣來(lái)理解socket,完成一段網(wǎng)絡(luò)通信需要五個(gè)元素,協(xié)議族,協(xié)議類型,協(xié)議,目標(biāo)ip地址,目標(biāo)端口號(hào),這是由TCP/IP網(wǎng)絡(luò)結(jié)構(gòu)決定的。socket作為一種數(shù)據(jù)結(jié)構(gòu),將以上五個(gè)信息組合起來(lái),稱為套接字。根據(jù)面向連接與非連接,套接字類型分為Datagram與Stream兩種類型。Datagram套接字使用UDP協(xié)議,向目標(biāo)地址發(fā)送數(shù)據(jù)包,不保證可靠送達(dá),Stream使用TCP協(xié)議,是可靠的、具有差錯(cuò)與流量控制的傳輸協(xié)議,也就是說(shuō),stream類型的套接字傳輸信息,具有較好的可靠性,而Datagram類型的套接字具有更高的實(shí)時(shí)性。
    • 初始化流式套接字的步驟
      • 服務(wù)端
        • 使用socket函數(shù)創(chuàng)建套接字
        • 綁定到指定的ip地址
        • 監(jiān)聽客戶端連接
        • 獲取客戶端連接后,使用返回的套接字收發(fā)數(shù)據(jù)
        • 通信結(jié)束,關(guān)閉套接字
      • 客戶端
        • 創(chuàng)建套接字
        • 連接指定ip地址
        • 連接成功后收發(fā)數(shù)據(jù)
        • 關(guān)閉套接字
      • 常用套接字函數(shù)



      • 例程
        • 時(shí)間戳服務(wù)器
                     #!/usr/bin/env python
           import socket
           from time import ctime
           
           
           host='127.0.0.1'
           port=8000
           bufsize=1024
           addr=(host, port)
           
           sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
           sock.bind(addr)
           sock.listen(5)
           while True:
               print 'waiting for connection...'
               clisock, cliaddr = sock.accept()
               print 'connected from', cliaddr
               print 'peername:', clisock.getpeername()
               data = clisock.recv(bufsize)
               if not data:
                   break
               clisock.send('[%s] %s' % (ctime(), data))
               clisock.close()
           sock.close()
          
        • 客戶端
           #!/usr/bin/env python
           from time import ctime
           import socket
           
           host='127.0.0.1'
           port=8000
           addr=(host, port)
           bufsize=1024
           
           
           while True:
               sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
               sock.connect(addr)
               data = raw_input(u'>')
               if not data:
                   break
               sock.send(data)
               data = sock.recv(bufsize)
               print data
                      #!/usr/bin/env python
           from time import ctime
           import socket
           
           host='127.0.0.1'
           port=8000
           addr=(host, port)
           bufsize=1024
           
           
           while True:
               sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
               sock.connect(addr)
               data = raw_input(u'>')
               if not data:
                   break
               sock.send(data)
               data = sock.recv(bufsize)
               print data
           
               sock.close()
               sock.close()
          
  2. 網(wǎng)絡(luò)http操作,使用urllib、urllib2、requests
    • 一般來(lái)說(shuō),進(jìn)行網(wǎng)絡(luò)操作時(shí)可以使用以上三個(gè)模塊任意一種,但是就功能上來(lái)說(shuō),requests較為強(qiáng)大,對(duì)底層模塊進(jìn)行了封裝,使用更為簡(jiǎn)便,我們重點(diǎn)來(lái)討論一下requests
    • http請(qǐng)求有幾種類型,最常用的是get與post,get請(qǐng)求向指定的資源請(qǐng)求數(shù)據(jù),GET 請(qǐng)求可被緩存,GET 請(qǐng)求保留在瀏覽器歷史記錄中,GET 請(qǐng)求可被收藏為書簽,GET 請(qǐng)求不應(yīng)在處理敏感數(shù)據(jù)時(shí)使用,GET 請(qǐng)求有長(zhǎng)度限制,GET 請(qǐng)求只應(yīng)當(dāng)用于取回?cái)?shù)據(jù)。post請(qǐng)求向指定的資源提交數(shù)據(jù),POST 請(qǐng)求不會(huì)被緩存,POST 請(qǐng)求不會(huì)保留在瀏覽器歷史記錄中,POST 不能被收藏為書簽,POST 請(qǐng)求對(duì)數(shù)據(jù)長(zhǎng)度沒(méi)有要求。還有一個(gè)區(qū)別是,使用get請(qǐng)求時(shí),參數(shù)直接顯示在url中,安全性不高,而post請(qǐng)求將參數(shù)包含在請(qǐng)求體中。
    • 使用requests
      • 發(fā)出get與post請(qǐng)求
        In [8]: res = requests.get('http://www.baidu.com')
        In [9]: res2 = requests.post('http://www.baidu.com')
        
      • 設(shè)置請(qǐng)求參數(shù),通過(guò)params參數(shù)設(shè)置請(qǐng)求參數(shù),需要傳進(jìn)一個(gè)字典,且值為none的鍵不會(huì)出現(xiàn)在url中
        In [10]: para = {'username':'xiaozhi','password':'testpass'}
        In [11]: res3 = requests.get('http://www.baidu.com',params=para)
        In [12]: res3.url
        Out[12]: u'http://www.baidu.com/?username=xiaozhi&password=testpass'
        
      • 為post請(qǐng)求傳遞數(shù)據(jù)使用data參數(shù)
        In [3]: data = {'key':'value'}
        In [4]: res = requests.post('http://httpbin.org/post', data=data)
        
        In [38]: payload = (('key1', 'value1'), ('key1', 'value2'))
        In [39]: r = requests.post('http://httpbin.org/post', data=payload)
        
      • 獲取響應(yīng)內(nèi)容
        字符串內(nèi)容
        In [5]: res.text
        Out[5]: u'{"args":{},"data":"","files":{},"form":{"key":"value"},"headers":{"Accept":"*/*","Accept-Encoding":"gzip, deflate","Connection":"close","Content-Length":"9","Content-Type":"application/x-www-form-urlencoded","Host":"httpbin.org","User-Agent":"python-requests/2.18.4"},"json":null,"origin":"113.140.11.6","url":"http://httpbin.org/post"}\n'
        
        查看與設(shè)置內(nèi)容編碼
        In [8]: res.encoding
        In [9]: res.encoding='utf-8'
        
        使用content屬性可以查看返回內(nèi)容的字節(jié)內(nèi)容,python會(huì)自動(dòng)為我們解碼gzip壓縮的數(shù)據(jù)
        In [15]: res.content
        Out[15]: '{"args":{},"data":"","files":{},"form":{"key":"value"},"headers":{"Accept":"*/*","Accept-Encoding":"gzip, deflate","Connection":"close","Content-Length":"9","Content-Type":"application/x-www-form-urlencoded","Host":"httpbin.org","User-Agent":"python-requests/2.18.4"},"json":null,"origin":"113.140.11.6","url":"http://httpbin.org/post"}\n'
        
        json對(duì)象
        In [16]: res.json()
        Out[16]: 
        {u'args': {},
         u'data': u'',
         u'files': {},
         u'form': {u'key': u'value'},
         u'headers': {u'Accept': u'*/*',
          u'Accept-Encoding': u'gzip, deflate',
          u'Connection': u'close',
          u'Content-Length': u'9',
          u'Content-Type': u'application/x-www-form-urlencoded',
          u'Host': u'httpbin.org',
          u'User-Agent': u'python-requests/2.18.4'},
         u'json': None,
         u'origin': u'113.140.11.6',
         u'url': u'http://httpbin.org/post'}
        
        查看返回狀態(tài)碼
        res.status_code
        
        原始數(shù)據(jù)
        In [31]: res = requests.get('https://api.github.com/events', stream=True)
        
        In [32]: with open('temp.txt', 'wb') as fd:
            ...:     for chunk in res.iter_content(512):
            ...:         fd.write(chunk)
        
        查看返回的http頭部
        In [44]: r.headers
        Out[44]: {'Content-Length': '351', 'Via': '1.1 vegur', 'Server': 'gunicorn/19.8.1', 'Connection': 'keep-alive', 'Access-Control-Allow-Credentials': 'true', 'Date': 'Thu, 24 May 2018 12:13:11 GMT', 'Access-Control-Allow-Origin': '*', 'Content-Type': 'application/json'}
        
        使用headers參數(shù)定制請(qǐng)求頭
        In [33]: headers = {'user-agent':'myapp'}
        In [34]: res = requests.get('http://www.baidu.com', headers=headers)
        
        返回對(duì)象的cookies屬性保存了會(huì)話cookie,當(dāng)需要重用cookies時(shí),將cookie對(duì)象作為參數(shù)傳遞給請(qǐng)求的cookies參數(shù)
        res = requests.get(url,cookies=res.cookies)
        
        使用cookie模擬登陸豆瓣網(wǎng)站
        cookies = {}
        
        In [57]: raw_cookies='bid=-aa9Pl9eMB0; _pk_ref.100001.8cb4=%5B%22%22%2C%22%22%2C1527208498%2C%22http
            ...: s%3A%2F%2Fwww.baidu.com%2Flink%3Furl%3DFpVInsn4IT_ak-F56yaoXBUJPtBtueoUd3nVtc4NWZV4QBO8sdnc
            ...: vDJTEYLuefv5%26ck%3D1583.5.192.64.190.324.478.646%26shh%3Dwww.baidu.com%26wd%3D%26eqid%3D89
            ...: b868ea00032e0c000000035b06b157%22%5D; _pk_id.100001.8cb4=83b586dcea10d0e5.1519446169.2.1527
            ...: 209320.1519446169.; __utma=30149280.2141300691.1527208503.1527208503.1527208503.1; __utmz=3
            ...: 0149280.1527208503.1.1.utmcsr=baidu|utmccn=(organic)|utmcmd=organic; ll="118371"; _pk_ses.1
            ...: 00001.8cb4=*; __utmb=30149280.9.10.1527208503; __utmc=30149280; ps=y; push_noty_num=0; push
            ...: _doumail_num=0; __utmv=30149280.13784; ap=1; dbcl2="137847261:LmaeCQEh7Eo"; ck=x-vi; __utmt
            ...: =1'
        
        In [58]: for line in raw_cookies.split(';'):
            ...:     key,value = line.split('=', 1)
            ...:     cookies[key] = value
        In [64]: url = 'https://www.douban.com/people/xiaozhiAXX/'
        
        In [65]: res = requests.get(url,cookies=cookies)
        
        In [66]: res.status_code
        Out[66]: 200
        
        
        使用proxies參數(shù)設(shè)置代理,訪問(wèn)google
        In [1]: import requests
        
        In [2]: proxy = {'http':'http://127.0.0.1:8118','https':'https://127.0.0.1:8118'}
        
        In [3]: res = requests.get('https://www.google.com', proxies=proxy)
        
        In [4]: res.status_code
        Out[4]: 200
        
  3. 網(wǎng)絡(luò)客戶端
    • 電子郵件
      python發(fā)送電子郵件時(shí),使用標(biāo)準(zhǔn)庫(kù)中的smtplib和email,smptlib中有一個(gè)SMTP類,需要發(fā)送郵件時(shí),初始化該類返回smtpserver對(duì)象,使用login登陸MUA,使用sendmail方法發(fā)送郵件,郵件的正文用email.mime.text.MIMEText對(duì)象進(jìn)行描述
      簡(jiǎn)單電子郵件發(fā)送程序
      from email.mime.text import MIMEText
      msg = MIMEText('hello message','plain', 'utf-8')
      from_addr = 'yourPhone@163.com'
      to_addr = 'yourQQ@qq.com'
      sub_msg = 'hello'
      smtp_server = 'smtp.163.com'
      import smtplib
      # 初始化smtp對(duì)象,傳入服務(wù)器地址與端口號(hào)
      server = smtplib.SMTP(smtp_server,25)
      # 設(shè)置調(diào)試模式可以讓我們看到發(fā)送郵件過(guò)程中的信息
      server.set_debuglevel(1)
      # 登陸MUA,使用賬戶與授權(quán)碼登陸
      server.login(from_addr, 'yourpassword')
      msg['From'] = from_addr
      msg['To'] = to_addr
      msg['Subject'] = 'important message'
      server.sendmail(from_addr, [to_addr], msg.as_string())
      

      郵件被放入垃圾郵件中,如下



      發(fā)送帶附件的電子郵件

       from email.mime.text import MIMEText
       from smtplib import SMTP
       from email.mime.multipart import MIMEMultipart
       
       
       from_addr = '18392136027@163.com'
       to_addr = '1786614260@qq.com'
       smtp_server = 'smtp.163.com'
       smtp_port = 25
       subject_msg = 'subject'
      
       mul_msg = MIMEMultipart()
       mul_msg['From'] = from_addr
       mul_msg['To'] = to_addr
       mul_msg['Subject'] = subject_msg
      
       msg = MIMEText('\n\rimportant message\n\r', 'plain', 'utf-8')
       mul_msg.attach(msg)
      
       att1 = MIMEText(open('program.txt','rb').read(), 'base64', 'utf-8')
       att1['Content-Type'] = 'application/octet-stream'
       att1["Content-Disposition"] = 'attachment;filename="program.txt"'
       mul_msg.attach(att1)
      
       smtp = SMTP(smtp_server, smtp_port)
       smtp.login(from_addr, 'youpass')
       smtp.set_debuglevel(1)
       smtp.sendmail(from_addr, to_addr, mul_msg.as_string())
       smtp.close()
      

      使用第三方開源庫(kù)yagmail發(fā)送電子郵件

      import yagmail
      yag = yagmail.SMTP(user='youQQ@qq.com', password='you pass', host='smtp.qq.com', port=25)
      contents = ['import message','program.txt']
      yag.send(to='dest', subject='subject', contents=contents)
      
      使用pop3協(xié)議用網(wǎng)易郵箱發(fā)送郵件時(shí),容易被網(wǎng)易識(shí)別為垃圾郵件,可以使用qq郵箱
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容