当前位置: 首页 > 后端技术 > Python

使用Python实现一个简单的HTTP客户端

时间:2023-03-25 21:32:01 Python

本文是《用 Python 撸一个 Web 服务器》系列教程的补充。本系列教程介绍如何使用Python内置的socket库实现一个简易版的Web服务器。之所以写这篇文章,是因为发现很多人不理解HTTP客户端的概念,认为只有浏览器才叫HTTP客户端。事实上,情况并非如此。我们在Web开发中经常看到的Postman、爬虫程序、curl命令行工具等都可以称为HTTP客户端。服务器程序示例这里以一个HelloWorld程序作为示例服务器,实现如下:#server.pyimportsocketimportthreadingdefprocess_connection(client):"""Processclientconnection"""#从客户端data接收数据=b''whileTrue:chunk=client.recv(1024)data+=chunkiflen(chunk)<1024:break#打印从客户端接收到的数据print(f'data:{data}')#发送一个响应客户端数据client.sendall(b'HTTP/1.1200OK\r\nContent-Type:text/html\r\n\r\n

HelloWorld

')#关闭客户端连接objectclient.close()defmain():#创建套接字对象sock=socket.socket(socket.AF_INET,socket.SOCK_STREAM)#允许端口重用sock.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)#绑定IPandportsock.bind(('127.0.0.1',8000))#开始监听sock.listen(5)whileTrue:#等待客户端请求client,addr=sock.accept()print(f'clienttype:{type(client)}\naddr:{addr}')#创建一个新线程处理客户端连接t=threading.Thread(target=process_connection,args=(client,))t.start()if__name__=='__main__':main()服务端程序不用过多解释。如果有什么不懂的,可以参考使用Python运行Web服务器-第2章:Hello-World。极简客户端知道如何使用socket库实现服务端程序,很容易理解客户端程序的实现。客户端程序代码实现如下:#client.pyimportsocket#创建socket对象sock=socket.socket(socket.AF_INET,socket.SOCK_STREAM)#指定连接的服务器IP和端口sock.connect(('127.0.0.0.1',8000))#向URL"/"发送GET请求sock.send(b'GET/HTTP/1.1\r\nHost:127.0.0.1:8000\r\n\r\n')#接收服务器响应datadata=b''whileTrue:chunk=sock.recv(1024)data+=chunkiflen(chunk)<1024:break#printresponsedataprint(data)#关闭连接sock.close()相对客户端而言程序简单点说,创建套接字对象的代码和服务器端程序没什么区别。客户端套接字对象根据IP和端口连接到指定的服务器。建立连接后,就可以发送数据了。这里构造了一个HTTP协议对于/url路径的GET请求,为了简单起见,请求头只携带HTTP协议规定的必填字段Host。请求发送成功后,服务器可以收到响应。最后,不要忘记关闭套接字连接。仅仅几行代码,我们就实现了一个最小的HTTP客户端程序,然后进行了测试。首先在终端中使用Python运行服务器端程序:python3server.py。然后在另一个终端运行使用Python的客户端程序:python3client.py。可以看到客户端打印结果如下:b'HTTP/1.1200OK\r\nContent-Type:text/html\r\n\r\n

HelloWorld

'上面我们已经实现了一个简约的HTTP客户端。参考实现客户端的请求。用Python写过爬虫的同学一定听说过或者用过requests库。以下是使用requests访问HelloWorld服务器程序的示例代码:#demo_requests.pyimportrequestsresponse=requests.request('GET','http://127.0.0.1:8000/')print(response.status_code)#响应状态码print('----------------')print(response.headers)#响应头print('------------------')print(response.text)#使用python3demo_requests.py在终端中运行的响应主体该程序将打印以下结果:200------------------{'Content-Type':'text/html'}------------------

HelloWorld

接下来修改我们上面实现的极简HTTP客户端程序,使其可以支持response.status_code、response.headers和response.text功能。#client.pyimportsocketfromurllib.parseimporturlparseclassHTTPClient(object):"""HTTPclient"""def__init__(self):#创建套接字对象self.sock=socket.socket(socket.AF_INET,socket.SOCK_STREAM)#初始化数据self.status_code=200self.headers={}self.text=''def__del__(self):#关闭连接self.sock.close()defconnect(self,ip,port):"""建立连接"""self.sock.connect((ip,port))defrequest(self,method,url):"""request"""#URL解析parse_result=urlparse(url)ip=parse_result.hostnameport=parse_result.portor80host=parse_result.netlocpath=parse_result.path#建立连接self.connect(ip,port)#构造请求数据send_data=f'{method}{path}HTTP/1.1\r\nHost:{host}\r\n\r\n'.encode('utf-8')#发送请求self.sock.send(send_data)#从服务器接收数据responsedata=self.recv_data()#解析响应数据self.parse_data(data)defrecv_data(self):"""Receivedata"""data=b''whileTrue:chunk=self.sock.recv(1024)data+=chunkiflen(chunk)<1024:breakreturndata.decode('utf-8')defparse_data(self,data):"""解析数据"""header,self.text=data.split('\r\n\r\n',1)status_line,header=header.split('\r\n',1)foriteminheader.split('\r\n'):k,v=item.split(':')self.headers[k]=vself.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)代码实现比较简单,我写了比较详细的注释,相信你能看懂它使用内置函数urlparse,这个函数可以根据URL格式规则将URL拆分成多个部分。在带有python3client.py的终端中运行此程序打印出与使用requests完全相同的结果。200--------------------{'Content-Type':'text/html'}---------------------

HelloWorld

仅用几十行代码,我们就实现了一个简单版的HTTP客户端程序,同时也实现了类似requests库的功能。接下来可以尝试用它访问现实世界中的真实URL,比如http://httpbin.org/get,看看打印结果如何。附言Web开发的本质围绕着HTTP协议展开,HTTP协议是Web开发的基石。因此,如果对什么是HTTP服务器,什么是HTTP客户端的概念还不够清楚,其实是对HTTP协议缺乏了解。最后留一道作业题给大家实现requests库的response.json()方法。联系我:微信:jianghushinian邮箱:jianghushinian007@outlook.com博客地址:https://jianghushinian.cn/