【 Insight Masking API 徹底活用 】openpyxl で Excel ファイルを手軽にマスキング！

近年、企業や組織における情報漏洩のリスクは増大の一途を辿っています。特に、日常業務で頻繁に利用されるExcelファイルには、顧客情報や売上データなど、多くの機密情報が含まれている可能性があります。これらの情報を適切に保護・利用するためにマスキング処理が必要となるケースも多々あると思います。

そこで今回は、**Insight Masking Web API** と Python の **openpyxl** を組み合わせることで、Excel ファイルに含まれる機密情報を手軽にマスキングする方法をご紹介します。「なんだか難しそう…」と感じた方もご安心ください。この記事では、具体的なコード例を交えながら説明していきます。

## なぜ Insight Masking Web API を使うのか？

「Excel のマスキングなら、手作業で頑張って機微な情報を削除していけばいいのでは？」と思われる方もいるかもしれません。しかし、Insight Masking Web API を利用することには、以下のようなメリットがあります。

  * **高精度なマスキング:** テキストの内容を解析し、個人情報や機密情報を高精度に検出してマスキングできます。
  * **多様なデータ形式に対応:** Excel ファイルだけでなく、様々なデータ形式に対応しています。
  * **効率的な処理:** 大量のファイルや複雑なデータに対しても、プログラムで自動的に処理できるため、効率的です。

## 準備するもの

始める前に、以下の準備が必要です。

1.  **Python が利用可能な環境**
2.  **Insight Masking Web API の URL と API キー:** Insight Masking Web API を利用するためには、Insight Masking の Web コンソールからあらかじめプレーンテキストタイプの API を作成しておく必要があります。詳細は以下のサイトなどを参照ください。

https://qiita.com/takumats/items/10024720668022b48af7

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3480312/920edbd1-3892-42b3-a54e-b4f1f4c20e78.png)

ではさっそくやってみましょう。

## 実行環境の構築（venv）

Pythonのプロジェクトでは、仮想環境を利用することが推奨されます。venvを使ってこのプロジェクト専用の仮想環境を作成しましょう。

1.  **仮想環境の作成:** ターミナルまたはコマンドプロンプトを開き、プロジェクトのディレクトリに移動した後、以下のコマンドを実行します。
    ```bash
    python3 -m venv venv
    ```
2.  **仮想環境のアクティベート:** 作成した仮想環境を有効にします。
    ```bash
    venv/bin/activate
    ```
    アクティベートされると、ターミナルのプロンプトの先頭に `(venv)` と表示されます。

### 必要なライブラリのインストール（requirements.txt）

このプロジェクトで必要なライブラリは `openpyxl`, `requests`, `python-dotenv` です。以下の内容で `requirements.txt` ファイルをプロジェクトのルートディレクトリに作成してください。

```
openpyxl
requests
python-dotenv
```

作成後、アクティベートされた仮想環境内で以下のコマンドを実行し、必要なライブラリをインストールします。

```bash
pip3 install -r requirements.txt
```

### Insight Masking Web API の基本的な使い方

Insight Masking Web API (プレーンテキストタイプ) は、テキストデータを受け取り、その中に含まれる機密情報を検出してマスキングした結果を返してくれるサービスです。基本的なリクエストとレスポンスの形式は以下の通りです。**API キーは、HTTP リクエストのヘッダーに `X-Insight-Masking-API-KEY` として含めて送信します。**

  * **リクエスト:** マスキングしたいテキストデータを base64 エンコードしたものと、**API キーを HTTP ヘッダーに `X-Insight-Masking-API-KEY` を設定**して HTTP リクエストを送信します。
  * **レスポンス:** API は、base64 エンコードされたマスキング後のテキストデータを返します。

## Python で Excel ファイルのシートを指定してマスキングする具体的な手順

それでは、実際に Python と openpyxl を使って Excel ファイルを読み込み、Insight Masking Web API でマスキング処理を行い、結果を Excel ファイルに書き込む手順を見ていきましょう。今回は、**コマンドラインオプションでシート名を指定**するか、指定がない場合は**最初のシート**を対象とすることとします。

### コマンドライン引数の処理

まず、`argparse` モジュールを使ってコマンドライン引数を処理します。

```python
import openpyxl
import requests
import json
import os
from dotenv import load_dotenv
import argparse
import urllib.parse
import base64

load_dotenv()

# コマンドライン引数の設定
parser = argparse.ArgumentParser(description='Excelファイルの指定シートをInsight Masking APIでマスキングします。')
parser.add_argument('excel_file', help='マスキング対象のExcelファイルパス')
parser.add_argument('-s', '--sheet_name', help='処理対象のシート名（省略時は最初のシート）')
args = parser.parse_args()

excel_file_path = args.excel_file
sheet_name = args.sheet_name
```

ここでは、必須の引数としてExcelファイルのパス (`excel_file`) を、オプション引数としてシート名 (`-s` または `--sheet_name`) を定義しています。

### Insight Masking Web API との連携

次に、各セルの値を Insight Masking Web API に送信してマスキング処理を行います。`requests`ライブラリを利用すると便利です。API のエンドポイントと API キーは環境変数から読み込みます。

```python
# Insight Masking Web APIのエンドポイント (環境変数 INSIGHT_MASKING_API_ENDPOINT から取得)
api_url = os.getenv('INSIGHT_MASKING_API_ENDPOINT')
if not api_url:
    print("エラー: 環境変数 INSIGHT_MASKING_API_ENDPOINT が設定されていません。")
    exit()

# APIキー (環境変数 INSIGHT_MASKING_API_KEY から取得)
api_key = os.getenv('INSIGHT_MASKING_API_KEY')
if not api_key:
    print("エラー: 環境変数 INSIGHT_MASKING_API_KEY が設定されていません。")
    exit()

headers = {
    'X-Insight-Masking-API-KEY': api_key
}

# マスキング処理を行う関数
def mask_text(text):
    plain_text = base64.b64encode((urllib.parse.unquote(urllib.parse.quote(text)) + '\n').encode())
    try:
        response = requests.post(api_url, headers=headers, data=plain_text)
        response.raise_for_status()  # エラーレスポンスの場合に例外を発生させる
        result = urllib.parse.unquote(urllib.parse.quote(base64.b64decode(response.text)))
        return result.rstrip('\n')
    except requests.exceptions.RequestException as e:
        print(f"APIリクエストエラー: {e}")
        return text
    except json.JSONDecodeError as e:
        print(f"JSONデコードエラー: {e}")
        return text
```

API の URL と、API キーは以下のように `.env` ファイルに記載しておくと便利です。

```bash
cat .env
INSIGHT_MASKING_API_ENDPOINT="https://xxxx.xx.xx/webapi/xxxxxxxxx/xxxxxxxxx/plain"
INSIGHT_MASKING_API_KEY="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxx"
```


### Excel ファイルの読み込みとシートの選択

Excel ファイルを読み込み、コマンドライン引数で指定されたシート、または最初のシートを取得します。その後、全カラムの文字列型のセルに対してマスキング処理を行い、結果を保存します。

```python
# Excelブックを読み込む
try:
    workbook = openpyxl.load_workbook(excel_file_path)
    if sheet_name:
        try:
            sheet = workbook[sheet_name]
        except KeyError:
            print(f"エラー: シート '{sheet_name}' は存在しません。")
            exit()
    else:
        sheet = workbook.active  # 最初のシートを取得
        print(f"シート名が指定されなかったため、最初のシート '{sheet.title}' を処理します。")

    # 各行のすべてのセルを処理
    for row in sheet.iter_rows(min_row=1):
        for cell in row:
            original_value = cell.value
            if original_value is not None and isinstance(original_value, str):
                masked_value = mask_text(original_value)
                cell.value = masked_value

    # マスキング後のExcelファイルを保存
    output_file_path = f'masked_{os.path.basename(excel_file_path)}'
    workbook.save(output_file_path)
    print(f"マスキング処理完了。結果は {output_file_path} に保存されました。")

except FileNotFoundError:
    print(f"エラー: ファイル '{excel_file_path}' が見つかりません。")
except Exception as e:
    print(f"予期せぬエラーが発生しました: {e}")
```

## 実行方法

このスクリプトは、コマンドラインから以下のように実行します。

```bash
python your_script_name.py your_excel_file.xlsx -s Sheet1
```

または、最初のシートを処理する場合は `-s` オプションを省略します。

```bash
python your_script_name.py your_excel_file.xlsx
```

では実際に実行してみます。以下のように、`masked_入力ファイル名` のファイルがマスキング結果として生成されました。

```bash
python3 test.py test.xlsx
シート名が指定されなかったため、最初のシート 'Sheet1' を処理します。
マスキング処理完了。結果は masked_test.xlsx に保存されました。
```

入力に使用した Excel ファイルと出力結果を以下に掲載します。なおマスキングの設定等により、実行結果は異なることがあります。

入力：
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3480312/27077633-e1bb-402b-91ad-5174a1037a38.png)

出力：
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3480312/0640990a-3016-44a0-b029-410da6ce8385.png)

簡単でしたね！

## まとめ

この記事では、`openpyxl` ライブラリと Insight Masking Web API を連携させて Excel ファイルに含まれる機密情報を効率的にマスキングする方法をご紹介しました。API を活用することで、面倒なマスキング処理を自動化できることが確認できたのではないでしょうか？
まだ Insight Masking をご利用いただいたことがない方で、ご興味をお持ちの方は https://www.insight-tec.com/products/masking/#f から是非お問合せください。

## 参考情報

https://qiita.com/takumats/items/10024720668022b48af7

https://openpyxl.readthedocs.io/en/stable/

:::note info
ソースコードの生成、テスト用 Excel ファイルの用意、文章の校正などに Gemini for Google Workspace を活用しています
:::

## ソースコード全体

以下に、この記事で解説したPythonスクリプトの全体像を記載します。

```python
import openpyxl
import requests
import json
import os
from dotenv import load_dotenv
import argparse
import urllib.parse
import base64

load_dotenv()

# コマンドライン引数の設定
parser = argparse.ArgumentParser(description='Excelファイルの指定シートをInsight Masking APIでマスキングします。')
parser.add_argument('excel_file', help='マスキング対象のExcelファイルパス')
parser.add_argument('-s', '--sheet_name', help='処理対象のシート名（省略時は最初のシート）')
args = parser.parse_args()

excel_file_path = args.excel_file
sheet_name = args.sheet_name

# Insight Masking Web APIのエンドポイント (環境変数 INSIGHT_MASKING_API_ENDPOINT から取得)
api_url = os.getenv('INSIGHT_MASKING_API_ENDPOINT')
if not api_url:
    print("エラー: 環境変数 INSIGHT_MASKING_API_ENDPOINT が設定されていません。")
    exit()

# APIキー (環境変数 INSIGHT_MASKING_API_KEY から取得)
api_key = os.getenv('INSIGHT_MASKING_API_KEY')
if not api_key:
    print("エラー: 環境変数 INSIGHT_MASKING_API_KEY が設定されていません。")
    exit()

headers = {
    'X-Insight-Masking-API-KEY': api_key
}

# マスキング処理を行う関数
def mask_text(text):
    plain_text = base64.b64encode((urllib.parse.unquote(urllib.parse.quote(text)) + '\n').encode())
    try:
        response = requests.post(api_url, headers=headers, data=plain_text)
        response.raise_for_status()  # エラーレスポンスの場合に例外を発生させる
        result = urllib.parse.unquote(urllib.parse.quote(base64.b64decode(response.text)))
        return result.rstrip('\n')
    except requests.exceptions.RequestException as e:
        print(f"APIリクエストエラー: {e}")
        return text
    except json.JSONDecodeError as e:
        print(f"JSONデコードエラー: {e}")
        return text

# Excelブックを読み込む
try:
    workbook = openpyxl.load_workbook(excel_file_path)
    if sheet_name:
        try:
            sheet = workbook[sheet_name]
        except KeyError:
            print(f"エラー: シート '{sheet_name}' は存在しません。")
            exit()
    else:
        sheet = workbook.active  # 最初のシートを取得
        print(f"シート名が指定されなかったため、最初のシート '{sheet.title}' を処理します。")

    # 各行のすべてのセルを処理
    for row in sheet.iter_rows(min_row=1):
        for cell in row:
            original_value = cell.value
            if original_value is not None and isinstance(original_value, str):
                masked_value = mask_text(original_value)
                cell.value = masked_value

    # マスキング後のExcelファイルを保存
    output_file_path = f'masked_{os.path.basename(excel_file_path)}'
    workbook.save(output_file_path)
    print(f"マスキング処理完了。結果は {output_file_path} に保存されました。")

except FileNotFoundError:
    print(f"エラー: ファイル '{excel_file_path}' が見つかりません。")
except Exception as e:
    print(f"予期せぬエラーが発生しました: {e}")
```

**requirements.txt**

```
openpyxl
requests
python-dotenv
```