用 Python 实现一个简易版 HTTP 客户端

简介: 用 Python 实现一个简易版 HTTP 客户端

此文为《用 Python 撸一个 Web 服务器》系列教程的一个补充,这个系列教程介绍了如何使用 Python 内置的 socket 库实现一个简易版的 Web 服务器。

之所以写这篇文章,是因为我发现很多人并不清楚 HTTP 客户端的概念,以为只有浏览器才叫 HTTP 客户端。事实上并非如此,我们在 Web 开发中常见的 Postman爬虫程序curl 命令行工具 等,这些都可以称为 HTTP 客户端。

服务器程序示例

这里以一个 Hello World 程序来作为示例服务器,实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# server.py
import socket
import threading
defprocess_connection(client):
"""处理客户端连接"""
# 接收客户端发来的数据
    data = b''
whileTrue:
        chunk = client.recv(1024)
        data += chunk
if len(chunk) < 1024:
break
# 打印从客户端接收的数据
    print(f'data: {data}')
# 给客户端发送响应数据
    client.sendall(b'HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n<h1>Hello World</h1>')
# 关闭客户端连接对象
    client.close()
defmain():
# 创建 socket 对象
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 允许端口复用
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# 绑定 IP 和端口
    sock.bind(('127.0.0.1', 8000))
# 开始监听
    sock.listen(5)
whileTrue:
# 等待客户端请求
        client, addr = sock.accept()
        print(f'client type: {type(client)}\naddr: {addr}')
# 创建新的线程来处理客户端连接
        t = threading.Thread(target=process_connection, args=(client,))
        t.start()
if __name__ == '__main__':
    main()

服务器端程序不做过多解释,如有不明白的地方可以参考 用 Python 撸一个 Web 服务器-第2章:Hello-World 一节。

极简客户端

知道了如何用 socket 库实现服务器端程序,那么理解客户端程序的实现就非常容易了。客户端程序代码实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# client.py
import socket
# 创建 socket 对象
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 指定服务器 IP 和端口,进行连接
sock.connect(('127.0.0.1', 8000))
# 向 URL "/" 发送 GET 请求
sock.send(b'GET / HTTP/1.1\r\nHost: 127.0.0.1:8000\r\n\r\n')
# 接收服务端响应数据
data = b''
whileTrue:
    chunk = sock.recv(1024)
    data += chunk
if len(chunk) < 1024:
break
# 打印响应数据
print(data)
# 关闭连接
sock.close()

相对来说客户端程序要简单一些,创建 socket 对象的代码与服务器端程序并无差别,客户端 socket 对象根据 IP 和端口来连接指定的服务器,建立好连接后就可以发送数据了,这里根据 HTTP 协议构造了一个针对 / URL 路径的 GET 请求,为了简单起见,请求头中仅携带了 HTTP 协议规定的必传字段 Host,请求发送成功后便可以接收服务器端响应,最后别忘了关闭 socket连接。

仅用几行代码,我们就实现了一个极简的 HTTP 客户端程序,接下来对其进行测试。

首先在终端中使用 Python 运行服务器端程序:python3 server.py。然后在另一个终端中使用 Python 运行客户端程序:python3 client.py

可以看到客户端打印结果如下:

1
b'HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n<h1>Hello World</h1>'

以上,我们实现了一个极简的 HTTP 客户端。

参考 requests 实现客户端

用 Python 写过爬虫的同学,一定听说或使用过 requests 库,以下是使用 requests 访问 Hello World 服务端程序的示例代码:

1
2
3
4
5
6
7
8
9
10
# demo_requests.py
import requests
response = requests.request('GET', 'http://127.0.0.1:8000/')
print(response.status_code)  # 响应状态码
print('--------------------')
print(response.headers)  # 响应头
print('--------------------')
print(response.text)  # 响应体

在终端中使用 python3 demo_requests.py 运行此程序,将打印如下结果:

1
2
3
4
5
200
--------------------
{'Content-Type': 'text/html'}
--------------------
<h1>Hello World</h1>

接下来修改我们上面实现的极简 HTTP 客户端程序,使其能够支持 response.status_coderesponse.headersresponse.text功能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# client.py
import socket
from urllib.parse import urlparse
classHTTPClient(object):
"""HTTP 客户端"""
def__init__(self):
# 创建 socket 对象
        self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 初始化数据
        self.status_code = 200
        self.headers = {}
        self.text = ''
def__del__(self):
# 关闭连接
        self.sock.close()
defconnect(self, ip, port):
"""建立连接"""
        self.sock.connect((ip, port))
defrequest(self, method, url):
"""请求"""
# URL 解析
        parse_result = urlparse(url)
        ip = parse_result.hostname
        port = parse_result.port or80
        host = parse_result.netloc
        path = 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)
# 接收服务端响应的数据
        data = self.recv_data()
# 解析响应数据
        self.parse_data(data)
defrecv_data(self):
"""接收数据"""
        data = b''
whileTrue:
            chunk = self.sock.recv(1024)
            data += chunk
if len(chunk) < 1024:
break
return data.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)
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)

代码实现比较简单,我写了较为详细的注释,相信你能够看懂。其中使用了内置函数 urlparse ,此函数能够根据 URL 格式规则将 URL 拆分成多个部分。

在终端中使用 python3 client.py 运行此程序,打印结果与使用 requests 的结果完全相同。

1
2
3
4
5
200
--------------------
{'Content-Type': 'text/html'}
--------------------
<h1>Hello World</h1>

仅用几十行代码,我们就实现了一个简易版的 HTTP 客户端程序,并且还实现了类似 requests 库的功能。

接下来你可以尝试用它去访问现实世界中真实的 URL,比如访问 http://httpbin.org/get,看看打印结果如何。

P.S.

Web 开发本质是围绕着 HTTP 协议进行的,HTTP 协议是 Web 开发的基石。所以对于何为 HTTP 服务端、何为 HTTP 客户端的概念不够清晰的话,实际上都是对 HTTP 协议不够理解。

最后,给大家留一道作业题,实现 requests 库的 response.json() 方法。

相关文章
|
1天前
|
机器学习/深度学习 JSON API
HTTP协议实战演练场:Python requests库助你成为网络数据抓取大师
在数据驱动的时代,网络数据抓取对于数据分析、机器学习等至关重要。HTTP协议作为互联网通信的基石,其重要性不言而喻。Python的`requests`库凭借简洁的API和强大的功能,成为网络数据抓取的利器。本文将通过实战演练展示如何使用`requests`库进行数据抓取,包括发送GET/POST请求、处理JSON响应及添加自定义请求头等。首先,请确保已安装`requests`库,可通过`pip install requests`进行安装。接下来,我们将逐一介绍如何利用`requests`库探索网络世界,助你成为数据抓取大师。在实践过程中,务必遵守相关法律法规和网站使用条款,做到技术与道德并重。
7 2
|
3天前
|
数据采集 存储 JSON
从零到一构建网络爬虫帝国:HTTP协议+Python requests库深度解析
在网络数据的海洋中,网络爬虫遵循HTTP协议,穿梭于互联网各处,收集宝贵信息。本文将从零开始,使用Python的requests库,深入解析HTTP协议,助你构建自己的网络爬虫帝国。首先介绍HTTP协议基础,包括请求与响应结构;然后详细介绍requests库的安装与使用,演示如何发送GET和POST请求并处理响应;最后概述爬虫构建流程及挑战,帮助你逐步掌握核心技术,畅游数据海洋。
18 3
|
8天前
|
数据采集 网络协议 API
HTTP协议大揭秘!Python requests库实战,让网络请求变得简单高效
【9月更文挑战第13天】在数字化时代,互联网成为信息传输的核心平台,HTTP协议作为基石,定义了客户端与服务器间的数据传输规则。直接处理HTTP请求复杂繁琐,但Python的`requests`库提供了一个简洁强大的接口,简化了这一过程。HTTP协议采用请求与响应模式,无状态且结构化设计,使其能灵活处理各种数据交换。
37 8
|
12天前
|
JSON API 开发者
Python网络编程新纪元:urllib与requests库,让你的HTTP请求无所不能
【9月更文挑战第9天】随着互联网的发展,网络编程成为现代软件开发的关键部分。Python凭借简洁、易读及强大的特性,在该领域展现出独特魅力。本文介绍了Python标准库中的`urllib`和第三方库`requests`在处理HTTP请求方面的优势。`urllib`虽API底层但功能全面,适用于深入控制HTTP请求;而`requests`则以简洁的API和人性化设计著称,使HTTP请求变得简单高效。两者互补共存,共同推动Python网络编程进入全新纪元,无论初学者还是资深开发者都能从中受益。
31 7
|
2天前
|
Python
HTTP协议不再是迷!Python网络请求实战,带你走进网络世界的奥秘
本文介绍了HTTP协议,它是互联网信息传递的核心。作为客户端与服务器通信的基础,HTTP请求包括请求行、头和体三部分。通过Python的`requests`库,我们可以轻松实现HTTP请求。本文将指导你安装`requests`库,并通过实战示例演示如何发送GET和POST请求。无论你是想获取网页内容还是提交表单数据,都能通过简单的代码实现。希望本文能帮助你在Python网络请求的道路上迈出坚实的一步。
10 0
|
13天前
|
存储 JSON API
Python编程:解析HTTP请求返回的JSON数据
使用Python处理HTTP请求和解析JSON数据既直接又高效。`requests`库的简洁性和强大功能使得发送请求、接收和解析响应变得异常简单。以上步骤和示例提供了一个基础的框架,可以根据你的具体需求进行调整和扩展。通过合适的异常处理,你的代码将更加健壮和可靠,为用户提供更加流畅的体验。
39 0
|
27天前
|
Linux Python
【Azure 应用服务】Azure App Service For Linux 上实现 Python Flask Web Socket 项目 Http/Https
【Azure 应用服务】Azure App Service For Linux 上实现 Python Flask Web Socket 项目 Http/Https
|
1月前
|
网络协议 Python
python requests库如何使用http连接池降低延迟 keepalive复用连接
Python的`requests`库通过内置的连接池机制支持HTTP Keep-Alive特性,允许复用TCP连接以发送多个请求,减少连接开销。默认情况下,`requests`不显式禁用Keep-Alive,其行为取决于底层HTTP库(如urllib3)及服务器的支持。通过创建`Session`对象并自定义`HTTPAdapter`,可以调整连接池大小和重试策略,进一步优化连接复用。测试显示,使用`Session`和定制的`HTTPAdapter`比普通请求方法能显著减少连续请求间的时间消耗,体现了Keep-Alive的优势。
|
Web App开发 前端开发
|
Web App开发 监控 前端开发
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html><head><meta http-equiv="Cont
负载均衡: LVS(Layer 4), HAProxy(Layer 4、 7),Nginx(Layer 7) 虚拟化: LXC、KVM、Xen HA:Keepalived、Heartbeat 分布式缓存...
751 0