furl、yarl,功能强大的url路径操作库
在 Python小酷库系列:pathlib,全面取代os.path的路径操作小能手 一节里,我们介绍了Python 3.4+ 标准库中内置的pathlib,它在文件路径操作方面的能力让人印象深刻。本节我们再来看看url路径解析与拼接方面的有哪些惊艳的小酷库。
url路径作为一段普通的字符串,常常可以通过正则表达式进行解析,而普通的字符串拼接技术也能胜任url路径的拼接,乍看之下没有必要使用什么特殊的Python库。然而,经常进行url路径操作的小伙伴都知道,相对复杂一些url操作就会面临正斜杠、反斜杠、结尾留不留斜杆的斜杆问题;路径参数查询参数的参数混淆问题;URL 编码/解码、再编码再解码的问题。以上种种问题虽然不是不能解决,但是总让人不能踏实,生怕一段冷僻的url路径搞崩了整个程序,这时找一个好用的小酷库就很有必要了。
非专业的urllib.parse和requests
常用http请求库中也往往自带了url的解析和拼接功能,如标准库中的urllib和大名鼎鼎的requests,它们都有不错的url解析和拼接能力。
urllib.parse
1、urlparse():分解 URL 成组件
from urllib.parse import urlparse
result = urlparse("https://example.com:8080/path?query=1#frag")
print(result.scheme) # https
print(result.netloc) # example.com:8080
print(result.path) # /path
2、urlunparse():把解析后的组件组装回 URL
from urllib.parse import urlunparse
components = ('https', 'example.com', '/path', '', 'query=1', 'frag')
print(urlunparse(components)) # https://example.com/path?query=1#frag
3、urljoin():合并 base URL 与相对路径
from urllib.parse import urljoin
base = "https://example.com/api/"
print(urljoin(base, "v1/data")) # https://example.com/api/v1/data
4、urlencode():构造查询字符串
from urllib.parse import urlencode
params = {'q': 'python urllib', 'page': 1}
query_str = urlencode(params)
print(query_str) # q=python+urllib&page=1
requests
1、使用 params 自动构造带查询字符串的 URL
import requests
params = {'q': 'python requests', 'page': 2}
response = requests.get('https://www.google.com/search', params=params)
print(response.url)
# 输出: https://www.google.com/search?q=python+requests&page=2
2、自动编码参数(中文、特殊字符)
params = {'query': '你好 世界'}
r = requests.get('https://httpbin.org/get', params=params)
print(r.url)
# 输出: https://httpbin.org/get?query=%E4%BD%A0%E5%A5%BD+%E4%B8%96%E7%95%8C
3、使用 urllib.parse 搭配 requests 做高级 URL 操作
from urllib.parse import urlparse, urlunparse, urljoin
import requests
# 解析原始 URL
url = "https://example.com/api/data?query=test"
parsed = urlparse(url)
print(parsed.scheme) # https
print(parsed.netloc) # example.com
# 添加路径或构造完整 URL
base = "https://example.com/api/"
endpoint = "v1/items"
full_url = urljoin(base, endpoint)
print(full_url) # https://example.com/api/v1/items
# 请求 + 打印最终访问的 URL
res = requests.get(full_url, params={"search": "book"})
print(res.url)
furl
相比于urllib.parse,furl 是一个非常 直观、强大、专注于 URL 构造与解析的第三方 Python 库,它支持链式调用、自动编码、路径/参数/锚点操作,非常适合构造 API URL、爬虫跳转等用途。
基本使用
1、构造和解析 URL
from furl import furl
f = furl("https://example.com/api?search=test#section")
print(f.scheme) # https
print(f.host) # example.com
print(f.path) # /api
print(f.args) # {'search': 'test'}
print(f.fragment) # section
2、添加或修改路径段(Path)
f = furl("https://example.com")
f.path.add("v1")
f.path.add("users")
print(f.url) # https://example.com/v1/users
你也可以使用 .path.segments 直接访问列表:
f.path.segments # ['v1', 'users']
3、添加或修改查询参数(Query Args)
f = furl("https://example.com/api")
f.args["page"] = 2
f.args["q"] = "python"
print(f.url) # https://example.com/api?page=2&q=python
支持重复参数值(一个键多个值):
f.args.add("tag", "python")
f.args.add("tag", "ai")
print(f.url) # https://example.com/api?page=2&q=python&tag=python&tag=ai
4、删除参数或路径
f.args.remove("page")
f.path.segments.pop() # 删除最后一个路径段
5、添加锚点(fragment)
f.fragment = "section1"
print(f.url) # https://example.com/api?q=python#section1
6、合并 base URL 和子路径(链式构造)
base = furl("https://example.com/api")
child = base.copy().path.add("v1").add("item")
child.args["id"] = 42
print(child.url) # https://example.com/api/v1/item?id=42
综合示例
构造分页 API 链接列表
from furl import furl
for page in range(1, 4):
f = furl("https://api.example.com/data")
f.args["page"] = page
f.args["limit"] = 20
print(f.url)
常用属性/方法速查表
功能 | 示例 |
---|---|
创建 URL | f = furl(“https://…”) |
访问部分 | f.scheme, f.path, f.args, f.fragment |
添加路径段 | f.path.add(“v1”) |
修改参数 | f.args[“q”] = “value” |
删除参数 | f.args.remove(“q”) |
设置锚点 | f.fragment = “top” |
输出完整 URL | f.url |
拷贝 | new_f = f.copy() |
yarl
yarl 是一个功能强大且高性能的 Python 库,用于处理 URL 构造、解析和修改,它特别适合异步项目(比如配合 aiohttp),但也可以在任何 Python 项目中使用。相比标准库的 urllib.parse,yarl 提供了更加直观、链式、安全的 API。
基本使用
1、基本解析功能
from yarl import URL
url = URL("https://user:pass@example.com:8080/api/v1/items?id=10#top")
print(url.scheme) # https
print(url.user) # user
print(url.password) # pass
print(url.host) # example.com
print(url.port) # 8080
print(url.path) # /api/v1/items
print(url.query) # <MultiDictProxy('id': '10')>
print(url.fragment) # top
2、构造/拼接 URL(推荐用 .with_*()
)
url = URL("https://example.com")
# 添加路径
url = url / "api" / "v1"
print(url) # https://example.com/api/v1
# 添加查询参数
url = url.update_query(page=2, search="python")
print(url) # https://example.com/api/v1?page=2&search=python
# 添加锚点
url = url.with_fragment("section1")
print(url) # https://example.com/api/v1?page=2&search=python#section1
3、操作查询参数(.query 是一个 MultiDictProxy)
url = URL("https://example.com/?a=1&b=2")
print(url.query["a"]) # 1
print(list(url.query.items())) # [('a', '1'), ('b', '2')]
# 修改参数(返回新 URL)
url2 = url.update_query(a="100", c="new")
print(url2) # https://example.com/?a=100&b=2&c=new
# 删除参数
url3 = url2.with_query({})
print(url3) # https://example.com/
4、处理多值参数(重复 key)
url = URL("https://example.com/?tag=python&tag=ai")
print(url.query.getall("tag")) # ['python', 'ai']
5、路径分段和操作
url = URL("https://example.com/api/v1")
print(url.parts) # ('/', 'api', 'v1')
# 添加路径(返回新 URL)
url2 = url / "items"
print(url2) # https://example.com/api/v1/items
6、序列化为字符串
url = URL("https://example.com/api")
print(str(url)) # https://example.com/api
7、与 aiohttp 搭配
import aiohttp
from yarl import URL
url = URL("https://httpbin.org/get").update_query(key="value")
async with aiohttp.ClientSession() as session:
async with session.get(url) as resp:
print(await resp.text())
完整示例
构造分页 API URL 列表
from yarl import URL
base_url = URL("https://api.example.com/v1/items")
for page in range(1, 4):
url = base_url.update_query(page=page, limit=20)
print(url)
常用属性/方法速查表
yarl.URL常用属性
属性名 | 说明 | 示例值 |
---|---|---|
.scheme | 协议部分 | ‘https’ |
.host | 主机(无端口) | ‘example.com’ |
.port | 端口 | 8080 |
.user | 用户名(如果 URL 中有) | ‘admin’ |
.password | 密码(如果 URL 中有) | ‘123’ |
.path | 路径字符串 | ‘/api/v1/items’ |
.parts | 路径分段元组 | (‘/’, ‘api’, ‘v1’, ‘items’) |
.query | 查询参数字典(MultiDictProxy) | {‘page’: ‘1’, ‘limit’: ‘20’} |
.fragment | URL 锚点 | ‘top’ |
.raw_host | 原始 Host,不带解析 | ‘example.com’ |
.raw_path | 编码后的原始路径 | ‘/api/v1/items’ |
.raw_query_string | 编码后的原始查询字符串 | ‘page=1&limit=20’ |
构造与拼接方法
方法名 / 操作符 | 功能说明 | 示例 |
---|---|---|
URL(url_str) | 创建 URL 对象 | URL(“https://example.com”) |
url / “path” | 拼接路径段(不可变) | URL(…) / “api” / “v1” |
.with_path(path) | 设置完整路径(覆盖) | url.with_path(“/new/path”) |
.with_query(dict) | 替换所有查询参数 | url.with_query({‘q’: ‘python’}) |
.update_query(dict) | 添加或修改查询参数 | url.update_query(page=2) |
.with_fragment(str) | 设置锚点 | url.with_fragment(“top”) |
.with_host(host) | 替换主机名 | url.with_host(“api.example.com”) |
.with_scheme(scheme) | 替换协议 | url.with_scheme(“http”) |
查询参数操作(.query 是不可变的)
方法名 | 功能说明 | 示例 |
---|---|---|
.query[“key”] | 获取单个参数值 | url.query[“page”] → ‘1’ |
.query.getall() | 获取重复参数的所有值 | url.query.getall(“tag”) → [‘a’, ‘b’] |
.update_query() | 添加或更新参数 | url.update_query(page=2) |
.with_query({}) | 删除全部参数 | url.with_query({}) → 无参数 |
其他有用操作
操作 | 示例 | 说明 |
---|---|---|
str(url) | ‘https://example.com/path’ | 转换为字符串形式的 URL |
url.name | ‘items’ | 获取路径中的最后一段 |
url.parent | ‘https://example.com/api/v1’ | 返回上一级路径的 URL |
.is_absolute() | 判断是否是绝对路径 URL | 返回 True/False |
.origin() | 返回 origin(scheme + host + port) | https://example.com:8080 |