【Python】URL 結合は「+」ではなく「urljoin」を使うべき理由と注意点

  • URLをコピーしました!
目次

はじめに

Python で Web スクレイピングや REST API を利用するコードを書いていると、「ベースとなる URL(ドメイン)」「相対パス」 をくっつけて、アクセス先の絶対 URL を作りたい場面が頻繁にあります。

base = "https://example.com/"
path = "api/v1/users"
# これらをくっつけたい

この時、単純に文字列の足し算(+ 演算子)で結合していませんか? 文字列結合は一見簡単そうに見えますが、「スラッシュ(/)が重複してしまった」 あるいは 「足りなくて無効な URL になってしまった」 というバグの温床になりがちです。

Python には、この「スラッシュ問題」を賢く解決してくれる標準ライブラリ urllib.parse.urljoin が用意されています。 本記事では、urljoin の基本的な使い方と、開発者が知っておくべき挙動の注意点(スラッシュの罠)について解説します。

この記事でわかること
  • urljoin を使った正しい URL 結合方法
  • 文字列結合(+)と比較したメリット
  • 【重要】 ベース URL の末尾スラッシュ有無による挙動の違い

urljoin の基本的な使い方

urljoin は、Python の標準ライブラリである urllib.parse モジュールに含まれています。 使い方は非常にシンプルで、「ベースとなる URL」「くっつけたい URL(相対パス)」 を引数に渡すだけです。

基本構文

from urllib.parse import urljoin

urljoin(base, url)
  • 第1引数(base): 基底となる URL(例: https://example.com/
  • 第2引数(url): 結合したい相対パスなど(例: next/page

実装例

実際に、ブログのトップページ URL とカテゴリーのパスを結合してみましょう。

from urllib.parse import urljoin

# ベースとなる URL
base_url = 'https://friendsnow.hatenablog.com/'

# 結合したい相対パス
relative_url = 'archive/category/プログラミング-Python'

# 結合を実行
full_url = urljoin(base_url, relative_url)

print(full_url)

▼ 実行結果

https://friendsnow.hatenablog.com/archive/category/プログラミング-Python

このように、ベース URL と相対パスがきれいに結合され、正しい絶対 URL が生成されました。

【重要】スラッシュ(/)の有無による挙動の違い

urljoin を使う上で最も注意が必要なのが、「Base URL の末尾」「結合するパス(相対 URL)の先頭」 にあるスラッシュ(/)の扱いです。

以下の3パターンを理解しておけば、バグを未然に防げます。

パターンA: Base の末尾に「/」がある場合(通常)

Base URL がディレクトリ(フォルダ)として扱われます。 直感的で一番わかりやすい結合です。

base = "http://example.com/api/"  # 末尾あり
path = "users"

print(urljoin(base, path))
# 結果: http://example.com/api/users
# → 素直に後ろにくっつきます。

パターンB: Base の末尾に「/」がない場合(置換の罠)

これがハマりポイントです。 末尾にスラッシュがない場合、urljoin はベースの最後の部分(ここでは api)を「ファイル名」だと判断します。 そのため、最後の部分を取り除いてから結合します。

base = "http://example.com/api"   # 末尾なし(ファイル扱い)
path = "users"

print(urljoin(base, path))
# 結果: http://example.com/users
# → 「api」が消えて、「users」に置き換わりました!

💡 なぜこうなる? ブラウザで http://example.com/api というページを見ている時に、<a href="users"> というリンクをクリックした時の挙動と同じです。「同じ階層にある別のファイル(users)に移動する」という動きになります。

パターンC: 相対URL が「/」で始まる場合(ルート相対パス)

結合するパスの先頭にスラッシュがある場合、Base URL のパス部分は無視され、ドメイン直下(ルート)からのパスになります。

base = "http://example.com/api/v1/"
path = "/users"  # 先頭あり(ルート指定)

print(urljoin(base, path))
# 結果: http://example.com/users
# → 「/api/v1/」は無視され、ドメイン直下につきます。

挙動まとめ(早見表)

パターンBase URL(第1引数)Relative URL(第2引数)結合結果解説
A(通常)…/folder/file…/folder/fileディレクトリの中に配置
B(置換)…/folderfile…/filefolder が file に置換される
C(ルート)…/folder//file…/fileドメイン直下に配置

開発時は「Base URL の末尾スラッシュ」を意識しましょう。

文字列結合(+)との比較

ここまで読んだ方の中には、「仕様が複雑だから、自分で <code>+</code> を使って結合したほうが簡単では?」と思った方もいるかもしれません。

しかし、単純な文字列結合(<code>+</code>)で安全な URL を作ろうとすると、コードは意外と複雑になります。

文字列結合(+)で書く場合の苦労

「スラッシュが重ならないように」「足りなければ足すように」という処理を自前で書く必要があります。

base = "http://example.com/api"
path = "users"

# そのまま足すと...
# http://example.com/apiusers (スラッシュ抜け!)

# 自力で制御しようとすると...
if not base.endswith("/") and not path.startswith("/"):
    full_url = base + "/" + path
elif base.endswith("/") and path.startswith("/"):
    full_url = base[:-1] + path
else:
    full_url = base + path

↑ たったこれだけのために、こんな条件分岐を書くのはナンセンスですよね。

urljoin を使う場合

<code>urljoin</code> は、Web の標準規格(RFC 1808など)に基づいて、これらの処理を内部で自動的に行ってくれます。

# これだけでOK!
full_url = urljoin(base, path)

「スラッシュの有無を気にせず、ライブラリに任せる」ことができるのが最大のメリットです。 ただし、先ほどの 「パターンB(末尾にスラッシュがないと置換される)」 という仕様だけは、意識して使いこなしましょう。

まとめ

本記事では、Python で URL を結合する際に推奨される <code>urllib.parse.urljoin</code> について解説しました。

基本

URL の結合は + ではなく urljoin を使う。

注意

Base URL の末尾にスラッシュがないと、最後のパスが上書き(置換)される

対策

ディレクトリの下にパスを繋げたい場合は、Base URL の末尾に必ず / をつけておく。

以上、最後までお読みいただきありがとうございました。


よかったらシェアしてね!
  • URLをコピーしました!

この記事を書いた人

インフラ(クラウド/NW/仮想化)から Web 開発まで、技術領域を横断して活動するエンジニア💻 コンシューマー向けエンタメ事業での新規開発・運営経験を活かし、実戦的な技術ノウハウを発信中

[ Certs ] CCIE Lifetime Emeritus / VCAP-DCA ✒️ [ Life ] 技術書・ビジネス書愛好家📖 / 小・中学校で卓球コーチ👟

目次