📌 前言
在日常爬虫、API 调用或 Web 开发中,我们经常需要拼接 URL。
比如基础地址是 https://example.com/api/,后面要加上 user/info,最终得到
https://example.com/api/user/info。
很多新手会直接用 + 或 f-string 来拼接,但这样很容易少写或多写斜杠,导致 URL 错误。
Python 标准库 urllib.parse 提供了一个非常方便的函数 ------ urljoin ,它能智能地拼接 URL,帮你自动处理斜杠问题。
🎯 作用一句话概括
urljoin(base, url)将基础地址base和相对路径url拼接成一个完整、合法的绝对 URL。
📖 基本用法
from urllib.parse import urljoin
base = "https://www.example.com"
relative = "login/"
full_url = urljoin(base, relative)
print(full_url)
输出:
https://www.example.com/login/
瞧,它自动在 com 和 login 之间加上了 /,完美!
🧪 几个简单的例子
✅ 例子 1:基础地址末尾有斜杠
base = "https://example.com/"
relative = "api/user"
print(urljoin(base, relative)) # https://example.com/api/user
✅ 例子 2:基础地址末尾没有斜杠
base = "https://example.com"
relative = "api/user"
print(urljoin(base, relative)) # https://example.com/api/user
注意:和例子 1 结果一样,
urljoin会自动补充斜杠,不用担心!
✅ 例子 3:相对路径以斜杠开头
base = "https://example.com/abc/"
relative = "/login/"
print(urljoin(base, relative)) # https://example.com/login/
如果
relative以/开头,它会替换掉整个路径部分,只保留协议和域名。
✅ 例子 4:相对路径是上一级目录
base = "https://example.com/user/profile/"
relative = "../settings/"
print(urljoin(base, relative)) # https://example.com/user/settings/
urljoin 也支持 Unix 风格的相对路径 (.. 和 .)。
✅ 例子 5:相对路径是一个完整的 URL
base = "https://example.com"
relative = "https://google.com/search"
print(urljoin(base, relative)) # https://google.com/search
如果
relative已经是一个完整的绝对 URL,urljoin会直接返回它 ,忽略base。
✅ 例子 6:仅追加path
base = "https://example.com/abc/"
relative = "login"
print(urljoin(base,relative)) # https://example.com/abc/login
可以封装成函数使用
from urllib.parse import urljoin
def urljoin_test(base, relative):
# rstrip('/'):去掉 base_url 末尾的斜杠,防止后面手动加 / 时出现双斜杠。
base = base.rstrip('/')
# base_url + '/':保证基础部分以 / 结尾。
# path.lstrip('/'):去掉 path 开头的斜杠,保证中间只有一个 /。
# 最后 urljoin 做最终的拼接,安全可靠。
url = urljoin(base + '/', relative.lstrip('/'))
return url
base = "https://example.com/abc/"
relative = "/login"
print(urljoin_test(base,relative)) # https://example.com/abc/login
如果把 relative 改成不以 / 开头,这时就会在原有路径后面追加 login/,而不是替换。
🚫 手动拼接有什么坑?
假设你想拼接 https://ex.com/api 和 /v1/users:
# 错误示范 ❌
base = "https://ex.com/api"
bad_url = base + "/v1/users" # 结果:https://ex.com/api/v1/users (看起来对?)
好像没问题?但换个场景就出问题:
base = "https://ex.com/api/" # 末尾多了一个斜杠
bad_url = base + "/v1/users" # 结果:https://ex.com/api//v1/users (两个斜杠!)
虽然浏览器有时能容忍双斜杠,但这不是规范的 URL,某些严格的服务端会报错。
而 urljoin 永远不会出现双斜杠:
from urllib.parse import urljoin
base = "https://ex.com/api/"
print(urljoin(base, "/v1/users")) # https://ex.com/api/v1/users ✅
💡 实际应用场景
| 场景 | 说明 |
|---|---|
| 爬虫 | 从 HTML 中提取 href="/news/123",与当前页面 URL 拼接成绝对链接 |
| API 客户端 | 基础地址 https://api.example.com/v1,动态拼接 users, posts 等端点 |
| Django/Flask 测试 | 测试中动态构建请求 URL |
| 配置文件 | 配置一个 base_url,各处调用 urljoin 拼接子路径 |
📝 代码模板(直接复制用)
from urllib.parse import urljoin
class ApiClient:
def __init__(self, base_url):
self.base_url = base_url.rstrip('/') # 去掉末尾斜杠,urljoin 会自动加
def _url(self, path):
return urljoin(self.base_url + '/', path.lstrip('/'))
def get_users(self):
full_url = self._url("/users")
print(f"请求地址: {full_url}")
# 这里发请求...
client = ApiClient("https://myapi.com")
client.get_users() # 输出: 请求地址: https://myapi.com/users
⚠️ 注意事项(新手常见疑问)
-
urljoin不会做网络请求,它只负责字符串拼接。 -
第二个参数以
//开头 时(例如//example.com/),会沿用原协议(http 或 https),但这种行为很多新手可能想不到,尽量避免这样用。 -
第二个参数以
?或#开头时,会替换原 URL 的查询参数或锚点。 -
如果需要处理非常规 URL(比如
file:///C:/test),urljoin也能工作。
🎓 总结
| 方法 | 是否智能处理斜杠 | 是否支持 .. 路径 |
推荐程度 |
|---|---|---|---|
手动 + |
❌ | ❌ | 不推荐 |
| f-string | ❌ | ❌ | 不推荐 |
os.path.join |
❌(用于文件路径,不是 URL) | ❌ | 不推荐 |
urljoin |
✅ | ✅ | ✅ 强烈推荐 |
一句话记住:以后拼接 URL,直接用 urljoin,别再手动加斜杠了!