mei_13のPython講座 ロゴ

【解説】Pythonの正規表現マスターへ!re.findall()で文字列抽出を極める徹底解説




Pythonの正規表現マスターへ!re.findall()で文字列抽出を極める徹底解説


Hirokiのアイコン
【Hiroki】 Yukiさん、こんにちは! 最近Pythonでテキスト処理を勉強しているんですけど、特定のパターンに当てはまる部分を「全部」抜き出したい時って、どうすればいいんでしょうか? re.search()だと最初の一つしか見つからなくて、少し困っているんです。


Yukiのアイコン
【Yuki】 Hirokiくん、こんにちは。 文字列の中から条件に合うものをすべて見つけ出したいということですね。 ええと、そういう時にはPythonのreモジュールにあるre.findall()という関数を使うのが、一番の近道だと思います。 少し地味な機能に見えるかもしれませんが、データの抽出やクリーニングには欠かせない、とても大切なツールなんです。


Hirokiのアイコン
【Hiroki】 re.findall()ですね! やっぱり「全部(all)」を見つけるから、そういう名前なんですね。 具体的にどうやって使うのか、詳しく教えてもらえますか?


Yukiのアイコン
【Yuki】 はい、喜んで。 正規表現は少し複雑で、わたしもたまに「あれ?」って迷ってしまうことがあるんですけど……。 順を追って説明すれば、きっとHirokiくんも使いこなせるようになるはずです。 まずは、一番シンプルな使い方から見ていきましょう。

re.findall()の基本的な使い方


Yukiのアイコン
【Yuki】 re.findall()は、「パターン」に一致するすべての部分を「リスト」として返す関数です。 使い方はとてもシンプルで、re.findall(パターン, 対象の文字列)と書きます。

import re

text = "僕の電話番号は 080-1234-5678 で、友達のは 090-8765-4321 です。"
# 数字3桁-数字4桁-数字4桁のパターン
pattern = r"\d{3}-\d{4}-\d{4}"

results = re.findall(pattern, text)
print(results)


Hirokiのアイコン
【Hiroki】 あ、本当だ! 実行結果が ['080-1234-5678', '090-8765-4321'] というリストで返ってきました。 これなら、いくつ見つかっても一気に取得できて便利ですね。


Yukiのアイコン
【Yuki】 そうなんです。 re.search()と違って、マッチオブジェクトを介さずに直接文字列のリストが手に入るのが、re.findall()の大きな特徴ですね。 もし、一つも一致するものが見つからなかった場合は、エラーにはならず、空のリスト [] が返ってきます。 これも、プログラムを書く上では扱いやすいポイントかもしれません。


Hirokiのアイコン
【Hiroki】 なるほど。見つからなくてもプログラムが止まらないのは安心です。 ところで、パターンの前についている r って何でしたっけ?


Yukiのアイコン
【Yuki】 それは「生文字列(raw string)」という指定です。 正規表現では \(バックスラッシュ)をたくさん使うのですが、Pythonの普通の文字列だと \ は特別な意味(改行 \n など)を持ってしまいます。 r を付けることで、「この中の \ はそのまま正規表現の記号として扱ってね」とPythonに伝えることができるんです。 正規表現を書くときは、おまじないのように付けておくのが安全だと思います。

抽出の鍵を握る「メタ文字」と「特殊シーケンス」


Hirokiのアイコン
【Hiroki】 パターンの中に書いてある \d とか {3} とか、これらをもっと使いこなせれば、いろんなものが抜き出せそうですね。


Yukiのアイコン
【Yuki】 ええ、その通りです。 よく使う記号(メタ文字)をいくつか紹介しますね。これらを組み合わせることで、抽出の幅がぐっと広がります。

  • \d : 数字(0-9)に一致します。
  • \w : 英数字とアンダースコア(_)に一致します。
  • \s : 空白文字(スペース、タブ、改行など)に一致します。
  • + : 直前の文字が「1回以上」繰り返す場合に一致します。
  • * : 直前の文字が「0回以上」繰り返す場合に一致します。
  • . : 任意の1文字(改行を除く)に一致します。


Hirokiのアイコン
【Hiroki】 例えば、「英単語だけを全部抜き出したい」時はどうすればいいんでしょう?


Yukiのアイコン
【Yuki】 その場合は、\w+ を使うといいかもしれません。 やってみましょうか。

import re

text = "Python is fun! I love programming."
# 1文字以上の英数字の塊を抽出
words = re.findall(r"\w+", text)
print(words)


Hirokiのアイコン
【Hiroki】 実行すると ['Python', 'is', 'fun', 'I', 'love', 'programming'] になりました! 記号(! や .)が除外されて、言葉だけが綺麗に取れていますね。


Yukiのアイコン
【Yuki】 ふふ、上手くいきましたね。 このように、「何が何個続くか」を意識してパターンを作っていくのがコツです。

括弧 () を使う時の注意点:グループ化の挙動


Hirokiのアイコン
【Hiroki】 Yukiさん、ちょっと試してみたんですけど、パターンの中に括弧 () を入れたら、結果が変わっちゃったんです。 どうしてでしょうか?


Yukiのアイコン
【Yuki】 あ……それは、re.findall() の少し「お節介」というか、独特な仕様に触れたのかもしれません。 実は re.findall() は、パターンの中にグループ化の括弧 () があると、その括弧の中身だけを抽出するという性質を持っているんです。


Hirokiのアイコン
【Hiroki】 括弧の中身だけ?


Yukiのアイコン
【Yuki】 はい。例を見てみましょう。 例えば、日付の「年」の部分だけを括弧で囲ってみます。

import re

text = "2023/10/01, 2024/05/20"
# 年(4桁)だけをグループ化
pattern = r"(\d{4})/\d{2}/\d{2}"

results = re.findall(pattern, text)
print(results)


Hirokiのアイコン
【Hiroki】 あ、結果が ['2023', '2024'] になりました! 全体の一致(2023/10/01)じゃなくて、括弧の中の4桁の数字だけが取れるんですね。


Yukiのアイコン
【Yuki】 そうなんです。もし括弧が2つ以上あると、今度はタプルのリストが返ってくるようになります。

# 年と月をそれぞれグループ化
pattern = r"(\d{4})/(\d{2})/\d{2}"
results = re.findall(pattern, text)
print(results)
# 結果: [('2023', '10'), ('2024', '05')]


Hirokiのアイコン
【Hiroki】 なるほど!特定のパーツを分けて取りたい時にはすごく便利ですけど、知らないとびっくりしますね。 「全体もマッチさせたいけど、括弧を使いたい」時はどうすればいいんですか?


Yukiのアイコン
【Yuki】 その場合は、「非キャプチャグループ」というものを使います。 括弧の先頭に ?: を付けて (?:...) と書くと、「グループ化はするけれど、抽出の対象にはしない」という指定ができるんです。 少し複雑に見えるので、慣れるまでは大変かもしれませんけど……。

re.findall() でよく使われるフラグ


Hirokiのアイコン
【Hiroki】 大文字と小文字を区別しないで探したい時はどうすればいいんでしょう? 例えば "python" も "Python" も両方見つけたい時です。


Yukiのアイコン
【Yuki】 それには、第3引数に「フラグ」を渡すといいですよ。 よく使われるのは re.IGNORECASE(または re.I)です。

import re

text = "Python, python, PYTHON!"
results = re.findall(r"python", text, re.IGNORECASE)
print(results)
# 結果: ['Python', 'python', 'PYTHON']


Hirokiのアイコン
【Hiroki】 おぉ、一括で取れました! これならパターンの書き方を工夫しすぎなくても大丈夫そうですね。


Yukiのアイコン
【Yuki】 はい。他にも、.(ドット)を改行にも一致させる re.DOTALL や、各行の先頭にマッチさせる re.MULTILINE などがあります。 でも、まずは re.IGNORECASE を覚えておけば、日常的な作業では十分だと思います。

大規模なデータには re.finditer()


Hirokiのアイコン
【Hiroki】 あの、もしテキストがものすごく長かった場合でも、re.findall() を使って大丈夫でしょうか?


Yukiのアイコン
【Yuki】 それは、とても鋭い質問ですね。 re.findall() は見つけた結果をすべて一度にリストにしてメモリに読み込みます。 なので、数ギガバイトもあるような巨大なログファイルを解析しようとすると、メモリが足りなくなってしまうかもしれません。


Hirokiのアイコン
【Hiroki】 やっぱりそうですよね。どうすればいいんですか?


Yukiのアイコン
【Yuki】 そういう時は、re.finditer() を使うのがおすすめです。 re.finditer() は、結果を一つずつ取り出せる「イテレータ」という形式で返してくれます。

import re

text = "sample1 sample2 sample3"
pattern = r"sample\d"

# findall の代わりに finditer を使う
for match in re.finditer(pattern, text):
    print(f"見つかった文字列: {match.group()}, 位置: {match.start()}")


Yukiのアイコン
【Yuki】 これなら、一つずつ処理していくのでメモリに優しいですし、マッチした「場所(インデックス)」などの詳細な情報も取得できるんですよ。 「まずは手軽にリストが欲しいなら findall」、「大量のデータを丁寧に扱うなら finditer」と使い分けるのがスマートだと思います。

実戦的な例:メールアドレスの抽出


Hirokiのアイコン
【Hiroki】 だんだん使い方がわかってきました! 最後に、何か実用的な例を教えてもらえませんか?


Yukiのアイコン
【Yuki】 そうですね。よくある例として、テキストの中からメールアドレスをすべて抜き出してみましょう。 少し複雑なパターンになりますが、これまでの知識を組み合わせれば読めるはずです。

import re

content = """
お問い合わせは support@example.com までお願いします。
担当者の連絡先は hiroki-test@pro.example.co.jp です。
"""

# メールアドレスの簡易的なパターン
# [英数字や記号]+ @ [英数字や記号]+ . [英数字]
email_pattern = r"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}"

emails = re.findall(email_pattern, content)
print(emails)


Hirokiのアイコン
【Hiroki】 うわっ、パターンが急に難しくなりましたね……! でも、[] の中にある文字のどれかが +(1回以上)続いて、その後に @ が来て……と順番に見ていけば、なんとなく意味がわかります。


Yukiのアイコン
【Yuki】 すごいです、Hirokiくん。正解です! 正規表現は一見すると呪文のように見えますが、実はとても論理的に組み立てられているんです。 わたしも、最初は「こんなの無理……」って思っていたんですけど、一つ一つの記号の意味がわかると、パズルを解いているような感覚になれるかもしれません。

AIと正規表現の最新事情


Hirokiのアイコン
【Hiroki】 最近はAIがコードを書いてくれることも多いですよね。 正規表現もAIにお任せしていいんでしょうか?


Yukiのアイコン
【Yuki】 ええ、もちろんです。 例えば、Googleの最新モデルである gemini-2.0-flash (※2024年末時点の最新)などは、非常に精度の高い正規表現を生成してくれます。 PythonでAIを使うなら、新しいライブラリの google-genai を使って、「このテキストから日付を抽出する正規表現を書いて」と頼むのも一つの手ですね。


Hirokiのアイコン
【Hiroki】 AIにパターンを作ってもらって、それを re.findall() で使うっていう組み合わせですね!


Yukiのアイコン
【Yuki】 はい、それが今の時代の賢いプログラミングの進め方だと思います。 ただ、AIが作ったパターンが意図しないもの(例えば、不要な部分まで抜き出してしまうなど)である可能性もあります。 だからこそ、今回学んだ re.findall() の仕組みをHirokiくんが理解していることは、AIの出力をチェックするためにも、とても価値があることなんですよ。

まとめ


Hirokiのアイコン
【Hiroki】 今日はありがとうございました、Yukiさん! re.findall() について、すごくよくわかりました。

  1. re.findall() は一致するすべてをリストで返す。
  2. グループ化 () を使うと、その部分だけが抽出される。
  3. 大文字小文字を無視するには re.IGNORECASE フラグを使う。
  4. 大きなデータには re.finditer() を検討する。

これで合っていますか?


Yukiのアイコン
【Yuki】 完璧です!Hirokiくん、飲み込みが早くて助かります。 あ、最後に……正規表現の公式ドキュメントも、いつか目を通してみるといいかもしれません。 より深い使い方が載っていて、新しい発見があると思います。

Python 公式ドキュメント: re.findall()


Yukiのアイコン
【Yuki】 正規表現は、一度身につけると、テキスト処理の作業が何倍も速くなる魔法のような技術です。 もしまた、複雑なパターンに悩んだり、抽出が上手くいかなかったりした時は、いつでも相談してくださいね。 ……わたしも、もっとスマートに教えられるように、夜な夜な練習しておきます。


Hirokiのアイコン
【Hiroki】 心強いです!これからもよろしくお願いします、Yukiさん!


Yukiのアイコン
【Yuki】 はい、こちらこそ。 それでは、今日の講義はこれでおしまいにしましょう。 お疲れ様でした。


情報の根拠・参考リンク - Python documentation: re.findall - Python documentation: Regular Expression HOWTO - google-genai PyPI (Latest SDK info)



< Pillow
コラム一覧に戻る
sprintf()スタイル >

この記事では基礎を解説しましたが、実務においては「もっと複雑なデータを扱いたい」「独自のシステムに組み込みたい」といった、個別の課題に直面することも多いはずです。

「自分で書く時間は最小限に抑え、プロの品質でツールを完成させたい」という方は、ぜひ一度ご相談ください。

「教わる」だけでなく「形にする」パートナーとして、フリーランスエンジニアのmei_13が最短ルートでの解決をサポートします。

➡ ココナラで制作・相談を依頼する(見積もり無料)


初心者から始められるPythonレッスン

プログラミング未経験者・初心者歓迎!
月額4,000円で質問し放題!!
● 完全オンライン
● 翌日までには必ず返信
● 挫折しない独自の学習メソッド
● 圧倒的高評価!!
テキストベースで時間を選ばない
● 高品質なサンプルコード
詳細はこちら
興味がある方はまず質問だけでもどうぞ!



AIアシスタント Yuki