首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 更多程式設計知識

Python 從 URL 提取檔名:os.path.basename 與 urllib.parse 完整比較

     編輯:更多程式設計知識

從 URL 字串取得檔名是常見需求(下載、儲存、日誌)。最簡單的是 os.path.basename(url),但 URL 含查詢字串、片段、百分號編碼時會產生雜訊。本文比較多種方法、處理邊界情況並提供健壯的通用函數。

最簡單做法


import os
url = 'https://www.example.com/images/logo.gif'
filename = os.path.basename(url)
print(filename)  # logo.gif

問題:查詢字串與片段


url = 'https://example.com/file.pdf?download=1#page=2'
print(os.path.basename(url))  # file.pdf?download=1#page=2  ← 不想要

健壯解法:urllib.parse.urlparse


from urllib.parse import urlparse, unquote
import posixpath  # URL 路徑永遠用 POSIX 風格

def get_filename_from_url(url):
    parsed = urlparse(url)
    path = parsed.path
    # 取路徑最後一段
    filename = posixpath.basename(path)
    # 解碼百分號編碼
    filename = unquote(filename)
    return filename

print(get_filename_from_url('https://example.com/file.pdf?download=1#page=2'))  # file.pdf
print(get_filename_from_url('https://example.com/path/%E4%B8%AD%E6%96%87.txt'))  # 中文.txt

進階:Content-Disposition 標頭優先

HTTP 回應可能在 Content-Disposition 提供真實檔名(含編碼處理),優先順序:

  1. Content-Disposition: attachment; filename="real.pdf"
  2. Content-Disposition: attachment; filename*=UTF-8''real.pdf (RFC 5987)
  3. URL 路徑最後一段
  4. 預設名稱 download.bin

import re, email.utils

def parse_content_disposition(cd_header):
    """解析 Content-Disposition,回傳 (filename, filename_star)"""
    if not cd_header:
        return None, None
    # 簡易解析,生產建議用 email.message 或 requests-toolbelt
    value, params = email.utils.parse_header(cd_header)
    return params.get('filename'), params.get('filename*')

def get_final_filename(response, url):
    # 1. Content-Disposition
    cd = response.headers.get('Content-Disposition')
    if cd:
        fn, fn_star = parse_content_disposition(cd)
        if fn_star:
            # RFC 5987: encoding'lang'value
            encoding, lang, value = fn_star.split("'", 2)
            return unquote(value, encoding=encoding)
        if fn:
            return unquote(fn)
    # 2. URL 路徑
    return get_filename_from_url(url)

完整下載器片段


import requests
from pathlib import Path

def download_file(url, dest_dir='.'):
    resp = requests.get(url, stream=True, timeout=30)
    resp.raise_for_status()
    filename = get_final_filename(resp, url)
    dest = Path(dest_dir) / filename
    dest.parent.mkdir(parents=True, exist_ok=True)
    with open(dest, 'wb') as f:
        for chunk in resp.iter_content(8192):
            f.write(chunk)
    return dest

常見邊界情況

URLos.path.basenameurlparse+basename備註
https://a/b/c/'' (空)''目錄型 URL,需回退預設名
https://a/b/c?queryc?queryc查詢字串污染
https://a/b/%E4%B8%AD.txt%E4%B8%AD.txt中.txt需 unquote
https://a/b/..%2Fc.txt..%2Fc.txtc.txt路徑遍歷攻擊,需 normalize

最佳實踐總結

  1. 永遠用 urllib.parse.urlparse 解析 URL,不要用字串切分。
  2. 路徑操作用 posixpath(URL 永遠是 POSIX 風格)。
  3. 解碼用 urllib.parse.unquote(支援 UTF-8)。
  4. 優先信任 Content-Disposition 標頭。
  5. 清理檔名:移除控制字元、限制長度、防路徑遍歷(os.path.normpath 後確保不以 .. 開頭)。

掌握 URL 檔名提取的完整鏈路,能讓下載器、爬蟲、API 用戶端在面對各種奇葩 URL 時依然健壯可靠。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved