在Python语言丰富功能中,网络通信扮演着至关重要的角色,urllib作为 Python 内置的基础通信库,主要用于实现简单的 HTTP 请求,其功能比较基础,包括创建 HTTP 请求、处理响应和解析数据等。此外,urllib库所具备的强大能力远不止于实现HTTP网络访问,它还能支持诸如file、ftp、imap、sftp、svn和telnet等多种协议类型的使用。更重要的是,urllib库还特别提供了parse模块工具供您使用,这个模块能够处理URL的标准接口,从而帮助您更轻松地解析,融合及转换URL的各个组成部分。以下是parse模块的提供的功能。
先来看一个标准的 URL 通讯协议有哪些要素
# 为了方便查看,做了折行,其实为一行内容
<scheme>://
<username>:<password>
@<netloc>:<port>/<path>;<params>?<query>#<fragment>
# 每个关键字意义
scheme: 网络协议,http,https
username:用户名,ftp常见到
password:密码,ftp常见到
netloc:域名,如:www.baidu.com
port:端口,如:80
path:路径
params:参数,使用 分号 分隔
query:查询阐述,使用 问号 分隔
fragment:锚点
一个标准的URL通常包含上边多个内容,在网络爬虫中,又需要大量的操作网络地址,因此Python提供了强有力的方法来轻松的完成这些功能。
- urlparse (链接解析器)
from urllib.parse import urlparse
result = urlparse("https://www.baidu.com/s;type=a;color=b?wd=python#abc")
print(type(result), result)
# <class 'urllib.parse.ParseResult'>
# ParseResult(
# scheme='https',
# netloc='www.baidu.com',
# path='/s',
# params='type=a;color=b',
# query='wd=python',
# fragment='abc')
urlparse 方法可以对 URL 做解析,并将结果封装到 ParseResult 类中,从中可以获取到当前链接的详细信息。
通过ParseResult 可以获取如下数据:
scheme: 通讯协议
netloc:域名
path:访问路径
params: 参数
query:查询参数
fragment: 锚点,用于页面定位
- urlunparse(链接组装器)
urlunparse 方法和上边的正好相反,此方法用来组装访问 URL 协议,唯一需要注意的是,urlunparse 的参数需要是一个可迭代对象,并且长度必须是 6,否则会提示参数长度异常的错误(6 个参数本质上也是对应了上边解析出来的 6 个参数,没有的参数可以使用空内容占位)。
data = ("https", "www.baidu.com", "s", "", "wd=query", "")
url = urlunparse(data)
print(url)
# https://www.baidu.com/s?wd=query
通过运行上述代码可以看出,给定指定的参数,即可拼装一个完整 URL 链接。
- URL参数解析器(urlsplit)
此方法和 urlparse 功能相似,但是对于 params 参数不再做解析并合并到了 path 中, 因此结果中只包含 5 个值。
from urllib.parse import urlparse, urlunparse, urlsplit
res = urlsplit("https://www.baidu.com/s;type=a;color=b?wd=python#abc")
print(res)
# SplitResult(
# scheme='https',
# netloc='www.baidu.com',
# path='/s;type=a;color=b',
# query='wd=python',
# fragment='abc')
- urlunsplit
功能和 urlunparse 类似, 逻辑也是 urlsplit 的反向操作,只不过需要传入长度为 5 的可迭代对象即可。具体的使用可参考上边 urlunparse 代码。
- urljoin(标准模板)
在上边两个拼接 URL 的方法中,虽然可以方便的组出一个完整的 URL 地址,但是所需参数要求严格,在实际的使用中还是有许多冗余操作,因此还提供另一个方法urljoin。在此方法中,只需提供一个基础的 URL 地址作为第一个参数, 再将新的链接作为第二个参数,方法中会自动分析 base_url 中 scheme, netloc 和 path 这 3 个内容, 并补充新链接缺失的部分,最后返回完整的结果。
from urllib.parse import urljoin
print(urljoin('http://www.example.com/path/to/file.html', 'foo.html'))
print(urljoin('http://www.example.com/path/to/file.html', 'bar/baz.html'))
print(urljoin('http://www.baidu.com/about.html', 'https://cuiqingcai.com/FAQ.html'))
print(urljoin('http:www.baidu.com/about.html', 'https://cuiqingcai.com/FAQ.html?question=2'))
print(urljoin('http://www.baidu.com?wd=abc', 'https://cuiqingcai.com/index.php'))
print(urljoin('http://www.baidu.com', '?category=2#comment', allow_fragments=True))
print(urljoin('www.baidu.com', '?category=2#comment'))
print(urljoin('www.baidu.com#conent', '?category=2'))
# http://www.example.com/path/to/foo.html
# http://www.example.com/path/to/bar/baz.html
# https://cuiqingcai.com/FAQ.html
# https://cuiqingcai.com/FAQ.html?question=2
# https://cuiqingcai.com/index.php
# http://www.baidu.com?category=2#comment
# www.baidu.com?category=2#comment
# www.baidu.com?category=2
从运行结果中可以看出, base_url 如果提供了scheme, netloc 和 path 这 3 个内容,在新链接里不存在就会补充这些内容,如果新链接中存在,就会继续使用新链接。通过 urljoin 即可轻松实现链接的合并。
- urlencode(GET 请求参数拼装器)
urlencode, 可以非常容易的构造 GET 请求参数,如下:
from urllib.parse import urlencode
params = {
'page': 1,
'per_page': 10,
'sort': 'stars',
}
base_url = 'https://api.example.com/search?'
url = base_url + urlencode(params)
print(url)
# https://api.example.com/search?page=1&per_page=10&sort=stars
需要注意的是 urlencode 虽然会拼接 query 参数,但是不会提供路径上的 ?字符。
- parse_qs query 参数转字典类型
这个方法是 urlencode 的反向操作,可以将 query 参数转为字典。
from urllib.parse import parse_qs
print(parse_qs("a=1&b=2&c=3"))
# {'a': ['1'], 'b': ['2'], 'c': ['3']}
- parse_qsl 参数转元组类型
使用方式通上,只不过会转为元组列表。
from urllib.parse import parse_qsl
print(parse_qsl("a=1&b=2&c=3"))
# [('a', '1'), ('b', '2'), ('c', '3')]
- quote 中文编码器
URL中只能包含ASCII字符集中的字符,而中文字符不属于ASCII字符集,所以在网络请求中如果参数有中文,可以使用UTF-8编码将中文字符转换为字节序列。然后,将每个字节的值表示为两个十六进制数字,并在前面加上百分号“%”。这样就可以将中文字符转换为URL编码格式。使用 quote 可以方便的完成这个功能。
from urllib.parse import quote
print(f"https://www.baidu.com/s?wd={quote('你好,世界')}")
# https://www.baidu.com/s?wd=%E4%BD%A0%E5%A5%BD%EF%BC%8C%E4%B8%96%E7%95%8C
- unquote 解码器
此方法为 quote 的方向操作,可以将已经编码的 URL 做解码。
from urllib.parse import unquote
print(unquote("https://www.baidu.com/s?wd=%E4%BD%A0%E5%A5%BD%EF%BC%8C%E4%B8%96%E7%95%8C"))
# https://www.baidu.com/s?wd=你好,世界