
Python 中的 urllib.parse 模块提供了很多解析和组建 URL 的函数。
解析url
urlparse() 函数可以将 URL 解析成 ParseResult 对象。对象中包含了六个元素,分别为:
- 协议(scheme)
- 域名(netloc)
- 路径(path)
- 路径参数(params)
- 查询参数(query)
- 片段(fragment)
1 | from urllib.parse import urlparse |
结果为:
1 | parsed_result 包含了 6 个元素 |
ParseResult 继承于 namedtuple,因此可以同时通过索引和命名属性来获取 URL 中各部分的值。
为了方便起见, ParseResult 还提供了 username、 password、 hostname、 port 对 netloc 进一步进行拆分。
1 | print('scheme :', parsed_result.scheme) |
结果为:
1 | scheme : http |
除了 urlparse() 之外,还有一个类似的 urlsplit() 函数也能对 URL 进行拆分,所不同的是, urlsplit() 并不会把 路径参数(params) 从 路径(path) 中分离出来。
当 URL 中路径部分包含多个参数时,使用 urlparse() 解析是有问题的:
1 | url='http://user:pwd@domain:80/path1;params1/path2;params2?query=queryarg#fragment' |
结果为:
1 | ParseResult(scheme='http', netloc='user:pwd@domain:80', path='/path1;params1/path2', params='params2', query='query=queryarg', fragment='fragment') |
这时可以使用 urlsplit() 来解析:
1 | from urllib.parse import urlsplit |
结果为:
1 | SplitResult(scheme='http', netloc='user:pwd@domain:80', path='/path1;params1/path2;params2', query='query=queryarg', fragment='fragment') |
若只是要将 URL 后的 fragment 标识拆分出来,可以使用 urldefrag() 函数:
1 | from urllib.parse import urldefrag |
结果为:
1 | DefragResult(url='http://user:pwd@domain:80/path1;params1/path2;params2?query=queryarg', fragment='fragment') |
组建URL
ParsedResult 对象和 SplitResult 对象都有一个 geturl() 方法,可以返回一个完整的 URL 字符串。
1 | print(parsed_result.geturl()) |
结果为:
1 | http://user:pwd@domain:80/path1;params1/path2;params2?query=queryarg#fragment |
但是 geturl() 只在 ParsedResult 和 SplitResult 对象中有,若想将一个普通的元组组成 URL,则需要使用 urlunparse() 函数:
1 | from urllib.parse import urlunparse |
结果为:
1 | http://user:pwd@domain:80/path1;params1/path2;params2?query=queryarg#fragment |
相对路径转换绝对路径
除此之外,urllib.parse 还提供了一个 urljoin() 函数,来将相对路径转换成绝对路径的 URL。
1 | from urllib.parse import urljoin |
结果为:
1 | http://www.example.com/path/anotherfile.html |
查询参数的构造和解析
使用 urlencode() 函数可以将一个 dict 转换成合法的查询参数:
1 | from urllib.parse import urlencode |
结果为:
1 | name=dark+sun&country=%E4%B8%AD%E5%9B%BD |
可以看到特殊字符也被正确地转义了。
相对的,可以使用 parse_qs() 来将查询参数解析成 dict。
1 | from urllib.parse import parse_qs |
结果为:
1 | {'name': ['dark sun'], 'country': ['中国']} |
如果只是希望对特殊字符进行转义,那么可以使用 quote 或 quote_plus 函数,其中 quote_plus 比 quote 更激进一些,会把 :、/ 一类的符号也给转义了。
1 | from urllib.parse import quote, quote_plus, urlencode |
结果为:
1 | urlencode : url=http%3A%2F%2Flocalhost%3A1080%2F%7Ehello%21%2F |
可以看到 urlencode 中应该是调用 quote_plus 来进行转义的。
逆向操作则使用 unquote 或 unquote_plus 函数:
1 | from urllib.parse import unquote, unquote_plus |
结果为:
1 | http://localhost:1080/~hello!/ |
你会发现 unquote 函数居然能正确地将 quote_plus 的结果转换回来。