ゼロから正規表現ヒーローへ
単一正規表現で URL 風文字列を抽出・解析
目次
- 🚀 イントロダクション
- 🔍 テキストから URL を抽出する
- 🛳️ 120 バイト以上の正規表現
- 🧩 ステップごとの分解
- 🛠️ パース例
- ☑️ 次のステップ
- 📝 まとめ
- 📚 さらに学ぶ
TL;DR: まずは 120 バイト以上の正規表現 を確認してください。
🚀 イントロダクション
生テキストから URL を抽出する作業は、時に手間のかかるモグラ叩きゲームのように感じられます。句読点や丸括弧で囲まれた文字列、曖昧なフォーマットがすべて絡み合い、抽出を妨げます。ウェブスクレイパー、データ分析ツール、チャットアプリのいずれを構築していても、正確な URL 抽出は必須です。
この投稿では、柔軟な 2 段階アプローチで問題に正面から取り組みます。まず 潜在的な URL らしさを持つ文字列をすべて捕捉 し、その後で検証を別プロセスで行うことが目標です。
💡 注: このパターンは URL の 検証 用 ではありません!句読点や誤字に対して意図的に寛容に設計されています。
🔍 目的: テキストから URL を抽出する
生テキストから URL を抽出する際は、2 段階の手法が有効です。
- URL らしいものはすべて捕捉: 幅広く網を張り、URL になり得る 文字列をすべて取得します。ここで「120 バイト以上の正規表現」が活躍します。
- 検証: 捕捉した候補に対して、二次チェック(例: DNS 解決、既知ドメインとの比較)を行い、無効なエントリを除去します。
課題の可視化
extract と parse という用語はしばしば同義に扱われますが、実際には別々のプロセスを指します。URL を 抽出 するとは、テキスト全体から潜在的な URL を検出し捕捉することです。一方、パース するとは、取得した URL を構成要素に分解することを意味します。
パース、すなわち「URL の各部品」について言及するときは、以下のコンポーネントを指しています。
RegEx101 のサブストリングマッチングのスクリーンショットを見る
正規表現の詳細に入る前に、視覚的ツールでパターンがどれだけ多くのマッチを捕捉できるか確認しましょう。

120 バイト超の正規表現
以下は URL を一括で抽出・パースできるよう設計された、コンパクトな正規表現です。さまざまなプロトコル、ドメイン、パス、そしてオプションのクエリ/フラグメント部をサポートします。
心配しないでください—段階的に分解していきます!
const urlRegex = /([-.a-z0-9]+:\/{1,3})([^-\/\.[\](|)\s?][^`\/\s\]?]+)([-_a-z0-9!@$%^&*()=+;/~\.]*)[?]?([^#\s`?]*)[#]?([^#\s'"`\.,!]*)/gi;// Compatibility: ES5+
// 同じパターンを可読性向上のため改行で分割:([-.a-z0-9]+:\/{1,3})([^-\/\.[\](|)\s?][^`\/\s\]?]+)([-_a-z0-9!@$%^&*()=+;/~\.]*)[?]?([^#\s`?]*)[#]?([^#\s'"`\.,!]*)これまでに遭遇した(または自作した)最もワイルドな正規表現を、コメント欄で共有してください! 🚀
🧩 ステップバイステップで分解する
正規表現を構成要素に分解して、動作を確認しましょう。
1. プロトコル (グループ 1): ([-.a-z0-9]+:/{1,3})
- 目的: URL のプロトコル部分をマッチさせます(例:
http://、ftp://、custom-scheme://)。 解説:
[-.a-z0-9]+: 小文字の英字、数字、ハイフン、またはピリオドが 1 文字以上続くパターンにマッチします(プロトコルスキームで一般的です)。:/{1,3}: コロンに続くスラッシュが 1〜3 個(:/、://、または:///)にマッチします。
2. ドメイン (グループ 2): ([^-/.[](|)s?][^`/s]?]+)
- 目的: URL のドメインまたはホスト部分を取得します。
解説:
[^-/.[](|)\s?]: 指定した特殊文字と空白以外の任意の文字にマッチします。[^`/\s]?]+: バックティック、スラッシュ、空白、または閉じ角括弧以外の文字が 1 文字以上続く部分にマッチします。
3. パス (グループ 3): ([-_a-z0-9!@$%^&*()=+;/~\.]*)
- 目的: URL のパス部分にマッチします。
解説:
[-_a-z0-9!@$%^&()=+;/~.]: パスで一般的に見られる URL 安全文字に 0 回以上マッチします。
4. クエリ (グループ 4): [?]?([^#\s`?]*)
- 目的: 任意でクエリ文字列にマッチし、先頭は
?で始まります。 解説:
[?]?:?を任意でマッチします。角括弧は必須ではありませんが、超簡潔な??よりも可読性が高く、次の[#]?と視覚的に対応させやすくなります。([^#\s`?]*): ハッシュ、空白、バックティック、クエリマーク以外の文字に 0 回以上マッチします。
5. フラグメント (グループ 5): [#]?([^#\s’”`.,!]*)
- 目的: 任意でフラグメント識別子にマッチし、先頭は
#です。 解説:
[#]?:#を任意でマッチします。([^#\s’”`.,!]*): 禁止された句読点や空白以外の文字に 0 回以上マッチします。
🛠️ パース例
以下は、この巨大な正規表現を JavaScript で実際に使う例です。
const text = `Check this out: https://example.com/path?query=123#sectionAnd also (ftp://files.server.org/index).Plus a weird one: custom-scheme://host/param;weird^stuff`;
const urlRegex = /([-.a-z0-9]+:\/{1,3})([^-\/\.[\](|)\s?][^`\/\s\]?]+)([-_a-z0-9!@$%^&*()=+;/~\.]*)[?]?([^#\s`?]*)[#]?([^#\s'"`\.,!]*)/gi;
const matches = [ ...text.matchAll(urlRegex),].map((match) => match[0]);console.log("Extracted URLs:", matches);
const parts = [ ...text.matchAll(urlRegex),].map((match) => match.slice(1));console.log("Extracted Parts:", parts);[ "https://example.com/path?query=123#section", "ftp://files.server.org/index", "custom-scheme://host/param;weird^stuff"][ [ "https://", // Protocol "example.com", // Domain "/path", // Path "query=123", // Query "section" // Fragment ], [ "ftp://", // Protocol "files.server.org", // Domain "/index", // Path "", // Query "" // Fragment ], [ "custom-scheme://", // Protocol "host", // Domain "/param;weird^stuff", // Path "", // Query "" // Fragment ]]☑️ 次のステップ
利用シーンに応じて、この正規表現を微調整したり、追加のバリデーションや後処理を組み込む必要が出てくるでしょう。
プロジェクト別の要件
プロジェクトごとに求められる要件やセキュリティ上の懸念は異なります。
- Web Scraping: URL が到達可能で信頼できるかを検証します。
- Data Processing: ユーザー生成コンテンツから URL を抽出し、安全性を確保します。
- Data Analysis: 研究やマーケティング目的で、重複や無関係なリンクを除外します。
- User-facing Applications: チャットアプリやフォーラムで URL を自動的にハイパーリンク化します。
後処理と検証
取得した候補 URL に対して追加チェックを行います:
- DNS ルックアップ: ドメインが解決できるか確認します。
- 安全性チェック: 悪意あるサイトやフィッシングサイトかどうかをサービスで確認します。
- カスタムルール: プロジェクト固有のフィルタを適用します(例:許可する TLD、最大 URL 長さ)。
📝 Summary
半構造化文字列データの抽出は、正規表現の熟練度を最大限に活かす、最も満足感のある作業の一つです。
以下は主要なポイントのまとめです:
- 視覚的ツールを使って正規表現を書き、テスト しながら パターンを理解 します。(Regex パターンを試す)
- 課題を分割 してそれぞれを個別に解決します。捕捉グループは正規表現における比喩的な「道標」のような役割を果たします。
- 緩やかなマッチ式を使用 し、データ取り込み時に厳密な仕様への適合は避けます。
- 抽出後に検証ステップを適用 することが必須です――プロジェクトのセキュリティ要件や特有のニーズを常に考慮してください。
これらの手順に従えば、任意の半構造化文字列データを確実に抽出でき、以降の処理や検証の土台が整います。
📚 さらに学ぶ
- RegEx101.com のライブデモで試すことを忘れずに!(ライブデモを試す)
- 元の StackOverflow 質問と、こちらにある私の回答 (回答へのリンク)
- MDN の正規表現ドキュメント (MDN Docs on Regular Expressions)
- 高度な正規表現テクニック:先読み・後読みなど、より正確なマッチングのためのパターンを探求してください。(Advanced Regex Techniques)
- RFC 3986 - URI 汎用構文 (RFC 3986 - URI Generic Syntax)