這幾天在用 Python3 研究一個爬蟲,最后一個需求是把爬下來的20+個csv文件整合到一個excel表里的不同sheets。
初版的核心代碼如下:
1 while year <= 2018: 2 csvPath = sys.path[0] + '/result/%d.csv' % year 3 excelPath = sys.path[0] + '/result.xlsx' 4 csvReader = pandas.read_csv(csvPath, encoding='utf_8_sig') 5 excelWriter = pandas.ExcelWriter(excelPath) 6 print("正在將 %d 年的 %d 條數據轉換為 xlsx..." % (year, countThis)) 7 csvReader.to_excel(excelWriter, sheet_name=str(year)) 8 year = year + 1
奇怪的是使用這個方法,每次to_excel之后,result.xlsx中都只會存儲一年的數據,只會存在一個sheet,之前的所有數據都會被覆蓋。
通過查詢官方文檔(pandas.DataFrame.to_excel)和一個github上跨越了5年的issue(Allow ExcelWriter() to add sheets to existing workbook)得知pandas庫的ExcelWriter缺失了一個mode='a'
的append模式,所以在這種情況下每次to_excel()都會直接新建一個文件寫入而無視之前的數據。
解決方案是使用openpyxl engine來打開ExcelWriter,用openpyxl的load_workbook方法將之前已經存在的數據加載進ExcelWriter.book里。修改后的核心代碼如下:
1 # 依賴 openpyxl 庫 2 from openpyxl import load_workbook 3 4 while year <= 2018: 5 csvPath = sys.path[0] + '/result/%d.csv' % year 6 excelPath = sys.path[0] + '/result.xlsx' 7 csvReader = pandas.read_csv(csvPath, encoding='utf_8_sig') 8 # 增加 engine='openpyxl' 一欄 9 excelWriter = pandas.ExcelWriter(excelPath, engine='openpyxl') 10 # 使用 openpyxl 來把現有數據傳遞給excelWriter,使其在寫入的時候保留原本數據 11 book = load_workbook(excelPath) 12 excelWriter.book = book 13 14 print("正在將 %d 年的 %d 條數據轉換為 xlsx..." % (year, countThis)) 15 csvReader.to_excel(excelWriter, sheet_name=str(year)) 16 excelWriter.save()
如此存儲的excel文件里就會有多個sheets了,每個sheets里都存儲着一個csv里的全部數據。
需要注意的是這樣做的效率非常低,因為這並不是真正的追加模式,而是在每一次創建ExcelWriter對象之后,先將現有的數據全部傳入ExcelWriter,再將新的數據連同舊的數據一同寫入一個新的文件並覆蓋。這就導致程序作了許多重復而無用的工作,所以我在處理這個任務的時候。最后的幾個10+m的csv文件的平均耗時都在300s以上,如果還有后續任務的話,這個數字會一直增長下去。得到一個80m的xlsx總表耗費了接近一個小時的時間,這對於一些更大的任務來說是難以接受的。所以如果你需要處理的任務比較巨大,你可以脫離pandas庫而使用xlrd和xlwt里的方法,會使運行效率優雅不少。(也許直接使用excel的vba宏也是個不錯的選擇?)