2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

pythonで複数のExcelファイルからの文字列検索

Last updated at Posted at 2024-10-12

テキストエディタのGrep機能感覚で、複数Excelファイルから手軽に文字列検索するツールを作成したので、その実装方法をご紹介します。

背景

  • 複数Excel検索の一括検索をするシーンが多い
    • 仕様の調査
    • 設計書のレビュー
    • 過去議事録の読み返し
  • 多くのサンプルソースが公開されているが、自分好みのものを特定、選別するのが大変
  • AIを使って自分で好みのツールを作った方が速い

スケジュールがタイトで短時間で多くのレビューをこなさなくてはならず困っていました:joy:
ウォーターフォール開発はレビュー品質を高めることで防げる手戻りや障害が沢山あるのです:point_up:

ツールの要件

  • 検索対象Excelファイル数が多くても、レスポンスが速い(重要要件)
  • 検索対象指定フォルダを複数指定できる
  • 検索対象指定したフォルダのサブフォルダも検索対象に含める
  • 検索結果をTxtファイルに保存できる
    (同じキーワードで検索しなくて済むよう、TxtファイルをRenameして保存する意図)

:writing_hand_tone1:動作環境はWindows11、Python 3.12、Excel2021

ツールの実装

機能検討

  • 検索対象パスは外部ファイル「Search.txt」から読ませる
Search.txt(記入例)
# 検索対象パス
C:\Users\USER\hogePj\10.管理
C:\Users\USER\hogePj\20.要件定義
C:\Users\USER\hogePj\30.基本設計

# 検索文字列
顧客ID
  • 処理結果は「Result.txt」に出力
    SakuraエディタなどのGrep機能に近いイメージでOK。

  • 「Search.txt」、「Result.txt」ともに、pythonプログラムのある同フォルダに配置

実装準備

ライブラリOpenPyXl1をインストールしよう。

下記コマンドを実行すればOK。
pip install pandas openpyxl

OpenPyXL公式リファレンス

実装方法

コーディングはChatGPTに任せて、要件の検討と意思決定に注力。

  1. ChatGPTに要件を伝えて、「pythonでソース書いて下さい」と入力
  2. ChatGPTが出力したソースを、VSCodeに貼り付けて実行してみる
  3. 改良要件を「〇〇の部分を✕✕するように修正してください」と入力
  4. 自分の意図通りの動作になるまで、2~3を繰り返す

:point_up:実行エラーも「xx行目の変数hogehogeでint型変換エラーが発生しています。明示的にxxするよう修正して下さい」のように入力すればOK。

完成したソース

ExecelGrep.py
import os
import pandas as pd

def search_in_excel(folder_paths, search_string):
    results = []
    # 指定された各フォルダを処理
    for folder_path in folder_paths:
        # フォルダ内の全Excelファイルを取得(サブフォルダ含む)
        for dirpath, _, filenames in os.walk(folder_path):
            for filename in filenames:
                # "~$"で始まるファイルを除外
                if filename.startswith("~$"):
                    continue
                if filename.endswith(('.xlsx', '.xls', '.xlsm')):
                    file_path = os.path.join(dirpath, filename)
                    try:
                        # Excelファイルを全てのシートを読み込む
                        df = pd.read_excel(file_path, sheet_name=None, header=None)
                        for sheet_name, sheet_data in df.items():
                            # 各シート内のセルを検索
                            for row_index in range(sheet_data.shape[0]):  # 行をループ
                                for col_index in range(sheet_data.shape[1]):  # 列をループ
                                    cell = sheet_data.iloc[row_index, col_index]  # 行と列のインデックスでセルを取得
                                    # 検索対象の文字列が含まれているか確認
                                    if isinstance(cell, str) and search_string in cell:
                                        results.append((file_path, sheet_name, (row_index, col_index), cell))
                    except Exception as e:
                        print(f"Error reading {file_path}: {e}")
    return results

def read_search_config(file_path):
    folder_paths = []
    search_string = ""
    with open(file_path, 'r', encoding='utf-8') as f:
        current_section = None
        for line in f:
            line = line.strip()
            if not line:
                continue
            if line == "# 検索対象パス":
                current_section = "folder_paths"
            elif line == "# 検索文字列":
                current_section = "search_string"
            elif current_section == "folder_paths":
                folder_paths.append(line)
            elif current_section == "search_string":
                search_string = line
    return folder_paths, search_string

# Pythonスクリプトが存在するディレクトリを取得
script_dir = os.path.dirname(os.path.abspath(__file__))

# Search.txtから設定を読み込む
search_config_path = os.path.join(script_dir, 'Search.txt')
folder_paths, search_string = read_search_config(search_config_path)

# 検索を実行
results = search_in_excel(folder_paths, search_string)

# 結果をファイルに出力
output_file_path = os.path.join(script_dir, 'result.txt')
with open(output_file_path, 'w', encoding='utf-8') as file:
    file.write(f"search_keyword: {search_string}\n")
    for result in results:
        file_path, sheet_name, (row_index, col_index), cell_value = result
        file.write(f"Found in {file_path} -> {sheet_name} (Row: {row_index+1}, Col: {col_index+1}) - Value: {cell_value}\n")

print(f"Results have been written to {output_file_path}")

ChatGPT様、ありがとうございます。コメントも親切で、解読性が高くデバッグしやすい。

実行の様子

1.Search.txtを用意
2.pythonを実行
3.result.txtが生成される
gqp9mKPBj315yuczstsC1728721753-1728722051.gif
4.実際のExcelの中身と、検索Hitしたセル位置の出力内夜が一致していることも確認済み。

以上で、簡単な動作確認が完了。

ファイル数を増やして、処理速度性能を試した

  • Excelファイルが40~100個程度あるフォルダを4つ指定
  • 検索実行の結果、100か所近くHit

実行時間は2分程度:v:

さいごに

  • pythonのExcel操作はシンプル、VBAで同ツールを作るより処理速度は高い
  • 普段はコーディングしておらず、pythonの細かい文法を忘れてる私でも爆速でツールが完成した
  • 実行エラーやこちらの意図と違う動作はあるため、ソースが読めて原因の行や理由を特定する勘所は必要(そこさえできれば、ChatGPTに修正を依頼するだけ)
  • 皆さんもChatGPTで自分好みのツールを作って、生産性アップにチャレンジしてみよう

番外編(おまけの豆知識)

Win+Shift+Sだと右クリックメニューを表示したままのスクショが取れない💦
そんな悩みに遭遇したことがある方へ
私も本記事のアニメーションGifを作成するときに一瞬はまって、下記で解決しました:joy:

右クリックメニューを表示した状態でスクリーンショットを取る方法
 Ctrl + PrintScreen

  1. OpenPyXl:PythonでExcelファイルを読み書きするためのライブラリ

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?