本模块定义了一个标准接口,既可以把 统一资源定位符 (URL)解析为若干部分(通信协议,网络地址和路径等),也可以把各个部分重新组合成URL,还可以利用“基本URL”把相对路径转化为绝对路径。

本模块设计之初就考虑到要兼容一系列和URL相关的RFC标准。目前能够支持的URL协议包括: file, ftp, gopher, hdl,http, https, imap, mailto, mms, nntp, prospero, rsync, rtsp, rtspu, sftp, shttp, sip, sips, snews, svn, svn+ssh, telnet, wais, ws, wss

urllib.parse定义的函数主要分为两类: URL解析和URL转义 。下面详细介绍一下本模块的细节。

1

URL 解析

URL解析函数聚焦于将URL分割为若干部分,以及将各个部分拼接为URL。

urllib.parse.urlparse(urlstring, scheme='', allow_fragments=True)

本函数将URL解析为6个部分,返回一个包含6个元素的 named tuple 。这么处理符合URL的通用结构:

scheme://netloc/path;parameters?query#fragment

每个元组的元素都是一个字符串(也有可能为空字符串)。这些结构并不会再进行更细致的划分(例如网络地址整个作为一个字符串),并且不会还原%的转义。除了path开头的斜杠,各个部分之间的分割符号并不会在解析结果中出现。举例来说:

>>> from urllib.parse import urlparse

>>> o = urlparse('http://www.cwi.nl:80/%7Eguido/Python.html')

>>> o

ParseResult(scheme='http', netloc='www.cwi.nl:80', path='/%7Eguido/Python.html',

params='', query='', fragment='')

>>> o.scheme

'http'

>>> o.port

80

>>> o.geturl()

'http://www.cwi.nl:80/%7Eguido/Python.html'

根据  RFC1808  定义的语法规则,urlparse模块仅能识别以‘//’开头的网络地址。其他的输入均会被当作相对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='')

通过  scheme  参数传入的地址协议仅会在URL不包含网络协议的时候才会用到。除了缺省的空字符(特殊情况下可以自动转化为b'')之外,该入参应该同urlstring保持类型一致(文本或者字节)。

如果  allow_fragments  入参为false,返回结果中的fragment部分将不会有具体值,而是 仅返回一个空字符串 。URL中的该部分会被解析为path或者query的一部分。

以named tuple形式出现的返回值,既可以通过索引读取,也可以通过别名读取。

属性

索引

值(如果不存在)

scheme

0

URL方案说明符

scheme parameter

netloc

1

网络位置部分

空字符串

path

2

分层路径

空字符串

params

3

最后路径元素的参数

空字符串

query

4

查询组件

空字符串

fragment

5

片段识别

空字符串

username

用户名

None

password

密码

None

hostname

主机名(小写)

None

port

端口号为整数(如果存在)

None


如果URL中出现了非法的端口地址,在读取获取解析结果中的port值时会抛出ValueError。稍后会有更多详细的说明。

如果netloc部分的方括号不匹配也会抛出ValueError。

如果netloc在NFKC规范(使用IDNA编码)中包含/, ?, #, @或者:,将会抛出ValueError异常。但是如果显式的这样传入这种值来拼装URL,不需要解析的情况下,不会抛异常。

作为named tuple的子类,解析结果中的某些特有方法和属性可能有特殊用途。举个例子,_replace()将会返回另一个替换指定部分之后的解析结果。

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

urllib.parse.parse_qs(qs, keep_blank_values=False, strict_parsing=False, encoding='utf-8', errors='replace', max_num_fields=None)

该函数用于解析application/x-www-form-urlencoded类型的query字符串。返回值为字典类型,其中字典的key是唯一的,字典的value是list类型。

keep_blank_values 用于决定通过%转义的空格是否当作空字符串处理。当入参值为true的时候,保留作为空字符串。缺省值为false,此时空格会被忽略,当作不存在。

strict_parsing用于决定如何处理解析错误。缺省状态下为false,此时将会忽略异常。如果置为true,遇到错误会抛出ValueError异常。

encoding和errors的含义和bytes.decode()类似,用于决定如何将含有%的转义字符解析为Unicode字符。

max_num_fields用于决定最多可以读取多少字段。设置之后,如果发现多于设置的值,将会抛出ValueError异常。

使用urllib.parse.urlencode()函数(入参doseq设置为True)可以将结果再次转换为query字符串。

urllib.parse.parse_qsl(qs, keep_blank_values=False, strict_parsing=False, encoding='utf-8', errors='replace', max_num_fields=None)

该函数用于解析application/x-www-form-urlencoded类型的字符串数据。返回值为name,value组合的list(同parse_qs的区别在于返回值类型)。

keep_blank_values   用于决定通过%转义的空格是否当作空字符串处理。当入参值为true的时候,保留作为空字符串。缺省值为false,此时空格会被忽略,当作不存在。

strict_parsing用于决定如何处理解析错误。缺省状态下为false,此时将会忽略异常。如果置为true,遇到错误会抛出ValueError异常。

encoding和errors的含义和bytes.decode()类似,用于决定如何将含有%的转义字符解析为Unicode字符。

max_num_fields 用于决定最多可以读取多少字段。设置之后,如果发现多于设置的值,将会抛出ValueError异常。

使用urllib.parse.urlencode()函数可以将结果再次转换为query字符串。

urllib.parse.urlunparse(parts)

将urlparse()返回的tuple重新组合成一个URL。parts参数接受任何包含6个元素的可迭代对象。返回结果同原始URL相比可能会有细微差别,但是保证含义是一致的,尤其是在原始URL包含非必需的分隔符时(举例来说,RFC明确指出“?”是有效的query字符串)。

urllib.parse.urlsplit(urlstring, scheme='', allow_fragments=True)

和urlparse()有些类似,区别在于本函数不会分割URL中的params部分。如果你觉得params是path的一部分(参考RFC 2396),此时应该使用本函数,而不是urlparse()。本函数的设计初衷为了能够单独处理path和params。函数的返回值为包含5个元素的named tuple:

(addressing scheme, network location, path, query, fragment identifier)

返回值是named tuple类型,既可以通过索引读取,也可以通过别名读取:

别名 索引 缺省值
scheme 0 URL的网络协议 协议规范
netloc 1 网络地址 空字符串
path 2 地址路径 空字符串
query 3 查询参数 空字符串
fragment 4 片段参数 空字符串
username 5 用户名 None
password 密码 None
hostname 主机地址(小写) None
port 存在的情况下为整数端口 None

如果URL中出现了非法的端口地址,在读取获取解析结果中的port值时会抛出ValueError。更多的说明可以翻阅 **解析结果的构成**

如果netloc部分的方括号不匹配也会抛出ValueError。

如果netloc在NFKC规范(使用IDNA编码)中包含/, ?, #, @或者:,将会抛出ValueError异常。但是如果显式的这样传入URL的各个部分,不需要解析的情况下,不会抛异常。    如果netloc部分的方括号不匹配也会抛出ValueError。      如果URL中出现了非法的端口地址,在读取获取解析结果中的port值时会抛出ValueError。更多的说明可以翻阅解析结果的构成。

urllib.parse.urlunsplit(parts)

将urlsplite()返回的tuple重新组合成一个URL。parts参数接受任何包含5个元素的可迭代对象。返回结果同原始URL相比可能会有细微差别,但是保证含义是一致的,尤其是在原始URL包含非必需的分隔符时(举例来说,RFC明确说明“?”是有效的query字符串)。

urllib.parse.urljoin(base, url, allow_fragments=True)

将基地址和url组合成一个绝对URL。需要注意的是,本函数会使用scheme, netloc以及(部分的)path,来补全相对URL中缺失的部分。举个例子:

>>> from urllib.parse import urljoin

>>> urljoin('http://www.cwi.nl/%7Eguido/Python.html', 'FAQ.html')

'http://www.cwi.nl/%7Eguido/FAQ.html

入参allow_fragments的含义同urlparse()一致。

注意: 即使url是一个绝对URL(以//或者scheme://开头),url中的host以及scheme也会被替换。举个例子:

>>> urljoin('http://www.cwi.nl/%7Eguido/Python.html',

'//www.python.org/%7Eguido')

'http://www.python.org/%7Eguido'

如果你不希望这样处理,可以使用urlsplit()以及urlunsplit()预先处理url,去掉scheme和netloc部分。

urllib.parse.urldefrag(url)

如果url中包含fragment,本函数将会返回一个不带有fragment的url,以及截取的fragment。如果url中不包含fragment,本函数将返回原始url和一个空字符串。

返回值是一个named tuple类型,既可以通过索引读取,也可以通过别名读取:

别名 索引 缺省值
url 0 不含fragment的url 空字符串
fragment 1 片段参数 空字符串

更多的说明可以翻阅解析结果的构成

urllib.parse.unwrap(url)

本函数用于从一个被包裹的URL(形如<URL:scheme://host/path>,<scheme://host/path>, URL:scheme://host/path or scheme://host/path)中摘取出实际URL。如果url没有被包裹,则返回原始值。

解析ASCII编码

URL解析函数设计之初是仅仅是为了处理字符类型。在实践中,往往需要处理ASCII编码和转义的URL。因此,本模块的URL解析函数都可以处理bytes,bytearray以及str类型的对象。

如果入参是str类型,返回值也会是str。如果入参是bytes或者bytearray,结果就会只有bytes数据。

如果试图在一个函数里面混合使用字符串和字节类型会抛出TypeError异常,如果试图使用不是ASCII类型的字节会抛出UnicodeDecodeError异常。

为了方便的将结果在字符串和字节之间转换,所有的URL解析函数的返回值要么带有encode()方法(字符串类型的返回值),要么带有decode() 方法(字节类型的返回值)。这些函数的使用方式和str(), bytes() 一样( 唯一的细微区别 在于默认的编码形式是‘ascii’,而不是‘utf-8’)。encode() 方法返回bytes类型,decode() 方法返回str类型。

如果调用方期望处理没有正确转义的URL(可能含有非ASCII字符),需要在调用URL解析函数之前自己将字节类型转化为字符。

上述文档介绍的细节仅适用于URL解析函数。URL转义函数在处理字节时有自己的一套规则,感兴趣的可以参考URL转义函数的文档。

解析结果的数据结构

urlparse(), urlsplit() 以及urldefrag() 函数的返回对象都是tuple的子类。这些子类独有的一些特性在介绍函数的时候提到过了,例如上一部分刚刚介绍过的编码和解码都是作为附加的方法出现的,另外还有解析结构还有一些方法如下:

urllib.parse.SplitResult.geturl()

该方法返回重新组合的url字符串,它和原始url的区别在于,它会将scheme转为小写,并去掉原来为空的一些部分:空parameter、空query以及空fragment。

对于urldefrag()函数而言,只会去掉空的fragment。对于urlsplit()和urlparse(),上面提到的差别都会生效。

多次将本函数的返回值传入解析函数,得到的结果将不会有任何变化。

>>> from urllib.parse import urlsplit

>>> url = 'HTTP://www.Python.org/doc/#'

>>> r1 = 1urlsplit(url)

>>> r1.geturl()

'http://www.Python.org/doc/'

>>> r2 = urlsplit(r1.geturl())

>>> r2.geturl()

'http://www.Python.org/doc/'

以str类型的处理为例,下面介绍几个解析结构的具体实现:

class urllib.parse.DefragResult(url, fragment)

urldefrag()返回结果的具体实现,结果包含str类型的数据。其encode()方法会返回一个DefrageResultBytes实例。

class urllib.parse.ParseResult(scheme, netloc, path, params, query, fragment)

urlparse()返回结果的具体实现,结果包含str类型的数据。其encode()方法会返回一个 ParseResultBytes 实例。

class urllib.parse.SplitResult(scheme, netloc, path, query, fragment)

urlsplit()返回结果的具体实现,结果包含str类型的数据。其encode()方法会返回一个 SplitResultBytes实例。

以bytes类型的处理为例,下面介绍几个解析结构的具体实现:

class urllib.parse.DefragResultBytes(url, fragment)

urldefrag()返回结果的具体实现,结果包含str类型的数据。其decode()方法会返回一个DefragResult实例。

class urllib.parse.ParseResultBytes(scheme, netloc, path, params, query, fragment)

urlparse()返回结果的具体实现,结果包含str类型的数据。其decode()方法会返回一个ParseResult实例。

class urllib.parse.SplitResultBytes(scheme, netloc, path, query, fragment)

urlsplit()返回结果的具体实现,结果包含str类型的数据。其decode()方法会返回一个 SplitResult实例。

2

URL转义

URL转义函数是为了使得编程数据能够安全的使用,主要分为转义特殊字符和编码非ASCII字符两个部分,另外还有对应的逆向操作。

urllib.parse.quote(string, safe='/', encoding=None, errors=None)

以%xx的形式转义特殊字符,但是字母、数字以及   _ . - ~  这几个字符是不会被转义的。默认情况下,该函数是为了转义URL中的path部分。当然,可以通过safe参数,明确指定哪些ASCII字符不希望被转义,其缺省值为"/"。

string入参既可以是str类型,也可以是bytes类型。

encoding和errors的含义和str.encode()类似,用于决定如何处理非ASCII字符。encoding缺省值为‘utf-8’。errors缺省值为‘strict’,即严格模式——如果遇到不支持的字符将会抛出UnicodeEncodeError异常。如果string入参为bytes类型,那么encoding和errors入参将不能够支持自定义,否则会抛出TypeError异常。

需要指出的是,quote(string, safe, encoding, errors)和quotefrombytes(string.encode(encoding, errors), safe)含义是一致的。

示例:quote('/El Niño/')会返回'/El%20Ni%C3%B1o/'。

urllib.parse.quote_plus(string, safe='', encoding=None, errors=None)

本函数和quote()类似,区别在于会用加号来转义空格,这么做主要是因为通常需要将query部分附加到URL上的时候需要转义HTML数据。除非通过safe参数明确指出加号不需要转义,否则该函数会自动转义加号。另外,safe入参的缺省值去掉了‘/’。

示例:quote_plus('/El Niño/') 会返回 '%2FEl+Ni%C3%B1o%2F'

urllib.parse.quote_from_byte(bytes, safe='/')

本函数和quote()类似,但是仅接受bytes类型的入参,不接受str类型,并且不支持string到bytes的转码。

示例:quotefrombytes(b'a&\xef') 会返回 'a%26%EF'

urllib.parse.unquote(string, encoding='utf-8', errors='replace')

本函数可以将%xx形式的转义字符还原为单个字符。encoding和errors的含义和bytes.decode()类似,用于决定如何将百分号的转义字符还原为Unicode字符。

string入参必须为str类型。

encoding缺省值为‘utf-8’。errors缺省值为‘replace’,意味着无效的内容会被占位符替代。

示例:unquote('/El%20Ni%C3%B1o/') 会返回 '/El Niño/'

urllib.parse.unquote_plus(string, encoding='utf-8', errors='replace')

本函数和unquote()类似,区别在于还原HTML内容的时候会使用空格替换加号。

string入参必须为str类型。

示例:unquote_plus('/El+Ni%C3%B1o/') 会返回 '/El Niño/'

urllib.parse.unquote_to_bytes(string)

本函数会将%xx形式的转义字符还原,返回值为bytes类型。

string入参既可以是str类型,也可以是bytes类型。如果是str类型,非ASCII字符会被还原为UTF-8的字节序。

示例:unquotetobytes('a%26%EF') 会返回 b'a&\xef'

urllib.parse.urlencode(query, doseq=False, safe='', encoding=None, errors=None, quote via=quote plus)

本函数可以将映射对象或者两元素类型的tuple(其具体值既可以是str,也可以是bytes),转义为ASCII类型的文本。如果期望将返回值用于urlopen()来传递POST数据,需要先将结果转为bytes类型,否则会抛出TypeError异常。

返回值是串通过&符号连接的key=value组合,其中无论是key还是value都已通过入参quotevia转义。和标准的GET请求(application/x-www-form-urlencoded)兼容,默认情况下,转义时使用的是quoteplus()函数,这就意味着空格会被转义为加号、‘/‘会被转义为%2F。另一个可以用于quote_via的转义函数是quote(),此时会将空格转义为%20,并保留‘/’不转义。如果想要更好的控制转义规则,建议使用quote函数,并显式的知名safe入参。

当入参query为两元素类型的tuple时,每个tuple成员里面的第一个元素会被当作key,第二个元素会被当作value。其中value元素可以有多个,此时如果doseq入参为True,该函数会迭代value,生成多个key=value的组合,将这些组合通过&分割符全部添加到返回值里面。最终的返回值里面的参数顺序同tuple顺序保持一致。

safe、encoding以及errors入参都将透传给quote_via(当然,只有query入参为str类型,encoding和errors才会真正向下透传)。

parseqs()和parseqsl()可以逆向本函数的编码过程,将一个query字符串转换为Python的数据结构。

---------- ------------------------------------------------------

相关文档:

RFC 3986-统一资源标识符: 该标准为当前在用的标准。任何urllib.parse模块的变动都需要参考该标准。某些同标准不一致的地方主要是为了向后兼容,并和浏览器的实际实现保持一致。

RFC 2732-URL中的IPV6地址格式 该标准具体说明了如何解析带有IPV6的URL

RFC 2396-统一资源标识符--通用格式: 该标准介绍了统一资源名(URN)和统一资源定位符(URL)的通用格式。

RFC 2367-mailto的URL协议: 该标准解释了mailto的URL协议

RFC 1808-相对URL: 该标准包含如何将拼接绝对URL和相对URL的详细说明,以及一系列的错误示例来展示如何处理边界值。

RFC 1738-统一资源定位符(URL): 该文档介绍了URL的格式及其含义。

相关文章