# 21.8. `urllib.parse` — 分解URL

源代码：Lib/urllib/parse.py

该模块定义了一个标准接口，用于分解统一资源定位符（URL）字符串为各个组成部分（寻址方案、网络位置、路径等），以及将各个组成部分组合回URL字符串，并将“相对URL”转换为给定“基本URL”的绝对URL。

该模块的设计符合相对统一资源定位符的因特网RFC。它支持以下网址格式: `file`, `ftp`, `gopher`, `hdl`, `http`, `https`, `imap`, `mailto`, `mms`, `news`, `nntp`, `prospero`, `rsync`, `rtsp`, `rtspu`, `sftp`, `shttp`, `sip`, `sips`, `snews`, `svn`, `svn+ssh`, `telnet`, `wais`.

`urllib.parse`模块定义的函数分为两大类：URL解析和URL转义。它们将在以下章节详细阐述。

## 21.8.1. URL解析

URL解析函数专注于将URL字符串拆分为各个部分，或者将URL各个部分组合到一个URL字符串中。

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

  将URL解析为六个部分，返回一个6元组。这对应于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'

  遵循 RFC 1808中的语法规范，urlparse仅在由“//”正确引入时识别netloc。否则，输入被假定为相对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不指定时使用。它应该是与urlstring相同的类型（文本或字节），除了始终允许默认值`''`，并自动转换为`b''`（如果适用）。

  如果allow_fragments参数为false，则无法识别片段标识符。而是将它们作为路径，参数或查询组件的一部分进行解析，并将`fragment`设置为返回值中的空字符串。

  返回值实际上是`tuple`的子类的实例。此类具有以下额外的只读方便属性：

  属性 | 指数 | 值 | 值（如果不存在）
  --- | --- | --- | ---
  `scheme` | 0 | URL方案说明符 | scheme参数
  `netloc` | 1 | 网络位置部分 | 空字符串
  `path` | 2 | 分层路径 | 空字符串
  `params` | 3 | 最后路径元素的参数 | 空字符串
  `query` | 4 | 查询组件 | 空字符串
  `fragment` | 5 | 片段标识符 | 空字符串
  `username` |  | 用户名 | `None`
  `password` |  | 密码 | `None`
  `hostname` |  | 主机名（小写） | `None`
  `port` |  | 端口号为整数（如果存在） | `None`

  有关结果对象的更多信息，请参见 Structured Parse Results一节。

  在版本3.2中已更改：添加了IPv6 URL解析功能。

  Changed in version 3.3: The fragment is now parsed for all URL schemes (unless allow_fragment is false), in accordance with RFC 3986. 以前，支持片段的方案的白名单存在。

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

  解析作为字符串参数（application / x-www-form-urlencoded类型的数据）提供的查询字符串。数据作为字典返回。字典键是唯一的查询变量名，值是每个名称的值列表。

  可选参数keep_blank_values是指示百分比编码查询中的空白值是否应被视为空白字符串的标志。true值表示空白应保留为空白字符串。默认的false值表示将空值忽略并视为未包括空值。

  可选参数strict_parsing是指示如何处理解析错误的标志。如果为false（默认值），则错误将被忽略。如果为true，则错误引发`ValueError`异常。

  可选的编码和错误参数指定如何将百分比编码序列解码为Unicode字符，如`bytes.decode()`方法所接受。

  使用`urllib.parse.urlencode()`函数（将`doseq`参数设置为`True`）将这些字典转换为查询字符串。

  在版本3.2中更改：添加编码和错误参数。

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

  解析作为字符串参数（application / x-www-form-urlencoded类型的数据）提供的查询字符串。数据作为名称，值对的列表返回。

  The optional argument keep_blank_values is a flag indicating whether blank values in percent-encoded queries should be treated as blank strings. true值表示空白应保留为空白字符串。默认的false值表示将空值忽略并视为未包括空值。

  可选参数strict_parsing是指示如何处理解析错误的标志。如果为false（默认值），则错误将被忽略。如果为true，则错误引发`ValueError`异常。

  可选的编码和错误参数指定如何将百分比编码序列解码为Unicode字符，如`bytes.decode()`方法所接受。

  使用`urllib.parse.urlencode()`函数将这些对列表转换为查询字符串。

  在版本3.2中更改：添加编码和错误参数。

`urllib.parse.``urlunparse`(parts)

  从`urlparse()`返回的元组构造一个URL。部分参数可以是任何六项可迭代。如果最初解析的URL具有不必要的分隔符（例如，`?`），则这可能会导致稍有不同，用空查询； RFC声明这些是等效的）。

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

  这类似于`urlparse()`，但不会从URL中分离参数。如果最近的URL语法允许将参数应用于URL的路径部分的每个段（参见），则通常应使用`urlparse()` RFC 2396）。需要单独的功能来分离路径段和参数。此函数返回一个5元组：（寻址方案，网络位置，路径，查询，片段标识符）。

  返回值实际上是`tuple`的子类的实例。此类具有以下额外的只读方便属性：

  属性 | 指数 | 值 | 值（如果不存在）
  --- | --- | --- | ---
  `scheme` | 0 | URL方案说明符 | 方案参数
  `netloc` | 1 | 网络位置部分 | 空字符串
  `path` | 2 | 分层路径 | 空字符串
  `query` | 3 | 查询组件 | 空字符串
  `fragment` | 4 | 片段标识符 | 空字符串
  `username` |  | 用户名 | `None`
  `password` |  | 密码 | `None`
  `hostname` |  | 主机名（小写） | `None`
  `port` |  | 端口号为整数（如果存在） | `None`

  有关结果对象的更多信息，请参见Structured Parse Results一节。

`urllib.parse.``urlunsplit`(parts)

  将`urlsplit()`返回的元组元素合并为一个完整的URL作为字符串。部分参数可以是任何五项可迭代。这可能会导致稍有不同，但等效的URL，如果解析的URL原来有不必要的分隔符（例如，用空查询； RFC声明这些是等效的）。

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

  通过将“基本网址”（基本）与另一个网址（url）组合，构造完整（“绝对”）网址。非正式地，这使用基本URL的组件，特别是寻址方案，网络位置和（部分）路径，以在相对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是绝对网址（即以`//`或`scheme://`开头），则url t5 >的主机名和/或方案将出现在结果中。例如：

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

  如果您不想要该行为，请使用`urlsplit()`和`urlunsplit()`预处理url，删除可能的方案 t7 >和netloc部分。

  在版本3.5中已更改：已更新以符合 RFC 3986中定义的语义的行为。

`urllib.parse.``urldefrag`(url)

  如果url包含片段标识符，则返回没有片段标识符的url的修改版本，片段标识符作为单独的字符串。如果url中没有片段标识符，则返回未修改的url和空字符串。

  返回值实际上是`tuple`的子类的实例。此类具有以下额外的只读方便属性：

  属性 | 指数 | 值 | 值（如果不存在）
  --- | --- | --- | ---
  `url` | 0 | 没有片段的网址 | 空字符串
  `fragment` | 1 | 片段标识符 | 空字符串

  有关结果对象的更多信息，请参见Structured Parse Results一节。

  在版本3.2中更改：结果是一个结构化对象，而不是一个简单的2元组。

## 21.8.2. 解析ASCII编码字节

URL解析函数最初设计为仅对字符串进行操作。在实践中，能够将正确引用和编码的URL作为ASCII字节序列进行操作是有用的。因此，除了`str`对象，此模块中的URL解析函数都对`bytes`和`bytearray`

如果传递`str`数据，结果也将只包含`str`数据。如果传入`bytes`或`bytearray`数据，则结果将只包含`bytes`数据。

尝试在单个函数调用中将`str`数据与`bytes`或`bytearray`混合会导致产生`TypeError`而尝试传入非ASCII字节值将触发`UnicodeDecodeError`。

为了支持在`str`和`bytes`之间更容易转换结果对象，来自URL解析函数的所有返回值都提供了一个`encode()`结果包含`str`数据）或`decode()`方法（当结果包含`bytes`数据时）。The signatures of these methods match those of the corresponding `str` and `bytes` methods (except that the default encoding is `'ascii'` rather than `'utf-8'`). 每个产生包含`bytes`数据（用于`encode()`方法）或`str`数据（用于`decode()`方法）。

需要在可能包含非ASCII数据的可能不正确引用的URL上操作的应用程序需要在调用URL解析方法之前，从字节到字符进行自己的解码。

此部分中描述的行为仅适用于URL解析函数。在生成或使用字节序列时，URL引用函数使用自己的规则，如各个URL引用函数的文档中所述。

在版本3.2中更改： URL解析函数现在接受ASCII编码的字节序列

## 21.8.3. 结构化解析结果

来自`urlparse()`，`urlsplit()`和`urldefrag()`函数的结果对象是`tuple`类型。这些子类添加了文档中列出的那些功能的属性，上一节中描述的编码和解码支持以及一个附加方法：

`urllib.parse.SplitResult.``geturl`()

  以字符串形式返回原始URL的重新组合版本。这可能不同于原始URL，因为方案可以归一化为小写，并且可以丢弃空组件。具体来说，将删除空参数，查询和片段标识符。

  对于`urldefrag()`结果，仅删除空片段标识符。对于`urlsplit()`和`urlparse()`结果，将对由此方法返回的URL进行所有标注的更改。

  如果通过原始解析函数传回，此方法的结果保持不变：

      >>> from urllib.parse import urlsplit
      >>> url = 'HTTP://www.Python.org/doc/#'
      >>> r1 = urlsplit(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)

  包含`str`数据的`urldefrag()`结果的具体类。`encode()`方法返回一个`DefragResultBytes`实例。

  版本3.2中的新功能。

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

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

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

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

以下类提供在`bytes`或`bytearray`对象上操作时解析结果的实现：

class `urllib.parse.``DefragResultBytes`(url, fragment)

  包含`bytes`数据的`urldefrag()`结果的具体类。`decode()`方法返回一个`DefragResult`实例。

  版本3.2中的新功能。

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

  包含`bytes`数据的`urlparse()`结果的具体类。`decode()`方法返回`ParseResult`实例。

  版本3.2中的新功能。

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

  包含`bytes`数据的`urlsplit()`结果的具体类。`decode()`方法返回一个`SplitResult`实例。

  版本3.2中的新功能。

## 21.8.4. URL转义

URL引用功能专注于获取程序数据，并通过引用特殊字符并适当编码非ASCII文本，使其作为URL组件安全使用。它们还支持反转这些操作以从URL组件的内容重新创建原始数据，如果该任务尚未被上面的URL解析函数覆盖。

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

  使用`%xx`转义替换string中的特殊字符。字母、数字和字符`'_.-'`永远不会转义。默认情况下，此函数用于转义URL的路径部分。可选的safe参数指定不应转义的其他ASCII字符 — 其默认值为`'/'`。

  string可以是`str`或`bytes`。

  可选的encoding和errors参数指定如何处理非ASCII字符，它们会由`str.encode()`方法所接受。encoding默认为`'utf-8'`。errors默认为`'strict'`，表示不支持的字符引发`UnicodeEncodeError`。如果string是一个`bytes`，那么encoding和errors不可以指定，否则会引发一个`TypeError`。

  注意，`quote(string, safe, encoding, errors)`等同于`quote_from_bytes(string.encode(encoding, errors), safe)`。

  示例：`quote('/El Niño/')`得到`'/El%20Ni%C3%B1o/'`。

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

  类似`quote()`，但是用加号替换空格，在转义HTML表单值来构建查询字符串以进入URL时，这是必需的。原始字符串中的加号将被转义，除非它们包含在safe中。它的safe没有默认为`'/'`。

  示例：`quote_plus('/El Niño/')`得到`'%2FEl+Ni%C3%B1o%2F'`。

`urllib.parse.``quote_from_bytes`(bytes, safe='/')

  像`quote()`，但接受`bytes`对象而不是`str`，并且不执行字符串到字节编码。

  示例：`quote_from_bytes(b'a&\xef')`生成`'a%26%EF'`。

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

  将`%xx`转义替换为等效的单字符。可选的编码和错误参数指定如何将百分比编码序列解码为Unicode字符，如`bytes.decode()`方法所接受。

  string必须是`str`。

  encoding默认为`'utf-8'`。错误默认为`'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)

  Replace `%xx` escapes by their single-octet equivalent, and return a `bytes` object.

  string可以是`str`或`bytes`。

  如果它是`str`，则string中的非转义非ASCII字符将编码为UTF-8字节。

  示例：`unquote_to_bytes('a%26%EF')`产生`b'a&\xef'`。

`urllib.parse.``urlencode`(query, doseq=False, safe='', encoding=None, errors=None, quote_via=quote_plus)

  将映射对象或两元素元组序列，可以包含`str`或`字节`对象，转换为百分号编码的ASCII文本字符串。如果结果字符串要作为data用于使用`urlopen()`函数的POST操作，那么它应该被编码为字节，否则会导致`TypeError`。

  结果字符串是由`'&'`字符分隔的一系列`key=value`对，其中key和value使用quote_via函数转义。默认情况下，`quote_plus()`用于引用值，这意味着空格被引用为`'+'`字符，'/'字符编码为`%2F`，它遵循GET请求的标准（`application/x-www-form-urlencoded`）。可作为quote_via传递的备用函数为`quote()`，它将将空格编码为`%20`，而不对'/'字符进行编码。要最大限度地控制所引用的内容，请使用`quote`并指定safe的值。

  当使用两元素元组序列作为query参数时，每个元组的第一个元素是键，第二个元素是值。value元素本身可以是一个序列，在这种情况下，如果可选参数doseq计算为True，则单个`key=value`由键的值序列的每个元素生成由`'&'`分隔的字符串。编码字符串中参数的顺序将匹配序列中参数元组的顺序。

  safe、encoding和errors参数将传递给quote_via（encoding和errors参数只有在查询元素为`str`时才会传递。）

  为了逆转此编码过程，在本模块中提供`parse_qs()`和`parse_qsl()`将查询字符串解析为Python数据结构。

  请参阅urllib examples以了解如何使用urlencode方法生成URL的数据或POST的查询字符串。

  在版本3.2中更改：查询参数支持字节和字符串对象。

  新版本3.5： quote_via参数。

也可以看看

RFC 3986 - 统一资源标识符
  这是当前标准（STD66）。对urllib.parse模块的任何更改都应符合此。可以观察到某些偏差，这主要是出于向后兼容性目的和对于在主要浏览器中通常观察到的某些事实解析要求。
RFC 2732 - 字面值格式IPv6地址在URL中。
  这指定IPv6 URL的解析要求。
RFC 2396 - 统一资源标识符（URI）：通用语法
  描述统一资源名称（URN）和统一资源定位符（URL）的通用句法要求的文档。
RFC 2368 - mailto URL方案。
  解析mailto URL方案的要求。
RFC 1808 - 相对统一资源定位符
  此征求意见稿包括加入绝对和相对网址的规则，包括管理边境案件处理的相当数量的“异常例子”。
RFC 1738 - 统一资源定位符（URL）
  这指定绝对URL的形式语法和语义。