python客户端软件开发_用 Python 实现一个简易版 HTTP 客户端
此文為《用 Python 擼一個 Web 服務(wù)器》系列教程的一個補充,這個系列教程介紹了如何使用 Python 內(nèi)置的 socket 庫實現(xiàn)一個簡易版的 Web 服務(wù)器。
之所以寫這篇文章,是因為我發(fā)現(xiàn)很多人并不清楚 HTTP 客戶端的概念,以為只有瀏覽器才叫 HTTP 客戶端。事實上并非如此,我們在 Web 開發(fā)中常見的 Postman、爬蟲程序、curl 命令行工具 等,這些都可以稱為 HTTP 客戶端。
服務(wù)器程序示例
這里以一個 Hello World 程序來作為示例服務(wù)器,實現(xiàn)如下:# server.py
import socket
import threading
def process_connection(client):
"""處理客戶端連接"""
# 接收客戶端發(fā)來的數(shù)據(jù)
data = b''
while True:
chunk = client.recv(1024)
data += chunk
if len(chunk) < 1024:
break
# 打印從客戶端接收的數(shù)據(jù)
print(f'data: {data}')
# 給客戶端發(fā)送響應(yīng)數(shù)據(jù)
client.sendall(b'HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n
Hello World
')# 關(guān)閉客戶端連接對象
client.close()
def main():
# 創(chuàng)建 socket 對象
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 允許端口復(fù)用
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# 綁定 IP 和端口
sock.bind(('127.0.0.1', 8000))
# 開始監(jiān)聽
sock.listen(5)
while True:
# 等待客戶端請求
client, addr = sock.accept()
print(f'client type: {type(client)}\naddr: {addr}')
# 創(chuàng)建新的線程來處理客戶端連接
t = threading.Thread(target=process_connection, args=(client,))
t.start()
if __name__ == '__main__':
main()
極簡客戶端
知道了如何用 socket 庫實現(xiàn)服務(wù)器端程序,那么理解客戶端程序的實現(xiàn)就非常容易了。客戶端程序代碼實現(xiàn)如下:# client.py
import socket
# 創(chuàng)建 socket 對象
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 指定服務(wù)器 IP 和端口,進行連接
sock.connect(('127.0.0.1', 8000))
# 向 URL "/" 發(fā)送 GET 請求
sock.send(b'GET / HTTP/1.1\r\nHost: 127.0.0.1:8000\r\n\r\n')
# 接收服務(wù)端響應(yīng)數(shù)據(jù)
data = b''
while True:
chunk = sock.recv(1024)
data += chunk
if len(chunk) < 1024:
break
# 打印響應(yīng)數(shù)據(jù)
print(data)
# 關(guān)閉連接
sock.close()
相對來說客戶端程序要簡單一些,創(chuàng)建 socket 對象的代碼與服務(wù)器端程序并無差別,客戶端 socket 對象根據(jù) IP 和端口來連接指定的服務(wù)器,建立好連接后就可以發(fā)送數(shù)據(jù)了,這里根據(jù) HTTP 協(xié)議構(gòu)造了一個針對 / URL 路徑的 GET 請求,為了簡單起見,請求頭中僅攜帶了 HTTP 協(xié)議規(guī)定的必傳字段 Host,請求發(fā)送成功后便可以接收服務(wù)器端響應(yīng),最后別忘了關(guān)閉 socket連接。
僅用幾行代碼,我們就實現(xiàn)了一個極簡的 HTTP 客戶端程序,接下來對其進行測試。
首先在終端中使用 Python 運行服務(wù)器端程序:python3 server.py。然后在另一個終端中使用 Python 運行客戶端程序:python3 client.py。
可以看到客戶端打印結(jié)果如下:b'HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n
Hello World
'以上,我們實現(xiàn)了一個極簡的 HTTP 客戶端。
參考 requests 實現(xiàn)客戶端
用 Python 寫過爬蟲的同學(xué),一定聽說或使用過 requests 庫,以下是使用 requests 訪問 Hello World 服務(wù)端程序的示例代碼:# demo_requests.py
import requests
response = requests.request('GET', 'http://127.0.0.1:8000/')
print(response.status_code) # 響應(yīng)狀態(tài)碼
print('--------------------')
print(response.headers) # 響應(yīng)頭
print('--------------------')
print(response.text) # 響應(yīng)體
在終端中使用 python3 demo_requests.py 運行此程序,將打印如下結(jié)果:200
--------------------
{'Content-Type': 'text/html'}
--------------------
Hello World
接下來修改我們上面實現(xiàn)的極簡 HTTP 客戶端程序,使其能夠支持 response.status_code、response.headers 和 response.text功能。# client.py
import socket
from urllib.parse import urlparse
class HTTPClient(object):
"""HTTP 客戶端"""
def __init__(self):
# 創(chuàng)建 socket 對象
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 初始化數(shù)據(jù)
self.status_code = 200
self.headers = {}
self.text = ''
def __del__(self):
# 關(guān)閉連接
self.sock.close()
def connect(self, ip, port):
"""建立連接"""
self.sock.connect((ip, port))
def request(self, method, url):
"""請求"""
# URL 解析
parse_result = urlparse(url)
ip = parse_result.hostname
port = parse_result.port or 80
host = parse_result.netloc
path = parse_result.path
# 建立連接
self.connect(ip, port)
# 構(gòu)造請求數(shù)據(jù)
send_data = f'{method} {path} HTTP/1.1\r\nHost: {host}\r\n\r\n'.encode('utf-8')
# 發(fā)送請求
self.sock.send(send_data)
# 接收服務(wù)端響應(yīng)的數(shù)據(jù)
data = self.recv_data()
# 解析響應(yīng)數(shù)據(jù)
self.parse_data(data)
def recv_data(self):
"""接收數(shù)據(jù)"""
data = b''
while True:
chunk = self.sock.recv(1024)
data += chunk
if len(chunk) < 1024:
break
return data.decode('utf-8')
def parse_data(self, data):
"""解析數(shù)據(jù)"""
header, self.text = data.split('\r\n\r\n', 1)
status_line, header = header.split('\r\n', 1)
for item in header.split('\r\n'):
k, v = item.split(': ')
self.headers[k] = v
self.status_code = status_line.split(' ')[1]
if __name__ == '__main__':
client = HTTPClient()
client.request('GET', 'http://127.0.0.1:8000/')
print(client.status_code)
print('--------------------')
print(client.headers)
print('--------------------')
print(client.text)
代碼實現(xiàn)比較簡單,我寫了較為詳細的注釋,相信你能夠看懂。其中使用了內(nèi)置函數(shù) urlparse ,此函數(shù)能夠根據(jù) URL 格式規(guī)則將 URL 拆分成多個部分。
在終端中使用 python3 client.py 運行此程序,打印結(jié)果與使用 requests 的結(jié)果完全相同。200
--------------------
{'Content-Type': 'text/html'}
--------------------
Hello World
僅用幾十行代碼,我們就實現(xiàn)了一個簡易版的 HTTP 客戶端程序,并且還實現(xiàn)了類似 requests 庫的功能。
接下來你可以嘗試用它去訪問現(xiàn)實世界中真實的 URL,比如訪問 http://httpbin.org/get,看看打印結(jié)果如何。
P.S.
Web 開發(fā)本質(zhì)是圍繞著 HTTP 協(xié)議進行的,HTTP 協(xié)議是 Web 開發(fā)的基石。所以對于何為 HTTP 服務(wù)端、何為 HTTP 客戶端的概念不夠清晰的話,實際上都是對 HTTP 協(xié)議不夠理解。
最后,給大家留一道作業(yè)題,實現(xiàn) requests 庫的 response.json() 方法。
聯(lián)系我:
總結(jié)
以上是生活随笔為你收集整理的python客户端软件开发_用 Python 实现一个简易版 HTTP 客户端的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Linux 压缩和解压命令
- 下一篇: 死锁产生的原因及条件、如何避免死锁