Claude にきいてみた。

Python の正規表現で `()` を使うとグループ化できるって聞いたけど、どういうこと?
正規表現における括弧 () には 2 つの役割がある。1 つは複数の文字をまとめて扱う「グループ化」、もう 1 つはマッチした部分を取り出す「キャプチャ」だ。
たとえば (ab)+ と書くと、ab という 2 文字のかたまりが 1 回以上繰り返すパターンを表せる。括弧がなければ ab+ は「a の後に b が 1 回以上」という別の意味になってしまう。
ab, abab, ababab ... にマッチ
ab, abb, abbb ... にマッチ
キャプチャの機能を使うと、マッチした文字列の一部だけを抜き出せる。re.search や re.findall と組み合わせて使うのが一般的だ。

具体的なコード例を見せて。
日付文字列から年・月・日をそれぞれ取り出す例を見てみよう。
import re text = "今日は2025-06-15です" pattern = r"(\d{4})-(\d{2})-(\d{2})" match = re.search(pattern, text) if match: print(match.group(0)) # マッチ全体: 2025-06-15 print(match.group(1)) # 1番目のグループ: 2025 print(match.group(2)) # 2番目のグループ: 06 print(match.group(3)) # 3番目のグループ: 15
group(0) はマッチ全体を返し、group(1) 以降は括弧で囲んだ部分を左から順に返す。複数の値を一度に取得したい場合は groups() メソッドが便利だ。
year, month, day = match.groups() print(f"{year}年{month}月{day}日") # 2025年06月15日

`re.findall` だとどうなる?
re.findall にグループを含むパターンを渡すと、マッチ全体ではなくグループ部分だけがリストで返ってくる。グループが複数ある場合はタプルのリストになる。
import re text = "会議: 2025-06-15, 締切: 2025-07-01" pattern = r"(\d{4})-(\d{2})-(\d{2})" results = re.findall(pattern, text) print(results) # [('2025', '06', '15'), ('2025', '07', '01')]
この挙動を知らないと「なぜ日付全体が取れないのか」と混乱しがちなので注意が必要だ。マッチ全体を取得したい場合は、グループを使わないパターンにするか、後述する非キャプチャグループを使う。

非キャプチャグループって何?
(?:...) という書き方をすると、グループ化はするがキャプチャはしない「非キャプチャグループ」になる。量指定子をまとめて適用したいが、結果には含めたくない場合に使う。
import re text = "color: red, colour: blue" pattern = r"colou?r: (\w+)" results = re.findall(pattern, text) print(results) # ['red', 'blue']
上の例では u? で「u があってもなくてもよい」としているが、もし (ou)? のように括弧を使うと、その部分もキャプチャされてしまう。
# 意図しない結果 pattern_bad = r"col(ou)?r: (\w+)" results_bad = re.findall(pattern_bad, text) print(results_bad) # [('', 'red'), ('ou', 'blue')]
非キャプチャグループを使えば、グループ化しつつキャプチャを避けられる。
# 非キャプチャグループを使用 pattern_good = r"col(?:ou)?r: (\w+)" results_good = re.findall(pattern_good, text) print(results_good) # ['red', 'blue']

グループに名前を付けることもできる?
(?P<name>...) という構文で名前付きグループを作れる。番号ではなく名前でアクセスできるため、複雑なパターンでも可読性が高まる。
import re text = "田中太郎 <tanaka@example.com>" pattern = r"(?P<name>.+?) <(?P<email>.+?)>" match = re.search(pattern, text) if match: print(match.group("name")) # 田中太郎 print(match.group("email")) # tanaka@example.com
groupdict() メソッドを使うと、名前をキーとした辞書形式で取得できる。
print(match.groupdict()) # {'name': '田中太郎', 'email': 'tanaka@example.com'}
グループが多くなると番号で管理するのは大変になる。パターンを変更したときに番号がずれるリスクもあるため、実務では名前付きグループを積極的に使うとよいだろう。