創用 CC 授權條款
除非另有註明,本網站的著作Fygul Hether製作,以創用CC 姓名標示-非商業性-禁止改作 4.0 國際 授權條款釋出。

2017年9月24日 星期日

Python csv模組

本文簡單展示Python內建準標函式庫中csv模組的用法。

以下範例採用一個從臺灣證券交易所網站下載的實際csv資料檔,從中取得當日ETF交易資料,把需要的欄位資料另存到一個csv檔案。即簡單的csv讀取與寫入的作法。

範例說明

先自行從臺灣證券交易所網站下載csv檔,過程如下:
  • 交易資訊 > 盤後資訊 > 每日收盤行情
  • 資料日期為上週五:106-9-22
  • 類別:全部(不含權證、牛熊證、可展延牛熊證)
  • 按查詢後,選擇:CSV 下載
  • 下載存檔
先談一下本csv檔案中的內容,以及取得、處理與儲存最終資料的大致過程。

本文主題在csv資料,為避免主題失焦,這裡不解釋金融術語,只交代資料中的有關項目。這csv檔中包含了臺灣上市公司所發行證券以及各種ETF的當日收盤資料,本文只要其中的ETF資料。以這原始資料檔案來看,其中有1172行,其中所要擷取的ETF資料有100筆。

原檔案中的資料欄位很多,這裡只需要以下欄位:
  • 證券代號
  • 證券名稱
  • 開盤價
  • 最高價
  • 最低價
  • 收盤價
  • 成交金額
範例代碼將從csv檔中逐行找出ETF資料,取得上述欄位資料,最後存到另一個csv檔。大致上就是分成讀取、寫入兩類工作。必要的細部工作說明,將在以下補充。

讀取

原始csv檔案中資料的構成方式有幾個重點,讀取時要了解:
  • 檔案裡有空行,做為資料區段的區隔
  • 每列資料之間的欄位以半形逗點做分隔,並以 " 符號括住資料
  • ETF資料列開頭是個=符號,並以 " 括住證券代號
  • 成交金額中採用了半形逗點標示千位數
由於最終要儲存的資料,只需要以半形逗點做分隔,不採用"括號,成交金額中也不要半形逗點,所以讀取後,要做些處理。

以下是讀取工作的代碼,與部分註釋文字,在代碼之後再補上必要的說明。

import csv

# 來源與輸出檔
source = 'MI_INDEX.csv'
output = 'output.csv'

# 用來存放ETF列表。先放入表頭。
etf_lst = [['證券代號', '證券名稱', '開盤價', '最高價', '最低價', '收盤價', '成交金額']]

# 讀取資料,把其中的ETF資料調整後存入etf_lst中
with open(source, encoding='cp950', newline='') as rf:
    csv_reader = csv.reader(rf, delimiter=',', quotechar='"')
    for row in csv_reader:
        if row and row[0][0] == '=':
            code = row[0].replace('=', '').replace('"', '')
            etf_lst.append([code, row[1], row[5], row[6], row[7], row[8], row[4].replace(',', '')])
來源檔採cp950編碼,讀取時最好指定正確。

newline=''若沒指定,資料欄括號裡的斷行無法正確解譯。本範例沒這類資料,所以可省略。

csv.reader會回傳一個reader object。兩個參數中delimiter指的是CSV中的分隔符號,quotechar則是做為括號的字元。這裡這兩個的設置都是預設值,所以可以省略不寫,這裡是為了展示才寫出來。

reader object會迭代(iterate)rf中的每一列,所以可以逐列處理其中的資料。

每列中所要的資料欄位,其位置若以0做第一欄,則分別在0, 1, 5, 6, 7, 8, 4欄。這點請對照前述所需欄位。

rf中的來源資料中有空行,且如前所述,ETF代號的第一字元是=,所以用

if row and row[0][0] == '=':
來篩選所要的ETF資料。取得所要的一列資料後:
  • ETF代號要去除=與"符號
  • 成交金額要去除,符號
處理好的資料,逐一存到etf_lst中。

上述代碼主要純粹為展示而寫,並不是最佳的方式,像ETF代碼的處理那行可改成:

code = row[0][2:-1]
但這麼寫,表面上並不易看出“去除=與"符號”這動作,而且也不是本文主題的重點。

寫入儲存

再來把etf_lst寫入output.csv檔中:

# etf_lst內容儲存到輸出檔
with open(output, 'w', encoding='cp950', newline='') as wf:
    csv_writer = csv.writer(wf, delimiter=',', quoting=csv.QUOTE_NONE)
    csv_writer.writerows(etf_lst)
這裡不需要存括號字元,所以設置quoting=csv.QUOTE_NONE,這個是csv模組所定義的常數。

因為資料量不大,所以一次寫入,用writerows()把etf_lst串列寫入。

若資料較大,最好採逐行寫入的方式(writerow()),像:

    for row in etf_lst:
        csv_writer.writerow(row)
或者不透過etf_lst,而是採讀一行就寫一行的方式。

以上是csv模組的簡單使用,完整說明請見Python文件csv模組說明。參數說明可見其中的Dialects and Formatting Parameters。

完整的範例代碼重新整理如下,全部範例代碼、資料與輸出的檔案打包在

import csv

# 來源與輸出檔
source = 'MI_INDEX.csv'
output = 'output.csv'

# 用來存放ETF列表。先放入表頭。
etf_lst = [['證券代號', '證券名稱', '開盤價', '最高價', '最低價', '收盤價', '成交金額']]

# 讀取資料,把其中的ETF資料調整後存入etf_lst中
with open(source, encoding='cp950', newline='') as rf:
    csv_reader = csv.reader(rf, delimiter=',', quotechar='"')
    for row in csv_reader:
        if row and row[0][0] == '=':
            code = row[0].replace('=', '').replace('"', '')
            etf_lst.append([code, row[1], row[5], row[6], row[7], row[8], row[4].replace(',', '')])

# etf_lst內容儲存到輸出檔
with open(output, 'w', encoding='cp950', newline='') as wf:
    csv_writer = csv.writer(wf, delimiter=',', quoting=csv.QUOTE_NONE)
    csv_writer.writerows(etf_lst)

沒有留言:

張貼留言