登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  文章 >  python教程

Python urllib.parse.urlsplit 怎样拆分 URL 的主机与端口

来源:17golang原创

时间:2026-11-14 23:07:36 441浏览 收藏

用 Python 从 URL 中取主机和端口时,直接调用 urllib.parse.urlsplit(),再读取返回对象的 hostname 和 port 属性即可。hostname 通常是小写主机名,port 是整数或 None;但端口文本非法、超出范围时,读取 port 会抛出 ValueError。

urlsplit 负责把 URL 拆成结构化字段,不负责替你确认输入一定可信。先确认输入是否带 //,再读取 hostname 和 port,最后按业务要求校验 scheme、主机和端口范围。
  • 完整网络位置通常写成 scheme://host:port/path,主机和端口从 hostname、port 读取。
  • 没有 // 的字符串可能被当作相对路径,hostname 和 port 会是空结果。
  • IPv6 主机要保留方括号,例如 http://[2001:db8::1]:8443/。
  • 下面的两张图都是静态结构说明图,不是本地运行截图或运行证据。

Python urlsplit 怎样读取 hostname 和 port

urlsplit 返回一个五项的 SplitResult:scheme、netloc、path、query 和 fragment。其中 netloc 还可以通过属性继续拆成 username、password、hostname 和 port。

from urllib.parse import urlsplit


def read_endpoint(raw_url: str) -> tuple[str | None, int | None]:
    # 这里做结构拆分,不把 urlsplit 当作完整 URL 校验器。
    parsed = urlsplit(raw_url)
    try:
        # hostname 会规范为小写;port 存在时返回整数。
        return parsed.hostname, parsed.port
    except ValueError as exc:
        # 端口不是整数或超出允许范围时,在读取 port 时报告异常。
        raise ValueError(f"URL 端点无效: {raw_url}") from exc


host, port = read_endpoint("https://api.example.com:8443/v1/items")
# host == "api.example.com",port == 8443;后续再按业务规则校验。

这里没有手工切分 :。直接按冒号切分会在 IPv6 地址上失效,也容易把用户名、密码或路径误算进端口。使用返回对象的属性,可以把“原始网络位置”和“端点字段”分开处理。

Python urlsplit 将 URL 输入拆成 SplitResult、netloc、hostname 和 port 的静态关系图
图1:urlsplit 主机端点结构说明图,展示 URL、SplitResult、hostname 和 port 的静态关系,不是截图或运行证据。

为什么 hostname 有时为空

关键在于网络位置是否由 // 引入。官方文档说明,只有正确出现 // 时,urlsplit 才把后面的内容识别为 netloc;否则输入会被当作相对 URL,整体更像路径。

from urllib.parse import urlsplit

samples = [
    "https://example.com:443/docs",  # 有 scheme 和 //,能识别主机与端口。
    "//example.com:443/docs",        # 没有 scheme,但 // 仍然引入 netloc。
    "example.com:443/docs",          # 没有 //,通常按相对路径解释。
]

for raw_url in samples:
    # 只打印结构字段,便于区分 netloc 是否被识别。
    parsed = urlsplit(raw_url)
    print(raw_url, parsed.netloc, parsed.hostname, parsed.port)

因此,urlsplit("example.com:443/docs") 不等价于“解析一个没有协议的主机”。如果输入来自配置文件,应该在进入解析前明确它允许的是绝对 URL、网络路径引用,还是必须补齐 scheme 的主机地址,而不是看见字符串里有冒号就自行猜测。

IPv6 和缺少端口怎么处理

IPv6 地址本身包含多个冒号,所以 URL 语法要求把主机放进方括号,端口再写在右方括号之后。读取 hostname 时得到的是地址本身,读取 port 时得到方括号外的整数;没有端口则返回 None。

from urllib.parse import urlsplit

for raw_url in (
    "http://[2001:db8::1]:8080/health",  # IPv6 主机和端口分开表达。
    "http://[2001:db8::1]/health",       # 没写端口,port 为 None。
):
    # 先取 hostname,再按 None 判断调用方是否显式提供端口。
    parsed = urlsplit(raw_url)
    print({"hostname": parsed.hostname, "port": parsed.port})

不要用字符串的最后一个冒号来判断端口:IPv6 的压缩地址、用户信息和异常方括号都会让这种写法变得脆弱。对于只允许域名或 IPv4 的业务,还应在解析之后额外拒绝 IPv6,而不是让解析器替你做业务决策。

Python urlsplit 的 //、相对 URL、IPv6 方括号、hostname、port 和 ValueError 边界说明图
图2:URL 解析边界说明图,展示 //、IPv6 方括号、hostname、port 与 ValueError 的关系,不是截图或运行证据。

非法端口为什么要单独捕获 ValueError

端口字段不是永远安全地返回整数。官方文档明确说明,端口无效时读取 port 会抛出 ValueError;未匹配的方括号等网络位置问题也可能在解析或访问结构属性时暴露异常。把异常处理包在读取端点的边界上,比先访问一次再让错误向上扩散更清楚。

from urllib.parse import urlsplit


def allowed_endpoint(raw_url: str) -> tuple[str, int]:
    # 业务只允许 HTTPS,避免把解析结果直接当成连接授权。
    parsed = urlsplit(raw_url)
    if parsed.scheme.lower() != "https":
        raise ValueError("只允许 https URL")

    try:
        hostname = parsed.hostname
        port = parsed.port
    except ValueError as exc:
        # 非法端口和异常主机表达都在这里转换成业务错误。
        raise ValueError("主机或端口格式不正确") from exc

    # 解析成功不等于业务通过,继续检查主机和端口是否存在且在白名单内。
    if not hostname or port not in {443, 8443}:
        raise ValueError("主机为空或端口不在允许范围")
    return hostname, port

这段代码只做输入边界示范,不负责 DNS 解析、证书校验或建立连接。Python 文档也提醒,urlsplit 的用途是实用拆分,并不完成通用 URL 验证;真正把结果交给网络客户端前,仍要结合应用自己的协议、主机白名单和安全规则。

常见问题

urlsplit 和 urlparse 取主机端口有什么区别?

两者都能从结果对象读取 hostname 和 port,但 urlsplit 返回五项结果,不单独拆出旧式的 params 字段。只需要 scheme、网络位置、路径、查询和片段时,优先用 urlsplit 更直接。

没有端口时应该默认使用多少?

urlsplit 只表达输入中是否有显式端口,没有端口时返回 None。是否根据 scheme 映射到 80 或 443,应由 HTTP 客户端或业务配置决定,不能把解析结果误认为已经完成默认端口选择。

urlsplit 能判断 URL 是否安全可靠吗?

不能。它会尽量拆出组件,官方文档明确提醒该 API 不执行完整验证。涉及跳转、代理、白名单或访问控制时,必须在读取字段后增加应用层约束。

总结

Python urllib.parse.urlsplit 拆分主机和端口的核心写法是 parsed = urlsplit(raw_url)、parsed.hostname 和 parsed.port。先记住 // 决定网络位置是否被识别,再处理 IPv6 方括号、缺少端口和 ValueError,最后把 scheme、hostname 和端口范围交给业务规则校验,就能避免把一个“能拆分”的字符串误当成“可信连接地址”。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>