更新: 2026年03月 · 9 分で読める · 4,577 文字
Pythonの正規表現で日々のテキスト処理を効率化する実践テクニック
本記事では、Pythonのreモジュールを使った正規表現の実践的な活用方法を紹介します。メールアドレスの抽出、ログファイルの解析、データクレンジングなど、実務でよく使うパターンをすぐに仕事で活用できるコード例で解説します。
Pythonの正規表現とは?基礎知識から始める
Pythonの正規表現は、reモジュールを使ってテキストパターンマッチングと置換を行う強力な機能です。データ分析、ログ解析、入力値の検証など、開発現場で毎日のように活用されます。
正規表現は強力な反面、複雑なパターンになると可読性が落ちやすいため、実務ではシンプルなパターンから段階的に複雑化させるアプローチが重要です。
よく使う基本パターン
| パターン | 説明 | マッチ例 |
|---|---|---|
\d |
数字1文字 | 0-9 |
\w |
単語文字(英数字とアンダースコア) | a-z, A-Z, 0-9, _ |
\s |
空白文字 | スペース、タブ、改行 |
+ |
1回以上の繰り返し | a+は「a」「aa」「aaa」に対応 |
* |
0回以上の繰り返し | a*は「」「a」「aa」に対応 |
実践例1:メールアドレスの抽出と検証
テキストファイルやログから有効なメールアドレスを抽出する場面は頻繁にあります。以下のコード例では、re.findall()を使ってメールアドレスを一括抽出し、さらにre.match()で個別の妥当性をチェックします。
import re
# テキストからメールアドレスをすべて抽出
text = """
連絡先:user1@example.com、user2@test.co.jp
管理者:admin@company.org
"""
# 基本的なメールパターン(簡易版)
email_pattern = r'[\w\.-]+@[\w\.-]+\.\w+'
emails = re.findall(email_pattern, text)
print("抽出されたメール:")
for email in emails:
print(f" {email}")
# より厳密な検証(RFC 5322準拠ではなく実用的なレベル)
strict_pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
for email in emails:
if re.match(strict_pattern, email):
print(f"✓ 有効: {email}")
else:
print(f"✗ 無効: {email}")
メール抽出でよくあるハマりポイント
問題1: 日本語ドメイン対応 — 国際化ドメイン名(IDN)を含むメールアドレスをマッチさせたい場合、単純な\wでは対応できません。その場合は[\w\u3000-\u303f\u3040-\u309f\u30a0-\u30ff\u4e00-\u9fff]のようにUnicodeレンジを追加します。
問題2: 長すぎるメールの除外 — メールアドレスの長さに上限がある場合、^.{1,254}$のような長さチェックを組み合わせます。
# 日本語対応とメール長チェックの例
import re
email_text = "contact@日本企業.jp invalid_very_long_email_address_that_exceeds_limit@test.co.jp"
# 国際化ドメイン対応
idna_pattern = r'[\w\.\-\u3000-\u303f\u3040-\u309f\u30a0-\u30ff\u4e00-\u9fff]+@[\w\.\-\u3000-\u303f\u3040-\u309f\u30a0-\u30ff\u4e00-\u9fff]+'
emails = re.findall(idna_pattern, email_text)
# メール長チェック(1〜254文字)
for email in emails:
if 1 <= len(email) <= 254:
print(f"OK: {email}")
else:
print(f"NG(長さ超過): {email}")
実践例2:ログファイル解析とデータ抽出
アクセスログやエラーログから特定の情報を抽出する場面では、複数のパターンを組み合わせた正規表現が活躍します。以下の例では、Apacheアクセスログから日時、ステータスコード、レスポンスサイズを抽出します。
import re
from datetime import datetime
# Apacheアクセスログのサンプル
log_lines = [
'192.168.1.1 - - [01/Jan/2025:10:15:30 +0900] "GET /api/users HTTP/1.1" 200 1234',
'192.168.1.2 - - [01/Jan/2025:10:16:45 +0900] "POST /api/data HTTP/1.1" 201 567',
'192.168.1.3 - - [01/Jan/2025:10:17:22 +0900] "GET /404 HTTP/1.1" 404 0',
]
# ログパターン:IP、日時、メソッド、パス、ステータス、サイズ
log_pattern = r'(\d+\.\d+\.\d+\.\d+).*?\[(.+?)\].*?"(\w+)\s(.+?)\s.*?"\s(\d+)\s(\d+)'
for line in log_lines:
match = re.search(log_pattern, line)
if match:
ip, timestamp, method, path, status, size = match.groups()
print(f"IP: {ip}, メソッド: {method}, パス: {path}, ステータス: {status}, サイズ: {size}B")
# エラーのみをフィルタリング
print("\n--- エラーログのみ ---")
for line in log_lines:
match = re.search(log_pattern, line)
if match:
status = match.group(5)
if status.startswith('4') or status.startswith('5'):
print(f"エラー検出: {line}")
ログ解析で効率を上げるコツ
re.compile()の活用 — 同じパターンで複数行をマッチさせる場合、re.compile()で事前にパターンをコンパイルすると処理速度が向上します。大規模ログファイル(数100万行)では10-20%の高速化が期待できます。
import re
import time
# パターンを事前コンパイル
log_pattern = re.compile(r'(\d+\.\d+\.\d+\.\d+).*?\[(.+?)\].*?"(\w+)\s(.+?)\s.*?"\s(\d+)\s(\d+)')
log_lines = ['192.168.1.1 - - [01/Jan/2025:10:15:30 +0900] "GET /api/users HTTP/1.1" 200 1234'] * 100000
# コンパイル版(高速)
start = time.time()
for line in log_lines:
match = log_pattern.search(line)
elapsed_compiled = time.time() - start
print(f"コンパイル版: {elapsed_compiled:.3f}秒")
実践例3:データクレンジングと形式統一
CSVデータやフォーム入力には不要な空白や特殊文字が含まれることが多いです。re.sub()を使った置換で、データを統一フォーマットに整形します。
import re
# 複数の電話番号形式をすべて統一フォーマットに変換
phone_numbers = [
"090-1234-5678",
"09012345678",
"090 1234 5678",
"+81-90-1234-5678",
"(090)1234-5678",
]
# ステップ1:非数字文字をすべて除去
# ステップ2:先頭の+81を0に置換
# ステップ3:0から始まる10桁に統一
def normalize_phone(phone):
# 非数字と括弧を除去
digits_only = re.sub(r'[^\d+]', '', phone)
# 国番号を日本の0に変換
digits_only = re.sub(r'^\+81', '0', digits_only)
# 10桁か11桁かチェック
if re.match(r'^0\d{9}$', digits_only):
return digits_only
elif re.match(r'^0\d{10}$', digits_only):
return digits_only
else:
return None
print("電話番号正規化結果:")
for phone in phone_numbers:
normalized = normalize_phone(phone)
print(f"{phone:20} → {normalized if normalized else '無効'}")
re.sub()で複数パターンを同時処理
複数の置換ルールがある場合、re.sub()を連鎖させるより、関数を使った動的な置換が効率的です。
import re
text = "価格: ¥1,234,567 / 割引: ¥12,345"
# パターン: 金額(数字+カンマ)をマッチ
def remove_currency_and_comma(match):
value = match.group(1) # マッチした値(例:¥1,234,567)
digits = re.sub(r'[^\d]', '', value) # 数字のみ抽出
return digits
# 複雑な変換ルール
result = re.sub(r'(¥[\d,]+)', remove_currency_and_comma, text)
print(f"変換前: {text}")
print(f"変換後: {result}")
# さらに別の規則を適用(複数ステップ)
text2 = "Contact: user@example.com, phone: 090-1234-5678"
# メールと電話番号をマスク
text2 = re.sub(r'[\w\.-]+@[\w\.-]+', 'xxx@xxx.xxx', text2)
text2 = re.sub(r'\d{3}-\d{4}-\d{4}', 'xxx-xxxx-xxxx', text2)
print(f"マスク後: {text2}")
正規表現の使うべき場面と使うべきでない場面
✅ 正規表現を使うべき場面
- テキストファイルやログから特定パターンのデータを抽出する
- ユーザー入力の形式検証(メール、電話番号、郵便番号など)
- 複数の異なる形式を統一フォーマットに変換する
- テキストの置換・削除で複雑な条件が必要な場合
- HTMLやJSONをシンプルなパターンマッチで処理する場合
❌ 正規表現を使うべきでない場面
- ネストされたHTML/XMLの解析 →
BeautifulSoupやxml.etreeを使用 - 複雑なJSON処理 →
jsonモジュールを使用 - データベースクエリの構築 → ORM(SQLAlchemy)を使用
- HTMLからのテキスト抽出 → HTMLパーサーライブラリを使用
デバッグのコツ:正規表現を可視化する
複雑な正規表現のパターンマッチングをデバッグする際は、re.finditer()で各マ
おすすめPythonリソース
- Python Official Tutorial The official Python tutorial. Perfect for building fundamentals.
- Real Python Practical Python tutorials for intermediate and advanced developers.
- PyPI Official Python package repository for library discovery.