Webスクレイピングを行っている際に、取得したURLが「/articles/python」のような形式になっており、そのままではアクセスできず困った経験はないでしょうか。
HTML内では記述を簡略化するためにドメイン名を省略した相対パスが多用されますが、プログラムでリンク先へ遷移するためには絶対パスへの変換が不可欠です。
本記事では、PythonのBeautifulSoupで抽出した相対パスを、標準ライブラリを用いてスマートに絶対パスへ変換する方法について詳しく解説します。
Webスクレイピングにおける相対パス変換の重要性
WebサイトのHTMLソースコードを確認すると、リンクを示すhref属性や画像を示すsrc属性には、ドメイン名を含まないパスが記述されていることが多々あります。
ブラウザで閲覧している場合は、ブラウザが現在のページのURLを基準にして自動的に補完を行ってくれますが、スクレイピングプログラムでは自前でこの補完処理を行う必要があります。
相対パスのままでは、requestsライブラリなどで新しいページを取得しようとした際にエラーが発生してしまいます。
そのため、抽出したデータを利用可能な形に整える「クレンジング」の工程として、URLの結合と変換は非常に重要なステップとなります。
urljoin関数を使用したURL結合の基本
PythonでURLの相対パスを絶対パスに変換する最も標準的かつ推奨される方法は、urllib.parseモジュールのurljoin関数を使用することです。
この関数を使用すると、ベースとなるURLと抽出した相対パスを、規約に従って適切に結合してくれます。
from urllib.parse import urljoin
# ベースとなるURL
base_url = "https://example.com/blog/index.html"
# 取得した相対パス
relative_path = "detail/python-tips.html"
# 絶対パスへの変換
absolute_url = urljoin(base_url, relative_path)
print(absolute_url)
https://example.com/blog/detail/python-tips.html
このように、urljoinは単に文字列を連結するのではなく、URLの構造を理解した上で正しいパスを生成してくれます。
階層構造を考慮したパス変換
urljoinの強力な点は、../(一つ上の階層へ戻る)といった相対パスの記法も正しく解釈してくれる点にあります。
手動で文字列操作を行おうとするとミスが発生しやすい部分ですが、標準ライブラリに任せることで安全に処理できます。
from urllib.parse import urljoin
base_url = "https://example.com/products/software/list.html"
relative_path = "../../contact.html"
# 上位階層への移動を含めた結合
result = urljoin(base_url, relative_path)
print(result)
https://example.com/contact.html
実行結果を見ると、正しくドメイン直下の階層まで遡って結合されていることがわかります。
BeautifulSoupとurljoinを組み合わせた実践的な書き方
実際にWebサイトからすべてのリンクを取得し、それらを絶対パスのリストにする実用的なコード例を見ていきましょう。
BeautifulSoupで見つかったすべてのaタグから、href属性を取り出して変換を行います。
import requests
from bs4 import BeautifulSoup
from urllib.parse import urljoin
# ターゲットサイトのURL
target_url = "https://example.com/news/"
response = requests.get(target_url)
soup = BeautifulSoup(response.text, "html.parser")
# すべてのリンクを絶対パスで取得
links = []
for a_tag in soup.find_all("a"):
href = a_tag.get("href")
if href:
# urljoinで絶対パスに変換(hrefが絶対パスならそのまま維持される)
abs_url = urljoin(target_url, href)
links.append(abs_url)
# 結果の一部を表示
for link in links[:5]:
print(link)
このコードの優れた点は、href属性にすでにhttpから始まる絶対パスが入っていた場合、urljoinは第二引数の絶対パスを優先して返し、二重にドメインが付与されるのを防いでくれる点です。
変換時に注意すべき特殊なケース
URL変換を行う際には、いくつか注意しなければならない特殊なパターンが存在します。
これらを考慮に入れておかないと、スクレイピングの精度が低下する恐れがあります。
ルート相対パスの扱い
/から始まるパスは「ルート相対パス」と呼ばれ、常にドメインの直下を指します。
urljoinはこれらも自動的に判別し、ベースURLのパス部分を無視してドメインと結合してくれます。
プロトコル相対パスの扱い
最近のWebサイトでは、//cdn.example.com/js/script.jsのように、スキーム(http/https)を省略した記述が見られます。
これもurljoinを使えば、ベースURLのスキームを補完して変換することが可能です。
baseタグが存在する場合
HTMLのhead内に<base href="...">というタグが存在する場合、すべての相対パスはそのURLを基準にするというルールがあります。
精度の高いスクレイパーを作成する場合は、BeautifulSoupでまずbaseタグを探し、存在すればその値をurljoinの第一引数(ベースURL)として使用するように実装しましょう。
変換処理を効率化するテクニック
大量のリンクを処理する場合、リスト内包表記を使用することでコードをより簡潔に記述できます。
Pythonらしい書き方を採用することで、可読性が向上しメンテナンスも容易になります。
# リスト内包表記を使った変換
all_links = [urljoin(target_url, a.get("href")) for a in soup.find_all("a") if a.get("href")]
このように、if a.get("href")を組み込むことで、href属性を持たないタグによるエラーを回避しながら一括処理が可能です。
まとめ
BeautifulSoupで取得した相対パスを絶対パスに変換するには、標準ライブラリのurllib.parse.urljoinを活用するのが最適です。
この関数は、複雑な階層構造やルート相対パス、スキーム省略形式など、Web特有の多様なパス記法を正確に処理してくれます。
手動での文字列置換や連結に頼らず、信頼性の高いライブラリの機能を使いこなすことで、バグの少ないスクレイピングコードを実現しましょう。
これからスクレイピングを始める際は、URLを取得したらまずurljoinを通すという流れを習慣化することをおすすめします。
