csvファイルを読み込んでブラウザに表示する

# 概要・目標
持ち運び用のノートPCにExcelをインストールしていないのでcsvファイルが表形式で照会できない。
スプレッドシートでの代用は可能だが、スプレッドシートで表示するにはネット接続が必要で、無駄なパケット通信をしたくない。
CSVファイルの照会だけならオフラインで十分。
色々とググっているとpythonのpandasライブラリでCSVファイルの読み込みやhtmlの出力もできることがわかった。
pythonの勉強を目的としてcsvファイルを読み込んでブラウザに表示するプログラムを作成する。

# 環境

* Windows10
* python　3.8.1
* VSCode

# やること、やる順

1. pandasをpipでインストールする
1. pythonプログラムを作成する
1. ブラウザで表示する

## pandasをpipでインストールする

VSCodeのターミナルでお決まりのコマンド `pip install pandas` を実行する。
なぜかエラー発生...ソース書く前に想定外のエラーを引くキャラなんで、これぐらいのエラーが発生することはお見通し。

```
（１）
ERROR: Could not install packages due to an EnvironmentError: [WinError 5] アクセスが拒否されました。: 'c:\\program files\\python38\\lib\\site-packages\\pip-19.2.3.dist-info\\entry_points.txt'
Consider using the `--user` option or check the permissions.

（２）
WARNING: You are using pip version 19.2.3, however version 19.3.1 is available.
You should consider upgrading via the 'python -m pip install --upgrade pip' command.
```

（１）･･･アクセスが拒否されましたやpermissionsって出てるからおそらく権限なんでしょうね。
VSCodeのターミナルは諦めて、コマンドプロンプトを管理者で起動してpipコマンドを実行すると正常にインストールできた。

（２）python3.8.1でインストールされたpipのバージョンが古いので警告が出ている。
''で表示されたコマンドを実行し、pipのアップグレードを（１）の実行前に実施した。

## pythonプログラムを作成する

pandasのread_csv()でCSVファイルを読み込み、to_html()でCSVデータを<table>に変換し、htmlに書き込む。
読み込んだCSVファイルで空白がある場合は欠損値として扱かわれNaNに変換されるため、`na_filter=False`で変換しないようにする。
htmlのタイトルにはCSVファイル名を表示する。

```main.py
import os
import pandas as pd
import sys

def main():
    # 引数１からCSVファイルを取得する
    args = sys.argv
    haserror = False
    csvfile = ''

    # 引数なしはエラーとする
    if 1 == len(args):
        print("CSVファイルが指定されていません。")
        haserror = True
    else:
        csvfile = args[1]

        # ファイル名が不正の場合はエラー
        if os.path.exists(csvfile) == False:
            print("指定したファイルは存在しません。")
            haserror = True
        
        # CSVファイルではない場合はエラーとする
        if csvfile.endswith(".csv") == False:
            print("ＣＳＶファイル以外が指定されています。")
            haserror = True
        
    if haserror == True:
        return()
    else:
        print("CSVファイルをhtmlに変換します。")

    # CSVファイル読み込み
    csvdata = pd.read_csv(csvfile, na_filter=False)

    # htmlファイル読み込み
    htmldata = ''
    with open('templete.html',mode='r',encoding='utf-8') as htmlfile:
        htmldata = htmlfile.read()

    # CSVファイルをhtmlに変換
    rpdict = { "filename" : os.path.basename(csvfile), "table" : csvdata.to_html() }
    htmldata = htmldata.format(**rpdict)

    # htmlファイル出力
    with open('csvconverted.html',mode='w',encoding='utf-8') as outputhtml:
        outputhtml.write(htmldata)

if __name__== '__main__':
    main()
```

変換前html

```
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title> {filename} </title>
        <link rel="stylesheet" type="text/css" href="style.css"/>
    </head>
    <body>
        <div class="csvlist">
            {table}
        </div>
    </body>
</html>
```

変換後html

```
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title> test.csv </title>
        <link rel="stylesheet" type="text/css" href="style.css"/>
    </head>
    <body>
        <div class="csvlist">
            <table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>日付</th>
      <th>休日区分</th>
      <th>出勤時刻</th>
      <th>退勤時刻</th>
      <th>労働時間</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>0</th>
      <td>2019/12/1</td>
      <td>法休</td>
      <td>09:00</td>
      <td>13:00</td>
      <td>04:00</td>
    </tr>
    <tr>
      <th>1</th>
      <td>2019/12/2</td>
      <td></td>
      <td>09:00</td>
      <td>21:30</td>
      <td>11:30</td>
    </tr>
    <tr>
      <th>2</th>
      <td>2019/12/3</td>
      <td></td>
      <td>09:00</td>
      <td>21:30</td>
      <td>11:30</td>
    </tr>
    <tr>
      <th>3</th>
      <td>2019/12/4</td>
      <td></td>
      <td>09:00</td>
      <td>21:00</td>
      <td>11:00</td>
    </tr>
    <tr>
      <th>4</th>
      <td>2019/12/5</td>
      <td></td>
      <td>09:00</td>
      <td>20:00</td>
      <td>10:00</td>
    </tr>
    <tr>
      <th>5</th>
      <td>2019/12/6</td>
      <td></td>
      <td>09:00</td>
      <td>20:00</td>
      <td>10:00</td>
    </tr>
    <tr>
      <th>6</th>
      <td>2019/12/7</td>
      <td>公休</td>
      <td></td>
      <td></td>
      <td></td>
    </tr>
    <tr>
      <th>7</th>
      <td>2019/12/8</td>
      <td>法休</td>
      <td></td>
      <td></td>
      <td></td>
    </tr>
  </tbody>
</table>
        </div>
    </body>
</html>
```

### ふりかえり

* htmlファイルに出力されたCSVファイルの内容が文字化けしていた。open()でencoding指定していなかったため、デフォルト(shift-jis)の文字コードで処理されてしまう。encoding='utf-8'を指定して文字化けしないようにした。
* templete.htmlにてファイル名とcsv内容を出力するフィールドに対してformat()実行時にKeyErrorが発生した。当初はformat(key="")を２か所コーディングしていたが、１つずつformatすると対象文字列内から指定したkey項目に置換されるため、置換されなかったkey項目が存在してることをKeyErrorになっていた。formatの使い方は難しい･･･置換対象はkey-value形式のディクショナリ形式で宣言し、formatにはディクショナリを指定して置換すると正しく置換できた。
* read_csv()での実行結果が２次元配列型で参照できるため、扱いやすくてびっくりした。

製作物はこちら
https://github.com/otowmoyarng/FreeStudyRepo/tree/master/20191228-pythoncsvviewer