📘 第一章 了解Web及网络基础
📄 一句话概括

URI 是总称,URL 是其中「说位置」的一类,URN 是「说名字」的一类。日常说的网址几乎全是 URL。

📝 本节和前面几节的关系

上一节收口了链路。这一节回到起点:请求行里那个 /index.html 到底是什么。

一、三个词的关系

URI(Uniform Resource Identifier)是让一个资源可以被唯一指认的字符串。它有两个子集: - URL:给位置。https://example.com/a.html 说的是「去哪拿」。 - URN:给名字。urn:isbn:9787115351531 说的是「它是什么」,不说在哪。 这张关系图在任何一本讲网络的教材里都出现过,但绝大多数人只记住「URL 就是网址」。 实际写代码时,URN 的存在感几乎为零,因为「按名字找」需要一个全局解析体系,现实里只有 ISBN、 UUID 这类东西在局部用。

二、URL 的五个部件

用 urllib.parse.urlsplit 拆一个完整 URL,会得到五个字段:scheme(协议)、 netloc(主机与端口)、path(路径)、query(查询串)、fragment(片段)。 其中 fragment 只在浏览器本地生效,根本不会发给服务器。 这一条最常被误解:#top 锚点在 HTTP 请求里是不存在的, 所以服务端拿不到它(SPA 用 # 做路由是另一回事)。 netloc 里还可能有 user:password@,这是 HTTP 基本认证的老写法。 现在浏览器基本不认了,因为把密码写在地址里极其危险。

三、相对引用为什么能省那么多字

Demo 里 /a/b.html 这种只给路径的写法叫相对引用。它要结合当前页面的地址才能算出完整地址。 相对引用是 HTML 体积能压下来的重要原因之一:同一个站点里几百个链接, 全都省掉 https://example.com 这二十多个字符。 但它也带来歧义:b.html 和 /b.html 和 ./b.html 在不同当前路径下指向不同地方。 写链接时用绝对路径(以 / 开头)几乎总是更安全的选择。

四、动手 Demo:拆五种 URI,看它们各属于哪一类

拿五条形态各异的串,逐个拆成五个字段,再判断它属于 URL、URN 还是相对引用。

for s in samples:
    p = urllib.parse.urlsplit(s)
    print(u'scheme', p.scheme, u'netloc', p.netloc,
          u'path', p.path, u'query', p.query,
          u'fragment', p.fragment)

实跑输出:

--------------------------------------------------------------------
URI、URL、URN:三个名字一件事
--------------------------------------------------------------------
  原串                       https://user:[email protected]:8443/a/b.html?x=1&y=2#top
    scheme                   https
    netloc                   user:[email protected]:8443
    path                     /a/b.html
    query                    x=1&y=2
    fragment                 top
    归类                     URL:给位置

  原串                       mailto:[email protected]
    scheme                   mailto
    netloc                   (空)
    path                     [email protected]
    query                    (空)
    fragment                 (空)
    归类                     URL:给「发到哪」

  原串                       urn:isbn:9787115351531
    scheme                   urn
    netloc                   (空)
    path                     isbn:9787115351531
    query                    (空)
    fragment                 (空)
    归类                     URN:给名字

  原串                       /a/b.html
    scheme                   (空,相对引用)
    netloc                   (空)
    path                     /a/b.html
    query                    (空)
    fragment                 (空)
    归类                     相对引用(路径跟当前页面)

  原串                       //cdn.example.com/lib.js
    scheme                   (空,相对引用)
    netloc                   cdn.example.com
    path                     /lib.js
    query                    (空)
    fragment                 (空)
    归类                     相对引用(协议跟当前页面)

  URI 是总称;URL 是「按位置找」的那一类,URN 是「按名字认」的那一类。
  日常说的网址,几乎全是 URL。

第一条带 user:pw@ 和端口 8443——netloc 里全都有,但现代浏览器对带密码的 URL 基本只警告不采用。 urn:isbn:… 的 path 是 isbn:9787115351531,没有 netloc。 这一行最能说明「URN 不说位置」——它连主机都没有。 //cdn.example.com/lib.js 的 scheme 是空的,但 netloc 有值。 这叫协议相对引用,意思是「协议跟当前页面一样」。 现在不推荐用了,因为本地文件打开时它会变成 file://。

30 秒实验

  1. 给 urn:isbn: 那条补一个 ?x=1,看 urlsplit 还认不认(会认,但没意义)。
  2. 试试 urlsplit(u'HTTP://EXAMPLE.COM/A'),看 scheme 和 host 会不会自动变小写(scheme 会, path 不会)。
  3. 写一个 #frag 的地址,用 http.client 真发一次,在服务端打印收到的原始字节, 看 fragments 有没有出现。

常见误区

  1. 「URI 就是 URL」——URL 是 URI 的子集。URN 也是 URI。
  2. 「# 后面的内容会发给服务器」——不会,只在浏览器本地用。
  3. 「相对路径和绝对路径一样」——b.html 依赖当前目录,/b.html 依赖站点根。
自测三问(点开对答案) 1. URI、URL、URN 三者的关系? —— URI 是总称;URL 按位置定位,URN 按名字标识,两者都是 URI 的子集。 2. URL 的五个部件里,哪个不会发给服务器? —— `fragment`(`#` 之后的部分)。它只在客户端使用。 3. `//cdn.example.com/x.js` 的 scheme 是什么? —— 没有 scheme,叫协议相对引用,实际协议跟当前页面一致。

部件认全了。下一节把这些部件拆到字节层面,看中文怎么变成百分号。

相关笔记