Python3: HTTP 请求模块 - http.client 和 urllib.request

本文链接: https://blog.csdn.net/xietansheng/article/details/115557974

Python3 学习笔记(目录)

0. http 相关模块

Python 官网相关文档:

  1. http是一个包,有多个用于处理 http 协议的模块,如: http.client, http.server, http.cookies, http.cookiejar
  2. http模块中通过http.HTTPStatus枚举定义了一些 HTTP 的状态码,如: HTTPStatus.OK, HTTPStatus.NOT_FOUND 等。
  3. http.client模块是一个低层级的 HTTP 协议客户端,定义了实现 HTTP 和 HTTPS 协议的客户端类。通常不直接使用该模块,一般使用urllib.request(用于打开 URL 的可扩展库) 模块来实现 HTTP 和 HTTPS 请求。

1. HTTP 协议客户端: http.client

Python 官网: http.client — HTTP protocol client

1.1 HTTPConnection 对象

Python 官网:

HTTPConnection/HTTPSConnection实例表示一个与 HTTP/HTTPS 建立连接的事务,需要传递连接的主机名和端口号,不需要传入具体的 URL 资源路径。

HTTPConnection/HTTPSConnection 的构造方法:

# 创建 HTTP 连接实例
# host:      HTTP URL 的 主机名(可以是 "host:port" 的形式, port 会自动读取)
# port:      HTTP URL 的 端口, 默认为 80
# timeout:   连接超时时间, 浮点类型的 秒数
# blocksize: 用于发送文件类消息Body的缓冲区大小
class http.client.HTTPConnection(host, port=None, [timeout, ]
                                 source_address=None, blocksize=8192)


# 创建 HTTP 连接实例
# host:      HTTPS URL 的 主机名(可以是 "host:port" 的形式, port 会自动读取)
# port:      HTTPS URL 的 端口, 默认为 443
# timeout:   连接超时时间, 浮点类型的 秒数
# blocksize: 用于发送文件类消息Body的缓冲区大小
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 对象中的方法:

# 主动连接 HTTP 服务器, 可以不调用。
# 如果客户端没有建立连接, 该方法将在 request() 时自动被调用。
HTTPConnection.connect()


# 发送 HTTP 请求
#
# method: 请求方法, "GET", "POST" 等
# url: 请求的资源链接(可以只填写资源路径, 如: "/index.html"
# body: 请求的 body, 可以是 str, bytes/bytearray 或 file object;
#       str 将使用 iso-8859-1编码, file object 必须已打开并支持 read() 方法
# headers: 额外增加的 HTTP 请求头, 字典类型
HTTPConnection.request(method, url, body=None, headers={}, 
                       *, encode_chunked=False)


# 发出 request() 之后, 调用该方法返回一个 HTTPResponse 对象
HTTPConnection.getresponse()


# 设置调试等级, 默认为 0 表示不输出调试信息, 大于0表示输出调试信息到 stdout,
# 该值将传递给 HTTPResponse 对象
HTTPConnection.set_debuglevel(level)


# 为 HTTP 连接隧道设置主机和端口, 这将允许通过代理服务器进行连接
HTTPConnection.set_tunnel(host, port=None, headers=None)


# 关闭到服务器的连接
HTTPConnection.close()


# 用于发送文件类消息Body的缓冲区大小
HTTPConnection.blocksize

HTTPSConnection 继承自 HTTPConnection

1.2 HTTPResponse 对象

Python 官网:http.client.HTTPResponse Objects

HTTPResponse实例表示客户端发出请求之后,服务端的 HTTP 响应,包含 http code、响应头、响应内容的。HTTPResponse继承自io.BufferedIOBase,拥有 IO 字节流的相关操作方法。HTTPResponse对象支持with上下文管理器,在with语句退出时会自动调用其close()方法。调用HTTPConnection.close()方法关闭套接字连接时也会自动调用HTTPResponse.close()

# 读取 响应Body, amt 表示最多读取的字节数, 默认读取所有
HTTPResponse.read([amt])

# 读取 响应Body 到 buf 缓冲区, 最大的读取字节数为 buf 的长度,
# 返回读取的字节数
HTTPResponse.readinto(buf)

# 获取指定名称 响应头 的值, 如果有多个同名的 响应头name, 
# 则多个名称用 ", " 拼接返回, 没有对应的响应头, 返回 default
HTTPResponse.getheader(name, default=None)

# 获取所有的响应头, 返回一个列表, 列表元素为 (header, value)
HTTPResponse.getheaders()

# 返回基础套接字的 fileno
HTTPResponse.fileno()

# 关闭响应流, 一并不需要关闭, HTTPConnection.close() 时会自动关闭响应流。
HTTPResponse.close()


# 包含了所有响应头的 http.client.HTTPMessage 实例
HTTPResponse.msg

# HTTP 协议的版本: HTTP/1.0 为 10, HTTP/1.1 为 11
HTTPResponse.version

# HTTP 状态码, 200、404 等
HTTPResponse.status

# HTTP 原因短语, OK、NOT FOUND 等
HTTPResponse.reason

# 调试狗子, 如果大于0, 则读取和解析响应时, 消息将被输出到 stdout
HTTPResponse.debuglevel

# 流是否被关闭
HTTPResponse.closed

1.3 http.client 代码示例

httpbin.org 网站可以测试 HTTP/HTTPS 请求和响应的各种信息,如 Cookies、IP、Headers、参数、User-Agent 等,支持 GET、POST 等多种请求方式。

相关地址:http://httpbin.orghttps://github.com/postmanlabs/httpbin

GET 方式请求:

"""
请求连接: http://httpbin.org/get?name=Tom&age=25
"""
import http.client

# 创建于服务器的链接
conn = http.client.HTTPConnection("httpbin.org")

# 发出 get 请求, 请求url为: http://httpbin.org/get?name=Tom&age=25
conn.request("GET", "/get?name=Tom&age=25")

# 获取响应
resp = conn.getresponse()

# 打印响应状态等信息
print(resp.status, resp.reason, resp.version)

# 打印响应头
print(resp.msg)

# 打印响应Body的内容
print(resp.read().decode("utf-8"))

# 关闭连接
conn.close()

POST 方式请求:

"""
请求连接: http://httpbin.org/post
"""
import http.client

# 创建于服务器的链接
conn = http.client.HTTPConnection("httpbin.org")

# 发出 post 请求, 请求url为: http://httpbin.org/post
conn.request("POST", "/post", body="name=Tom&age=25")

# 获取响应
resp = conn.getresponse()

# 打印响应状态等信息
print(resp.status, resp.reason, resp.version)

# 打印响应头
print(resp.msg)

# 打印响应Body的内容
print(resp.read().decode("utf-8"))

# 关闭连接
conn.close()

请求 HTTPS 连接时如果报SSL: CERTIFICATE_VERIFY_FAILED的异常,可以在请求前加入下面代码,使用不校验的 HTTPS 上下文:

import ssl

# 默认的 HTTPS上下文 使用 不校验的上下文
ssl._create_default_https_context = ssl._create_unverified_context

# 或者 

# 在创建 HTTPS连接对象 时指定HTTPS上下文为 不校验的上下文
http.client.HTTPSConnection("httpbin.org", context=ssl._create_unverified_context())

2. urlopen() 与 Request 对象

Python 官网: urllib.request — 用于打开 URL 的可扩展库

2.1 urllib.request 中的函数

  • urllib.request.urlopen()函数:
# 打开一个链接或请求
#
# url:     链接 或 Request对象
#
# data:    请求Body中的字节数据, 可以是 bytes、bytearray、字节流打开的file对象。
#          有传该参数, 则使用 POST 方法请求, None 表示使用 GET 方法请求。
#
# timeout: 连接超时时间, 浮点类型的秒数
#
# cafile, capath: 为 HTTPS 请求指定一组授信的 CA 证书。
#                 cafile 为指向包含一堆 CA 证书的单个文件,
#                 capath 为指向 hash 证书文件的目录。
#                 如果不指定证书, HTTPS 请求时可以选择不校验证书。
# context: 描述 SSL 各种选项的 ssl.SSLContext 示例。
#          HTTPS 请求时如果不校验证书, 可以传 ssl._create_unverified_context()
#
# 返回 http.client.HTTPResponse 对象(使用完后需要关闭响应, 可以使用 with 语句)
#
urllib.request.urlopen(url, data=None, [timeout, ]
                       *, cafile=None, capath=None, 
                       cadefault=False, context=None)
  • 其他函数:
# 本地语法路径名 编码为 百分比表示的URL路径名(用于在 url 中的参数值的编码)
# 例如: "https://httpbin.org" -> "https%3A//httpbin.org" 
urllib.request.pathname2url(path)

# 百分比表示的URL路径名 解码为 本地语法路径名(用于在 url 中的参数值的解码)
# 例如: "https%3A//httpbin.org" -> "https://httpbin.org"
urllib.request.url2pathname(url)


# 返回 scheme 到 代理服务器 的 URL 映射(一个字典)
urllib.request.getproxies()


# 构建打开器
urllib.request.build_opener([handler, ...])
# 设置 urlopen 中默认的打开器
urllib.request.install_opener(opener)

2.2 请求对象: Request

Python 官网:

urllib.request.Request对象的方法和属性:

# 创建一个请求对象
#
# url:     请求链接
#
# data:    请求Body中的字节数据, 可以是 bytes、bytearray、字节流打开的file对象。
#          有传该参数, 则默认使用 POST 方法请求。
#
# headers: 请求头, 字典类型: {"key1": "value1", "key2": "value2"}
#
# method:  请求方法, "GET"、"POST" 等。默认为 "GET"; 如果有 data, 则默认为 "POST"。
#
class urllib.request.Request(url, data=None, headers={}, 
                             origin_req_host=None, unverifiable=False, 
                             method=None)


Request.full_url                # 设置 或 获取 请求连接
Request.get_full_url()          # 获取 请求连接
Request.selector                # 获取 URI 资源路径(url 中 host 后面的部分)

Request.method                  # 设置 或 获取 请求方法, "GET"、"POST" 等
Request.get_method()            # 获取 请求方法

Request.type                    # 获取类型, "http"、"https" 等
Request.host                    # 获取主机
Request.origin_req_host         # 获取请求的原始主机

Request.headers                                 # 设置 或 获取 所有请求头
Request.add_header(key, val)                    # 添加请求头
Request.get_header(header_name, default=None)   # 获取指定请求头的值
Request.has_header(header_name)                 # 是否有指定请求头
Request.remove_header(header_name)              # 移除请求头
Request.header_items()                          # 包含所有请求头的列表: [(key, value), ...]

Request.add_unredirected_header(key, header)    # 添加请求头, 如果发生重定向, 不会添加到重定向请求

Request.unverifiable                            # 请求是否不可校验
Request.set_proxy(host, type)                   # 设置代理服务器

创建 Request 对象,再发出GET请求:

import urllib.request

# 创建请求对象
req = urllib.request.Request("http://httpbin.org/get?name=Tom&age=25")

# 设置请求方法
req.method = "GET"
# 添加/覆盖请求头
req.add_header("User-Agent", "Mozilla/5.0")

# 发出请求, 返回响应对象 http.client.HTTPResponse
resp = urllib.request.urlopen(req)

# 打印响应状态等信息
print(resp.status, resp.reason, resp.version)
# 打印响应头
print(resp.getheaders())
# 打印响应Body的内容
print(resp.read().decode("utf-8"))

# 关闭响应流
resp.close()

创建 Request 对象,再发出POST请求:

import urllib.request

# 创建请求对象, 有 data 默认为 POST 请求, data 可以是 bytes、bytearray、字节流打开的file对象
req = urllib.request.Request("http://httpbin.org/post", data=b"name=Tom&age=25")

# 发出请求, 返回响应对象 http.client.HTTPResponse
resp = urllib.request.urlopen(req)

# 打印响应状态等信息
print(resp.status, resp.reason, resp.version)
# 打印响应头
print(resp.getheaders())
# 打印响应Body的内容
print(resp.read().decode("utf-8"))

# 关闭响应流
resp.close()

3. Cookie 处理: http.cookies 和 http.cookiejar

3.1 Cookies对象: SimpleCookie

Python 官网: http.cookies — HTTP state management

http.cookies.SimpleCookie表示一个简单的 Cookies 对象,继承自dict

SimpleCookie 创建简单示例:

>>> from http import cookies
>>> 
>>> C = cookies.SimpleCookie()      # 创建 Cookies 对象
>>> C["aa"] = "abc"
>>> C["bb"] = "123"
>>> 
>>> print(C)                        # 打印出 HTTP 响应头
Set-Cookie: aa=abc
Set-Cookie: bb=123
>>> 
>>> C["aa"].output()                # 生成 HTTP 响应头字符串
'Set-Cookie: aa=abc'
>>> 
>>> C["aa"]["Path"] = "/cookie"     # 添加其他属性
>>> C["aa"]["HttpOnly"] = True
>>> print(C["aa"])
Set-Cookie: aa=abc; HttpOnly; Path=/cookie
>>> 
>>> print(C["aa"].key)              # cookie 的 key
aa
>>> print(C["aa"].value)            # cookie 的 value
abc
>>> 
>>> for key in C:                   #  遍历 Cookie
...     print(C[key])
... 
Set-Cookie: aa=abc; HttpOnly; Path=/cookie
Set-Cookie: bb=123

官网 SimpleCookie 创建示例: http.cookies example

3.2 保存/添加 Cookies: http.cookiejar

Python 官网: http.cookiejar — Cookie handling for HTTP clients

http.cookiejar.Cookie表示一个 Cookie 对象:

http.cookiejar.Cookie(
        version,                    # 版本, >= 0
        name,                       # Cookie 的名字, str
        value,                      # Cookie 的值, str
        port,                       # 端口
        port_specified,             # 是否指定端口, 如果True, 则 port 必须指定
        domain,                     # 域名
        domain_specified,           # 是否指定域名
        domain_initial_dot,         # 是否域名点开头
        path,                       # 路径
        path_specified,             # 是否指定路径
        secure,                     # 是否是安全的
        expires,                    # 过期时间, 单位为秒的时间戳
        discard,                    # 是否丢弃(不保存)
        comment,                    # 描述
        comment_url,                # 描述链接
        rest,                       # 其他非标准的属性, 字典类型
        rfc2109=False               # 是否是 RFC 2109 标准
)

创建一个Cookie对象:

import http.cookiejar

# 创建一个 Cookie
cookie = http.cookiejar.Cookie(
        version=0,
        name="hello",
        value="world",
        port=None,
        port_specified=False,
        domain=".httpbin.org",
        domain_specified=True,
        domain_initial_dot=False,
        path="/",
        path_specified=True,
        secure=False,
        expires=3742443533,
        discard=False,
        comment=None,
        comment_url=None,
        rest={},
)

# 判断 Cookie 是否过期(默认与当前时间对比)
print(cookie.is_expired(now=None))


# 设置 指定名称的非标准属性值
cookie.set_nonstandard_attr("attr_name", "attr_value")
# 获取 指定名称的非标准属性值
cookie.get_nonstandard_attr("attr_name", default=None)
# 判断 是否有指定名称的非标准属性
cookie.has_nonstandard_attr("attr_name")

http.cookiejar.CookieJar用于管理 Cookie,手动设置 Cookie,给请求设置匹配的 Cookie,从响应中提取 Cookie 等。CookieJar的创建和常用方法:

# CookieJar 构造方法
class http.cookiejar.CookieJar(policy=None)

# 添加符合条件的 Cookie 到请求头中
CookieJar.add_cookie_header(request)

# 提取响应中的 Cookie
CookieJar.extract_cookies(response, request)

# 手动设置一个 Cookie
CookieJar.set_cookie(cookie)

# 清理一些 Cookie
CookieJar.clear(domain=None, path=None, name=None)
# 清理会话 Cookie
CookieJar.clear_session_cookies()

# 遍历 Cookie
for cookie in cookieJar:
    # 这里 cookie 的类型为 http.cookiejar.Cookie
    print(cookie.name + "=" + cookie.value)

CookieJar只支持在内存中保存,不能持久化,要想把 Cookie 保存为本地文件,可是使用FileCookieJarFileCookieJar没有实现保存方法,通常使用它的子类MozillaCookieJarLWPCookieJar:

# FileCookieJar 构造方法, filename 为加载和保存 Cookie 的默认文件路径
class http.cookiejar.FileCookieJar(filename, delayload=None, policy=None)

# 加载 Cookie
FileCookieJar.load(filename=None, ignore_discard=False, ignore_expires=False)

# 保存 Cookie
FileCookieJar.save(filename=None, ignore_discard=False, ignore_expires=False)

Cookie 添加、提取、保存 代码示例:

import ssl
import urllib.request
import http.cookiejar

# 默认的 HTTPS上下文 使用 不校验的上下文
ssl._create_default_https_context = ssl._create_unverified_context

# 创建 MozillaCookieJar 对象, 指定 Cookie 文件路径
cookieJar = http.cookiejar.MozillaCookieJar("cookies.txt")

# 从文件中加载 Cookie
cookieJar.load()

# 创建请求
req = urllib.request.Request("https://www.baidu.com")

# 从 CookieJar 中查找符合 req 请求的 Cookie 并添加到请求头中
cookieJar.add_cookie_header(req)

# 可以打印查看被添加的 Cookie
print(req.header_items())

# 打开请求, 获取响应
resp = urllib.request.urlopen(req)

# 输出响应内容
# print(resp.read().decode("utf-8"))

# 从响应中提取 Cookie 并保存到 CookieJar 中
cookieJar.extract_cookies(resp, req)

# 把内存中的 Cookie 保存到文件中
cookieJar.save()

# 关闭响应
resp.close()

手动设置 Cookie 代码示例:

import urllib.request
import http.cookiejar

# 创建 MozillaCookieJar 对象, 指定 Cookie 文件路径
cookieJar = http.cookiejar.MozillaCookieJar("cookies.txt")

# 从文件中加载 Cookie
cookieJar.load()

# 创建一个 Cookie: hello=world
cookie = http.cookiejar.Cookie(
    version=0,
    name="hello",
    value="world",
    port=None,
    port_specified=False,
    domain=".httpbin.org",
    domain_specified=True,
    domain_initial_dot=False,
    path="/",
    path_specified=True,
    secure=False,
    expires=3742443533,
    discard=False,
    comment=None,
    comment_url=None,
    rest={},
)

# 手动设置 Cookie 到 CookieJar 中
cookieJar.set_cookie(cookie)

# 创建请求
req = urllib.request.Request("http://httpbin.org/cookies")

# 从 CookieJar 中查找符合 req 请求的 Cookie 并添加到请求头中
cookieJar.add_cookie_header(req)

# 可以打印查看添加的 Cookie
print(req.header_items())

# 打开请求, 获取响应
resp = urllib.request.urlopen(req)

# 输出响应内容
print(resp.read().decode("utf-8"))

# 从响应中提取 Cookie 并保存到 CookieJar 中
cookieJar.extract_cookies(resp, req)

# 把内存中的 Cookie 保存到文件中
cookieJar.save()

# 关闭响应
resp.close()

4. Handler 与 Opener

处理器Handler 与 打开器Opener:

构建 Handler,自定义 Opener:

import urllib.request
import ssl

# 创建一个 HTTPHandler 处理器, 支持处理 HTTP 请求
handler = urllib.request.HTTPHandler()

# 创建一个 HTTPSHandler 处理器, 支持处理 HTTP/HTTPS 请求
# handler = urllib.request.HTTPSHandler(context=ssl._create_unverified_context())

# 通过 处理器handler 创建打开器 opener, 
# 可以传递多个 Handler: build_opener(handler1, handler2, ...), 也可以不传递 handler 创建默认的 Opener
opener = urllib.request.build_opener(handler)

# 把自定义的 opener 设置为 urllib.request.urlopen() 打开时默认用的 Opener
# urllib.request.install_opener(opener)

# 创建请求对象
req = urllib.request.Request("http://httpbin.org/")

# 用打开器打开请求, 返回响应
resp = opener.open(req)         # 打开 req 或者 url

# 输出响应内容
print(resp.read().decode("utf-8"))

5. Cookies 自动处理器: HTTPCookieProcessor

Python 官网: HTTPCookieProcessor Objects

import urllib.request
import http.cookiejar
import ssl

# 创建 CookieJar 管理 Cookie
cookiejar = http.cookiejar.CookieJar()

# 创建支持自动处理(提取/添加)Cookie 的 Handler
cookieHandler = urllib.request.HTTPCookieProcessor(cookiejar)

# 创建支持处理 HTTPS 请求的 Handler
httpsHandler = urllib.request.HTTPSHandler(context=ssl._create_unverified_context())

# 通过 多个handler 创建打开器 opener
opener = urllib.request.build_opener(cookieHandler, httpsHandler)

# 把自定义的 opener 设置为 urllib.request.urlopen() 打开时用的默认 Opener
# urllib.request.install_opener(opener)

# 创建请求对象, 返回将有 Cookie: id=123
req = urllib.request.Request("https://httpbin.org/cookies/set/id/123")

# 用打开器打开请求, 返回响应
resp = opener.open(req)         # 打开 req 或者 url

# 输出 cookiejar 中的 cookie
for cookie in cookiejar:
    print(cookie.name + "=" + cookie.value)

# 发出请求, 会自动添加 Cookie
resp = opener.open("https://httpbin.org/cookies")
print(resp.read().decode("utf-8"))

6. URL 解析: urllib.parse

Python 官网: urllib.parse — Parse URLs into components

urllib.parse主要用于将 URL 字符串分拆解析为其组件(scheme、host、query等),或将 URL 组件组合成 URL 字符串。

解析 URL:

>>> from urllib.parse import urlparse
>>> o = urlparse("http://www.cwi.nl:80/%7Eguido/Python.html?name=tom&age=25")
>>> o
ParseResult(scheme='http', netloc='www.cwi.nl:80', path='/%7Eguido/Python.html', 
            params='', query='name=tom&age=25', fragment='')
>>> o.scheme
'http'
>>> o.port
80
>>> o.query
'name=tom&age=25'

非标准 URL 的解析结果:

>>> from urllib.parse import urlparse
>>> 
>>> urlparse("//www.cwi.nl:80/%7Eguido/Python.html")
ParseResult(scheme='', netloc='www.cwi.nl:80', path='/%7Eguido/Python.html', 
            params='', query='', fragment='')
>>>
>>> urlparse("www.cwi.nl/%7Eguido/Python.html")
ParseResult(scheme='', netloc='', path='www.cwi.nl/%7Eguido/Python.html', 
            params='', query='', fragment='')
>>>
>>> urlparse("help/Python.html")
ParseResult(scheme='', netloc='', path='help/Python.html', 
            params='', query='', fragment='')

URL 组件组合:

>>> from urllib.parse import urljoin
>>> 
>>> urljoin("http://www.cwi.nl/%7Eguido/Python.html", "FAQ.html")
'http://www.cwi.nl/%7Eguido/FAQ.html'
>>> 
>>> urljoin("http://www.cwi.nl/%7Eguido/Python.html", 
...         "//www.python.org/%7Eguido") 
'http://www.python.org/%7Eguido'
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

谢TS

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值