专项练习 · 8 分钟阅读

Python URL 解析练习:拼接、查询与清理

很多重复链接并不是真的重复,而是同一个目的地的四种写法。这些练习把 URL 字符串拆成组成部分,基于页面补全,再重建成规范值。

读取每个组成部分

先给每个部分命名。scheme 表示协议,netloc 是主机和端口,path 是资源路径,query 是参数,fragment 是页内锚点。每个部分在抓取中承担不同职责。

from urllib.parse import urlparse

url = urlparse("https://example.com:8080/docs?q=python#intro")
print(url.scheme)
print(url.netloc)
print(url.path)
print(url.query)
print(url.fragment)

给每个部分命名

基于页面补全

链接相对于包含它的页面。urljoin 能处理 ../、根相对路径和只有查询参数的引用。保存前先补全,队列才不会把一个目的地当成多个页面。

from urllib.parse import urljoin

base = "https://example.com/docs/guide/page.html"
for href in ["../index.html", "/about", "?page=2"]:
    print(urljoin(base, href))

三种相对写法

读取重复的查询键

parse_qs 返回列表,即使键只出现一次。这是有意的:查询字符串可以重复键。只有 API 明确说明单值时,才取第一个值;数字在计算前要显式转换。

from urllib.parse import parse_qs

params = parse_qs("tag=python&tag=web&page=2")
print(params["tag"])
print(int(params["page"][0]) + 1)

先拿列表,再显式转换

编码新的查询字符串

urlencode 会转义空格和保留字符。搜索表单和分页链接都应使用它。传入键值对列表可以保留重复键和顺序,普通字典无法保证这一点。

from urllib.parse import urlencode

query = urlencode([("q", "a b"), ("tag", "python"), ("tag", "web")])
print(query)

编码键值对,保留重复

去重前去掉片段

# 后面的片段是给浏览器用的,不属于服务器请求。把页面加入 visited 集合前先去掉它。如果需要记录链接来源,可以另外保留原始 URL。

from urllib.parse import urlsplit, urlunsplit

parts = urlsplit("https://example.com/docs?q=1#section-2")
clean = urlunsplit((parts.scheme, parts.netloc, parts.path, parts.query, ""))
print(clean)

一个规范页面地址

你的练习清单

常见问题

urljoin 会验证目的地吗?

不会。它只做语法上的补全。仍然需要检查协议、主机和抓取规则。

为什么 parse_qs 返回列表?

查询字符串可能包含重复键,只保留一个值会丢失信息。

应该把整个 URL 转成小写吗?

不应该。主机名不区分大小写,但路径和查询值可能区分。只规范化你了解规则的部分。

练习 URL 解析

12 道题,覆盖 urlparse、urljoin、查询字符串、编码和片段。

打开练习

继续阅读: