Pythonでひとつのセル内の文字列に複数の色を指定したExcelファイルを作りたい

## 実現したいこと

以下のような Excel ファイルを作りたい。[^1]

[^1]: ここでは抽象的な例にしているが、具体例としては自然言語処理で生成した要約と人間の要約を横に並べて一致している部分を緑で、異っている部分を赤で表示することなどが考えられる。 (cf. [ROUGEを訪ねて三千里:より良い要約の評価を求めて - Qiita](https://qiita.com/icoxfog417/items/65faecbbe27d3c53d212)) 
その場合は [difflib.HtmlDiff](https://docs.python.org/ja/3/library/difflib.html#difflib.HtmlDiff) を使うと捗る。 (cf. [Pythonでファイルの差分を抽出し結果をhtmlとして出力する｜ゆゆうブログ](https://yuyuublog.com/pythondiff/))

![Book2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/63995/c74719b8-d35e-2bde-00c2-56b9ae72288f.png)

これは Python で Excel ファイルを扱う定番ライブラリである [OpenPyXL](https://openpyxl.readthedocs.io/en/stable/) では~~実現できない~~ [^2]が、 [XlsxWriter](https://xlsxwriter.readthedocs.io/) で実現できるらしい。詳しくは以下の記事を参照。

https://qiita.com/tekunikaruza_jp/items/53a5b482b20a85f9c44d

https://qiita.com/alicial/items/9db75fc457dfd33088e3

この方法では XlsxWriter の API を使うことになり、記述が複雑になる。そこで HTMLテーブル を経由する方法を考えた。

[^2]: [v3.1で導入された]((https://foss.heptapod.net/openpyxl/openpyxl/-/merge_requests/409))リッチテキストを使うことで実現できる。[Working with Rich Text — openpyxl 3.1.4 documentation](https://openpyxl.readthedocs.io/en/3.1/rich_text.html)

## フォーマット付き HTMLテーブル の作成

### テーブルの準備

[Pandas](https://pandas.pydata.org/docs/) を使ってテーブルを準備する。ここで文字列中で赤色にしたい部分を `_RED_START_`, `_END_` で、青色にしたい部分を `_BLUE_START_`, `_END_` で囲んだ。本当はここに HTML の `<font>` タグを書きたいが、この時点で書くと [`pandas.DataFrame.to_html()`](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.to_html.html) によってエスケープされてしまうので、あとで置換するようにする。

```python
import pandas as pd
dic = {
    '国旗': ['日本', 'アメリカ'],
    '色': [
          '白、_RED_START_紅_END_',
          '白、_BLUE_START_青_END_、_RED_START_赤_END_'
        ]
}
df = pd.DataFrame(dic)
```

![pandas_font_color_ipynb.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/63995/607c5f9e-625f-1901-8a08-7554b284f03f.png)

### HTMLテーブルに変換

`to_html()` で変換する。 `to_html()` は引数に `output.html` などを指定すると HTML ファイルに出力されるが、指定しない場合は文字列として返す。

```python
html = df.to_html()
```

文字列 `html` は以下のような中身になっている。

```html
<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>国旗</th>
      <th>色</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>0</th>
      <td>日本</td>
      <td>白、_RED_START_紅_END_</td>
    </tr>
    <tr>
      <th>1</th>
      <td>アメリカ</td>
      <td>白、_BLUE_START_青_END_、_RED_START_赤_END_</td>
    </tr>
  </tbody>
</table>
```

### font タグを挿入

あとは `_RED_START_` などを `<font>` タグで置き換える。

```python
html = html.replace('_RED_START_', '<font color="red">').replace('_BLUE_START_', '<font color="blue">').replace('_END_', '</font>')
```

`html` の中身は以下のようになった。

```html
<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>国旗</th>
      <th>色</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>0</th>
      <td>日本</td>
      <td>白、<font color="red">紅</font></td>
    </tr>
    <tr>
      <th>1</th>
      <td>アメリカ</td>
      <td>白、<font color="blue">青</font>、<font color="red">赤</font></td>
    </tr>
  </tbody>
</table>
```

### エクセルにコピペ

HTML を表示すると以下のようになっているはずなので、これをコピーして Excel に貼りつけることで欲しかった Excel ファイルが得られる。

![pandas_font_color_ipynb.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/63995/744ae26a-736c-b370-4844-c4183af16f15.png)

## Pros & Cons

この方法の利点は HTML タグを使うので新たに XlsxWriter の使い方を覚える必要がないこと、 XlsxWriter で `'A1'` などのわずらわしいセル指定を行う必要がないこと。
一方で欠点はコピペが手動であること、大きいファイルになるとコピペに失敗する可能性があることである。HTML ファイルから Excel ファイルを生成する方法があれば捗るかもしれない。

## おわりに

今回行った処理は[この Jupyter Notebook](https://nbviewer.org/gist/pn11/7be89fdccee07cabc371ff587d7ade70) で確認できる。

各国旗の色は以下の記事を参考にしたが、紅色は簡単のため HTML 上では赤色で指定した。

https://ja.wikipedia.org/wiki/%E6%97%A5%E6%9C%AC%E3%81%AE%E5%9B%BD%E6%97%97

https://ja.wikipedia.org/wiki/%E3%82%A2%E3%83%A1%E3%83%AA%E3%82%AB%E5%90%88%E8%A1%86%E5%9B%BD%E3%81%AE%E5%9B%BD%E6%97%97