本模块实现了HTTP和HTTPS协议的客户端功能。通常本模块不会被直接使用,而是被urllib.request调用,来处理HTTP和HTTPS相关的URL。
备注:HTTPS只有在支持SSL(带有ssl模块)的Python编译器里面才是可用的。
(一)
模块提供的类
class http.client.HTTPConnection(host, port=None, [timeout, ]source_address=None, blocksize=8192)
一个HTTPConnection实例表示和HTTP服务器的一次交互。实例化时,需要一个host和一个可选的port作为入参。在没有提供入参port的情况下,首先尝试判断host是不是符合host:port的格式:如果是,就从入参host中解析port;如果不是,此时会使用默认的80端口。如果可选的入参timeout有值,像建立连接之类的操作会在阻塞若干秒之后超时退出(如果没有显式的传递timeout,此时会使用系统默认的超时配置)。可选入参source_address可以是(host,port)形式的tuple,用来指定建立HTTP连接的时候使用的源地址。可选入参blocksize,用来指定发送消息体的缓冲区大小。
举个例子,下面的几个实例都是指向同一个服务器的同一个端口。
>>> h1 = http.client.HTTPConnection('www.python.org')
>>> h2 = http.client.HTTPConnection('www.python.org:80')
>>> h3 = http.client.HTTPConnection('www.python.org', 80)
>>> h4 = http.client.HTTPConnection('www.python.org', 80, timeout=10)
class http.client.HTTPSConnection(host, port=None, key_file=None, cert_file=None, [timeout, ]source_address=None, *, context=None, check_hostname=None, blocksize=8192)
HTTPConnection的子类,使用SSL和安全服务器通信。缺省的port是443。如果指定入参context,它必须是能够描述不同SSL选项的ssl.SSLContext实例。
class http.client.HTTPResponse
(sock, debuglevel=0, method=None, url=None)
该类会连接有响应的时候自动实例化,不需要用户直接实例化。
(二)
模块提供的函数
http.client.
parse_headers
(fp)
用来解析文件描述符中的请求头或者响应头信息。描述符指向的文件必须是可读的BufferedIOBase
(而不是文本文件),而且必须满足RFC 2822规定的头部样式。
本函数返回一个 http.client.HTTPMessage
实例,里面包含头部信息,但是没有请求体或者响应体(和HTTPResponse.msg以及http.server.BaseHTTPRequestHandler.headers类似)。当函数返回之后,文件描述符还可以继续读取HTTP消息实体。
备注:parse_headers()并不能够解析HTTP消息里面的请求行或者响应行,它只能够解析name:value形式的头部信息。在调用之前,需要确保文件描述符的首行消息已经被读取完毕。
(三)
HTTPConnection Objects
HTTPConnection.request(method, url, body=None, headers={}, *, encode_chunked=False)
调用本方法将会使用入参指定的method和url,发送一条HTTP请求到服务器。
如果指定入参body,body的数据会在发送完请求头之后发送。它可以是str类型、类bytes的对象、一个打开的文件对象,或者一个可迭代的bytes。如果body为字符串,它会被ISO-8859-1规范(HTTP默认规范)编码。如果body是类bytes对象,它会被直接发送。如果body是一个文件对象,要求该文件对象可读,如果文件对象是一个io.TextIOBase实例,调用read()返回的数据会通过ISO-8859-1规范编码之后再发送,其他情况下会文件数据被直接发送。如果body是一个可迭代对象,里面的所有数据都会被发送出去。
入参headers要求是一个映射对象,里面有想要发送给服务器的请求头信息。
在发送请求体的时候,如果headers里面没有Content-Length 也没有 Transfer-Encoding,它俩的其中之一会被自动添加。在要求有请求体的请求方法(PUT、POST、PATCH)中,如果请求体为空,Content-Length会被设为0。如果body是一个字符串或者类bytes对象,Content-Length会被设置为body的长度;其他情况下(body为文件对象或者可迭代对象),请求会被分块,自动设置实际的Transfer-Encoding,而不会使用Content-Length。
入参encode_chunked只有在headers里面包含Transfer-Encoding时才会被用到。如果encode_chunked为False,HTTPConnection对象会认为本次调用包含了全部的数据。如果encode_chunked为True,请求体会被分块。
备注:分块传输是HTTP/1.1添加的特性。如果服务器不支持HTTP/1.1,调用方要么指定Content-Length,要么需要将body指定为str类型或者类bytes对象。
HTTPConnection.getresponse()
本方法需要在发送完请求之后调用,来获取响应信息。其返回值为HTTPResponse实例。
备注:你需要在发送下一个请求之前,确保已经读取完毕所有的响应信息。
HTTPConnection.set_debuglevel(level)
本方法用来设置调试级别。默认的调试级别为0,即所有的调试输出都不会被打印。任何大于0的值,都会触发响应的调试级别,将调试信息打印到标准输出。调试级别会被随后创建的HTTPResponse实例继承。
HTTPConnection.set_tunnel(host, port=None, headers=None)
在使用HTTP连接隧道时,指定host和port。这样是为了能够使用代理连接。
host和port指定了连接的端点信息(即请求的服务器地址,而不是代理的地址)
举个例子,为了使用在本地8080端口运行的HTTPS代理服务,可以将代理地址传递给HTTPSConnection,然后调用set_tunnel()来指定真实的服务器地址。
>>> import http.client
>>> conn = http.client.HTTPSConnection("localhost", 8080)
>>> conn.set_tunnel("www.python.org")
>>> conn.request("HEAD","/index.html")
HTTPConnection.connect()
用来建立和服务器的连接。默认情况下,如果客户端没有建立连接,该方法会在请求的时会被自动被调用。
HTTPConnection.close()
关闭同服务器的连接。
HTTPConnection.blocksize
发送类文件消息时的缓冲区大小。
除了可以调用上面提到的request()方法,你也可以使用下面的四个方法,一步步的发送你的请求。
HTTPConnection.
putrequest
(method, url, skip_host=False, skip_accept_encoding=False)
同服务器建立连接后,本方法应该第一个被调用。它会发送一个包含method、url以及HTTP版本(HTTP/1.1)的请求行。如果想要禁用自动发送的Host和Accpet-Encoding请求头,可以为skip_host和skip_accept_encoding指定一个不是False的值。
HTTPConnection
.putheader(header, argument[,...])
本方法用来向服务器发送请求体,要求请求体符合RFC 822规范。它会发送一行包含header、一个冒号、一个空格以及第一个argument的请求头信息。如果argument有多个入参,请求消息的下一行会包含一个制表符和一个argument。
HTTPConnection
.endheaders(message_body=None, *, encode_chunked=False)
向服务器发送一个空行,标识请求头的结束。可选的入参message_body用来向请求消息中添加请求体。
如果encode_chunked为True,每个消息体会被按照RFC 7230规范( 3.3.1章节)分块。如何编码请求数据会根据message_body的类型来确定。如果message_body实现了python的buffer接口规范,编码后的消息会是一个单独的块。如果message_body是一个文件对象,每次调用read()读取的数据都是一个块。本方法会在message_body结束的时候自动在分块消息中添加结束信息。
备注:因为分块编码的特殊性,空块会被忽略。这是为了避免服务器因为畸形的编码提前结束对请求数据的接收。
HTTPConnection
.send(data)
发送数据给服务器。注意要在endheaders()方法被调用之后,getresponse()方法被调用之前调用本方法。
(四)
HTTPResponse Objects
一个HTTPResponse实例带有服务器的响应信息。也可以用它来查看请求头和请求体的数据。响应对象是一个可迭代对象。
HTTPResponse.
read
([amt])
读取并返回响应体数据,也可以跳转至指定的amt字节处。
HTTPResponse.
readinto
(b)
读取len(b)字节的数据并将其写入b。返回读取的字节长度。
HTTPResponse.
getheader
(name, default=None)
返回指定name的响应头内容,如果没有该响应体返回default值。如果响应头中有多个name头,其返回值以“,”连接。如果default不是单个字符串,而是可迭代对象,其返回值也会通过逗号连接。
HTTPResponse.
getheaders
()
返回一个列表,里面包含了(header,value)形式的tuple。
HTTPResponse.
fileno
()
返回套接字的描述符编号
HTTPResponse.
msg
http.client.HTTPMessage的实例,里面包含了响应体。http.client.HTTPMessage
是email.message.Message的子类。
HTTPResponse.
version
服务器使用HTTP协议版本。10代表HTTP/1.0,11代表HTTP/1.1
HTTPResponse.status
服务器返回的状态码
HTTPResponse.reason
服务器返回的状态信息
HTTPResponse.debuglevel
一个调试钩子。如果调试级别高于0,读取和解析响应数据时的调试消息会被打印在标准输出。
HTTPResponse.closed
如果连接关闭返回True。
(五)
演示示例
下面是一个GET请求的演示:
>>> import http.client
>>> conn = http.client.HTTPSConnection("www.python.org")
>>> conn.request("GET", "/")
>>> r1 = conn.getresponse()
>>> print(r1.status, r1.reason)
200 OK
>>> data1 = r1.read() # This will return entire content.
>>> # The following example demonstrates reading data in chunks.
>>> conn.request("GET", "/")
>>> r1 = conn.getresponse()
>>> while chunk := r1.read(200):
... print(repr(chunk))
b'<!doctype html>\n<!--[if"...
...
>>> # Example of an invalid request
>>> conn = http.client.HTTPSConnection("docs.python.org")
>>> conn.request("GET", "/parrot.spam")
>>> r2 = conn.getresponse()
>>> print(r2.status, r2.reason)
404 Not Found
>>> data2 = r2.read()
>>> conn.close()
下面是一个HEAD请求示例。注意HEAD请求永远不会返回响应体。
>>> import http.client
>>> conn = http.client.HTTPSConnection("www.python.org")
>>> conn.request("HEAD", "/")
>>> res = conn.getresponse()
>>> print(res.status, res.reason)
200 OK
>>> data = res.read()
>>> print(len(data))
0
>>> data == b''
True
下面是一个POST请求示例:
>>> import http.client, urllib.parse
>>> params = urllib.parse.urlencode({'@number': 12524, '@type': 'issue', '@action': 'show'})
>>> headers = {"Content-type": "application/x-www-form-urlencoded",
... "Accept": "text/plain"}
>>> conn = http.client.HTTPConnection("bugs.python.org")
>>> conn.request("POST", "", params, headers)
>>> response = conn.getresponse()
>>> print(response.status, response.reason)
302 Found
>>> data = response.read()
>>> data
b'Redirecting to <a href="http://bugs.python.org/issue12524">http://bugs.python.org/issue12524</a>'
>>> conn.close()
客户端的PUT请求和POST类似。不同之处在于服务端是不是支持通过PUT请求创建资源。需要注意的是,urllib.request.Reqest也可以通过指定method来使用自定义的HTTP方法。下面是一个PUT请求的示例:
>>> # This creates an HTTP message
>>> # with the content of BODY as the enclosed representation
>>> # for the resource http://localhost:8080/file
...
>>> import http.client
>>> BODY = "***filecontents***"
>>> conn = http.client.HTTPConnection("localhost", 8080)
>>> conn.request("PUT", "/file", BODY)
>>> response = conn.getresponse()
>>> print(response.status, response.reason)
200, OK
(五)
HTTPMessage Objects
一个http.client.HTTPMessage 实例包含了HTTP的响应头信息。它继承自email.message.Message类