PythonでCSVの列を取得してテキストファイルに出力する

## はじめに
お仕事で「問い合わせのやりとりが完了したらその関連メールの本文をエクセル台帳に貼り付けてください」というちょっと面倒な作業[^1]があったので、なんとか楽できないかなと思いPythonで書いてみました。
[^1]: 悲しいけどコレ、みんな知ってる大手SI企業なのよね・・・

当初はいちいちOutlookで検索してコピペしていましたが、元々現場でWebデータベースシステムを使っていたので、下記作業手順にしました。

1. メールがきたら都度Webデータベースに本文をコピペして登録する
1. その案件が終わったらCSVで吐き出す（ダウンロードフォルダに保存される）
1. メール内容以外も出力されるので、Pythonでいい感じに整形してテキストファイルに出力する
1. テキストファイルからエクセル台帳のセルにコピペする

手順3についてPythonによる汎用的なコードを解説・公開します。
同じ辛みを味わっているSIerの方々に届けばいいなと思います。[^2]
[^2]: Qiitaがブロックされているかもしれませんが

## Pythonのお仕事

手順3でやっているのは下記の通りです

1. ダウンロードフォルダからCSVを開く
1. CSVから特定の列を抽出する
1. 抽出データを整形する
1. テキストファイルとして書き出す

条件は以下となります。

OS:Windows7(64bit)[^3]
Python:3.7.1
[^3]: 書いてて自分がビックリしました


インストール方法は割愛します。

## テスト用CSV
[疑似個人情報データ生成サービス](https://hogehoge.tk/personal/)を使ってダミーデータを生成しました。
下記データを用いて解説します。
試す場合は下記test.csvをダウンロードフォルダに格納してください。
また、他のCSVファイルが含まれているとそっちが開く可能性があるのでご注意ください。

```test.csv
連番,氏名,氏名（カタカナ）,性別,電話番号,メールアドレス,生年月日,年齢,出身地,血液型
1,吉崎二三男,ヨシザキフミオ,男,077688707,fumioyoshizaki@iokt.ch,1960/01/05,59,佐賀県,B
2,中沢美南,ナカザワミナミ,女,0852980024,minami800@yebxscfg.sxg,1962/03/17,57,宮城県,O
3,立花利子,タチバナトシコ,女,0985454521,toshiko03675@swixzb.dyv,1991/03/13,28,愛知県,A
4,古屋希実,フルヤノゾミ,女,0864800246,nozomi440@jlngsfhfh.gbp.coe,1994/05/09,24,長野県,B
5,橋本明聖,ハシモトアキセ,女,0479219733,akise_hashimoto@ncozpxp.fo,1997/06/10,21,徳島県,A
6,安井涼香,ヤスイスズカ,女,015642892,suzukayasui@fnthlfuu.krsbh.fh,1987/06/25,31,佐賀県,O
7,前原早紀,マエハラサキ,女,0920841709,saki6992@zpnoajzqb.rd,1981/01/18,38,石川県,A
8,中澤愛奈,ナカザワアイナ,女,0776382538,Aina_Nakazawa@xhpvuky.ol,1970/05/15,48,鳥取県,A
9,石沢信長,イシザワノブナガ,男,0550222848,Nobunaga_Ishizawa@skshdowxev.qi,1983/01/26,36,香川県,O
10,川本喜美子,カワモトキミコ,女,0740628874,okawamoto@arwhc.xf.wwi,1993/11/12,25,千葉県,O
```

## 解説
### 1.CSVファイルを開く

```test.py
from pathlib import Path

downloads = Path(str(Path.home()) + r"\Downloads")
csv_list = list(downloads.glob("*.csv"))
csv_file = open(csv_list.pop(),'r')
```
Path:OS毎にいい感じにパスを操作できるのでべた書きしないで済むやつ

`downloads`には、`C:\Users\ユーザ名\Downloads`が入っています。
（`<class 'pathlib.WindowsPath'>`）

`downloads.glob("*.csv")`で、ダウンロードフォルダにあるcsvファイルの一覧を取得しています。

`open(csv_list.pop(),'r')`で最後にリストに追加されたファイルを開いています。

ファイル名が常に同じであれば`open("C:\Users\ユーザ名\Downloads\test.csv",'r')`と、べた書きしても問題ありません。
Webデータベースシステムだとダウンロードするたびに日付や時間がファイル名に入ってくるので、上記のようにしていました。

### 2.CSVから特定の列を抽出する
今回はtest.csvの名前を取得します。


```test.py
import csv

a_list = [] 
for row in csv.reader(csv_file):
    a_list.append(row[1]) #取得したい列番号を指定（0始まり）

# ['氏名', '吉崎二三男', '中沢美南', '立花利子', '古屋希実', '橋本明聖', '安井涼香', '前原早紀', '中澤愛奈', '石沢信長', '川本喜美子']

# 先頭行を削除しておく
del a_list[0]

# ['吉崎二三男', '中沢美南', '立花利子', '古屋希実', '橋本明聖', '安井涼香', '前原早紀', '中澤愛奈', '石沢信長', '川本喜美子']
```

csv:CSVファイルをうまいこと扱ってくれるやつ

１でCSVファイルを開いて`csv_file`に中身が入っているので、1行ずつ取り出して欲しい列だけ配列に追加しています。
`row[1]`を`row[5]`にすればメールアドレスだけ取得できますし、複数配列を用意してあげれば、それぞれの列を取得することができます。２つの列を取得して１つに連結してから配列に追加することもできますね。

この際、先頭に要素名がついていることが多いので、先頭行が不要であれば削除しておきます。

### 3.抽出データを整形する

```test.py

a_text = ""
for a in reversed(a_list):
    a_text += a
    a_text += "\n\n"
```

今回は取得した氏名を逆順にしてみました。
`for a in reversed(a_list):`とすると、配列の最後尾から処理を行ってくれます。

テキストファイルに一気に書き込みたいので、`a_text`に改行も含めて文字を連結しています。
ここは他の列と連結してからソートして・・・など、業務に合わせて適宜データを加工してください。

### 4.テキストファイルとして書き出す

```test.py
desktop = str(Path.home()) + "\Desktop"
a_file = open(Path(desktop +"\Aを出力.txt"),"w")
a_file.writelines(a_text.rstrip())
a_file.close()
```

`open(desktop +"\Aを出力.txt","w")`で書き込めるファイルモードでテキストファイルを開きます。
テキストファイルひとつだけ出すなら下記でも良いでしょう。

```python
desktop = Path(str(Path.home()) + "\Desktop\Aを出力.txt")
a_file = open(desktop,"w")
```

もし存在しなければ新規で作成されます。
存在する場合は新しいファイルで上書きされます。

`a_file.writelines()`のカッコの中に入っている文字列を書き込みます。
`a_file.close()`でファイルを閉じます。[^4]
[^4]: withもありますが、今回は一回開いて閉じるだからこれでいいかなと

これを実行すると、デスクトップに「Aを出力.txt」が作成されます。

```Aを出力.txt
川本喜美子

石沢信長

中澤愛奈

前原早紀

安井涼香

橋本明聖

古屋希実

立花利子

中沢美南

吉崎二三男
```


## コード全文

```test.py
from pathlib import Path
import csv

# CSVファイルのパスを取得
downloads = Path(str(Path.home()) + r"\Downloads")
csv_list = list(downloads.glob("*.csv"))

# CSVファイルを開く
csv_file = open(csv_list.pop(),'r')

# 列を取得
a_list = []
for row in csv.reader(csv_file):
    a_list.append(row[1]) #取得したい列番号を指定（0始まり）

# 先頭行を削除しておく
del a_list[0]

# テキストに書き込むテキストを作成
a_text = ""
for a in reversed(a_list):
    a_text += a
    a_text += "\n\n"

#書き込む
desktop = str(Path.home()) + "\Desktop"
a_file = open(Path(desktop + "\Aを出力.txt"),"w")
a_file.writelines(a_text)
a_file.close()
```

## おわりに
ファイル名やフォルダ名あたりは自分が扱いやすいように修正して使っていただければと思います。
変数名も簡素化してあるので、適宜業務に合わせて変更してください。

より簡単な記載ができる、間違いがある等あればご指摘ください。

## おまけ

以下は、ファイルが無かったり、CSVファイルが暗号化等で読めなかった場合のエラー処理を含んだコードです。
また、拡張子を.pywにしているので、実行時にコマンドプロンプトが開かなくなります。
解説はしませんが参考までに。

<details><summary>エラー処理付きコード</summary><div>

```test.pyw
from pathlib import Path
from datetime import datetime
import csv
from enum import Enum

class ErrorStatus(Enum):
    file_failed = 1 #ファイルが無い時
    file_encrypted = 2 #ファイルが暗号化されて読めない時

# 例外処理
def failed(num):
    # エラーの日時を入れるため取得する
    date = datetime.now().strftime("%Y/%m/%d %H:%M:%S")
    failure_string = ""
    error_status_dictionary = {ErrorStatus.file_failed:"CSVファイルの取得に失敗しました。",ErrorStatus.file_encrypted:"暗号化されて読めない可能性があります。"}
    failure_string = date + "\n" + error_status_dictionary[num]

    a_file = open(Path(desktop + "\Aを出力.txt"),"w")
    a_file.writelines(failure_string)
    a_file.close()

# CSVファイル名とパスを取得
downloads = Path(str(Path.home()) + r"\Downloads")
csv_list = list(downloads.glob("*.csv"))

# CSVファイル名を取得できなかったら終了
if csv_list == []:
    failed(ErrorStatus.file_failed)

# CSVファイルを開く
csv_file = open(csv_list.pop(),'r')

a_list = []

# 読めれば通常処理、読めなければ例外処理
try:
    for row in csv.reader(csv_file):
        a_list.append(row[1]) #CSVの行を指定
except UnicodeDecodeError:
    failed(ErrorStatus.file_encrypted)

# 先頭行を削除しておく
del a_list[0]

a_text = ""
for a in reversed(a_list):
    if a == "":
        continue
    a_text += a
    a_text += "\n\n"

# 出力用にデスクトップへのパスを取得
desktop = str(Path.home()) + "\Desktop"

#書き込む
a_file = open(Path(desktop +"\Aを出力.txt"),"w")
a_file.writelines(a_text.rstrip())
a_file.close()
```
</div></details>