用python + openpyxl處理excel(07+)文檔 + 一些中文處理的技巧

http://www.cnblogs.com/joyeecheung/p/3842002.html

用python + openpyxl處理excel(07+)文檔 + 一些中文處理的技巧

最近要幫做RA的老姐寫個合并excel工作表的腳本……源數(shù)據(jù)是4000+個excel 工作表溺忧,分布在9個xlsm文件里镜豹,文件內(nèi)容是中英文混雜的一些數(shù)據(jù)麻削,需要從每張表中提取需要的部分,分門別類合并到多個大的表里弥虐。

尋覓工具

確定任務之后第一步就是找個趁手的庫來干活增蹭。 Python Excel上列出了xlrd杆逗、xlwt、xlutils這幾個包剪个,但是

它們都比較老秧骑,xlwt甚至不支持07版以后的excel

它們的文檔不太友好,都可能需要去讀源代碼禁偎,而老姐的任務比較緊腿堤,加上我當時在期末,沒有這個時間細讀源代碼

再一番搜索后我找到了openpyxl如暖,支持07+的excel笆檀,一直有人在維護,文檔清晰易讀盒至,參照Tutorial和API文檔很快就能上手酗洒,就是它了~

安裝

這個很容易,直接pip install openpyxl枷遂,呵呵呵~

因為我不需要處理圖片樱衷,就沒有裝pillow。

一些考慮

源文件大約一個在1~2MB左右酒唉,比較小矩桂,所以可以直接讀入內(nèi)存處理。

既然是處理excel痪伦,何況他們整個組顯然都是win下干活(數(shù)據(jù)都用excel存了= =侄榴,商科的人啊……),這個腳本還是在win下做吧

這個任務完全不需要我對現(xiàn)有的文件做修改网沾!囧……我只要讀入癞蚕、處理、再寫出另一個文件就行了

學習使用

嗯辉哥,就是打開cmd桦山,然后用python的shell各種玩這個模塊來上手……(win下沒有裝ipython,囧)

做這個小腳本基本上我只需要import兩個東西

from openpyxl import Workbook

from openpyxl import load_workbook

load_workbook顧名思義是把文件導入到內(nèi)存醋旦,Workbook是最基本的一個類恒水,用來在內(nèi)存里創(chuàng)建文件最后寫進磁盤的。

干活

首先我需要導入這個文件

inwb = load_workbook(filename)

得到的就是一個workbook對象

然后我需要創(chuàng)建一個新的文件

outwb = Workbook()

接著在這個新文件里饲齐,用create_sheet新建幾個工作表寇窑,比如

careerSheet = outwb.create_sheet(0, 'career')

就會從頭部插入一個叫career的工作表(也就是說用法類似python list的insert)

接下來我需要遍歷輸入文件的每個工作表,并且按照表名做一些工作(e.g.如果表名不是數(shù)字箩张,我不需要處理)甩骏,openpyxl支持用字典一樣的方式通過表名獲取工作表窗市,獲取一個工作簿的表名的方法是get_sheet_names

for sheetName in inwb.get_sheet_names():

if not sheetName.isdigit():

continue

sheet = inwb[sheetName]

得到工作表之后,就是按列和行處理了饮笛。openpyxl會根據(jù)工作表里實際有數(shù)據(jù)的區(qū)域來確定行數(shù)和列數(shù)咨察,獲取行和列的方法是sheet.rows和sheet.columns,它們都可以像list一樣用福青。比如摄狱,如果我想跳過數(shù)據(jù)少于2列的表,可以寫

if len(sheet.columns) < 2:

continue

如果我想獲取這個工作表的前兩列无午,可以寫

colA, colB = sheet.columns[:2]

除了用columns和rows來得到這個工作表的行列之外媒役,還可以用excel的單元格編碼來獲取一個區(qū)域,比如

cells = sheet['A1':'B20']

有點像excel自己的函數(shù)宪迟,可以拉出一塊二維的區(qū)域~

為了方便處理酣衷,遇到一個沒有C列的工作表,我要創(chuàng)建一個和A列等長的空的C列出來次泽,那么我可以用sheet.cell這個方法穿仪,通過傳入單元格編號和添加空值來創(chuàng)建新列。

alen = len(colA)

for i in range(1, alen + 1):

sheet.cell('C%s' % (i)).value = None

注意:excel的單元格命名是從1開始的~

上面的代碼也顯示出來了意荤,獲取單元格的值是用cell.value(可以是左值也可以是右值)啊片,它的類型可以是字符串、浮點數(shù)玖像、整數(shù)紫谷、或者時間(datetime.datetime),excel文件里也會生成對應類型的數(shù)據(jù)捐寥。

得到每個單元格的值之后笤昨,就可以進行操作了~openpyxl會自 動將字符串用unicode編碼,所以字符串都是unicode類型的上真。

除了逐個逐個單元格用cell.value修改值以外咬腋,還可以一行行append到工作表里

sheet.append(strA, dateB, numC)

最后羹膳,等新的文件寫好睡互,直接用workbook.save保存就行

outwb.save("test.xlsx")

這個會覆蓋當前已有的文件,甚至你之前讀取到內(nèi)存的那個文件陵像。

一些要注意的地方

如果要在遍歷一列的每個單元格的時候獲取當前單元格的在這個column對象里的下標

for idx, cell in enumerate(colA):

# do something...

為了防止獲取的數(shù)據(jù)兩端有看不見的空格(excel文件里很常見的坑)就珠,記得strip()

如果工作表里的單元格沒有數(shù)據(jù),openpyxl會讓它的值為None醒颖,所以如果要基于單元格的值做處理妻怎,不能預先假定它的類型,最好用

if not cell.value

continue

之類的語句來先行判斷

如果要處理的excel文件里有很多noise泞歉,比如當你預期一個單元格是時間的時候逼侦,有些表的數(shù)據(jù)可能是字符串匿辩,這時候可以用

if isinstance(cell.value, unicode):

break

之類的語句處理。

win下的cmd似乎不太好設定用utf-8的code page榛丢,如果是簡體中文的話可以用936(GBK)铲球,print的時候會自動從unicode轉(zhuǎn)換到GBK輸出到終端。

一些幫忙處理中文問題的小函數(shù)

我處理的表有一些超出GBK范圍的字符晰赞,當我需要把一些信息print出來監(jiān)控處理進度的時候非常麻煩稼病,好在它們都是可以無視的,我直接用空格替換再print也行掖鱼,所以加上一些我本來就要替換掉的分隔符然走,我可以:

復制代碼

# annoying seperators

dot = u'\u00b7'

dash = u'\u2014'

emph = u'\u2022'

dot2 = u'\u2027'

seps = (u'.', dot, dash, emph, dot2)

def get_clean_ch_string(chstring):

"""Remove annoying seperators from the Chinese string.

Usage:

cleanstring = get_clean_ch_string(chstring)

"""

cleanstring = chstring

for sep in seps:

cleanstring = cleanstring.replace(sep, u' ')

return cleanstring

復制代碼

此外我還有一個需求,是把英文名[空格]中文名分成英文姓戏挡、英文名芍瑞、中文姓、中文名增拥。

首先我需要能把英文和中文分割開啄巧,我的辦法是用正則匹配,按照常見中英文字符在unicode的范圍來套掌栅。匹配英文和中文的正則pattern如下:

# regex pattern matching all ascii characters

asciiPattern = ur'[%s]+' % ''.join(chr(i) for i in range(32, 127))

# regex pattern matching all common Chinese characters and seporators

chinesePattern = ur'[\u4e00-\u9fff. %s]+' % (''.join(seps))

英文就用ASCII可打印字符的范圍替代秩仆,常見中文字符的范圍是\u4e00-\u9fff,那個seps是前面提到過的超出GBK范圍的一些字符猾封。 除了簡單的分割澄耍,我還需要處理只有中文名沒有英文名、只有英文名沒有中文名等情況晌缘,判斷邏輯如下:

復制代碼

def split_name(name):

"""Split [English name, Chinese name].

If one of them is missing, None will be returned instead.

Usage:

engName, chName = split_name(name)

"""

matches = re.match('(%s) (%s)' % (asciiPattern, chinesePattern), name)

if matches:? # English name + Chinese name

return matches.group(1).strip(), matches.group(2).strip()

else:

matches = re.findall('(%s)' % (chinesePattern), name)

matches = ''.join(matches).strip()

if matches:? # Chinese name only

return None, matches

else:? # English name only

matches = re.findall('(%s)' % (asciiPattern), name)

return ''.join(matches).strip(), None

復制代碼

得到了中文名之后齐莲,我需要分割成姓和名,因為任務要求不需要把姓名分割得很明確磷箕,我就按照常見的中文名姓名分割方式來分——兩個字or三個字的第一個字是姓选酗,四個字的前兩個字是姓,名字帶分隔符的(少數(shù)民族名字)分隔符前是姓(這里用到了前面的get_clean_ch_string函數(shù)來移除分隔符)岳枷,名字再長一些又不帶分割符的芒填,假設整個字符串都是名字。(注意英語的first name 指的是名空繁,last name指的是姓殿衰,2333)

復制代碼

def split_ch_name(chName):

"""Split the Chinese name into first name and last name.

* If the name is XY or XYZ, X will be returned as the last name.

* If the name is WXYZ, WX will be returned as the last name.

* If the name is ...WXYZ, the whole name will be returned

as the last name.

* If the name is ..ABC * XYZ..., the part before the seperator

will be returned as the last name.

Usage:

chFirstName, chLastName = split_ch_name(chName)

"""

if len(chName) < 4:? # XY or XYZ

chLastName = chName[0]

chFirstName = chName[1:]

elif len(chName) == 4:? # WXYZ

chLastName = chName[:2]

chFirstName = chName[2:]

else:? # longer

cleanName = get_clean_ch_string(chName)

nameParts = cleanName.split()

print u' '.join(nameParts)

if len(nameParts) < 2:? # ...WXYZ

return None, nameParts[0]

chLastName, chFirstName = nameParts[:2]? # ..ABC * XYZ...

return chFirstName, chLastName

復制代碼

分割英文名就很簡單了,空格分開盛泡,第一部分是名闷祥,第二部分是姓,其他情況暫時不管就行傲诵。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末凯砍,一起剝皮案震驚了整個濱河市箱硕,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌悟衩,老刑警劉巖颅痊,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異局待,居然都是意外死亡斑响,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進店門钳榨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來舰罚,“玉大人,你說我怎么就攤上這事薛耻∮眨” “怎么了?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵饼齿,是天一觀的道長饲漾。 經(jīng)常有香客問我,道長缕溉,這世上最難降的妖魔是什么考传? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮证鸥,結(jié)果婚禮上僚楞,老公的妹妹穿的比我還像新娘。我一直安慰自己枉层,他們只是感情好泉褐,可當我...
    茶點故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著鸟蜡,像睡著了一般膜赃。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上揉忘,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天跳座,我揣著相機與錄音,去河邊找鬼癌淮。 笑死躺坟,一個胖子當著我的面吹牛沦补,可吹牛的內(nèi)容都是我干的乳蓄。 我是一名探鬼主播,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼夕膀,長吁一口氣:“原來是場噩夢啊……” “哼虚倒!你這毒婦竟也來了美侦?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤魂奥,失蹤者是張志新(化名)和其女友劉穎菠剩,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體耻煤,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡具壮,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了哈蝇。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片棺妓。...
    茶點故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖炮赦,靈堂內(nèi)的尸體忽然破棺而出怜跑,到底是詐尸還是另有隱情,我是刑警寧澤吠勘,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布性芬,位于F島的核電站,受9級特大地震影響剧防,放射性物質(zhì)發(fā)生泄漏植锉。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一峭拘、第九天 我趴在偏房一處隱蔽的房頂上張望汽煮。 院中可真熱鬧,春花似錦棚唆、人聲如沸暇赤。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽鞋囊。三九已至,卻和暖如春瞎惫,著一層夾襖步出監(jiān)牢的瞬間溜腐,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工瓜喇, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留挺益,地道東北人。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓乘寒,卻偏偏與公主長得像望众,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,592評論 2 353

推薦閱讀更多精彩內(nèi)容

  • 使用首先需要了解他的工作原理 1.POI結(jié)構(gòu)與常用類 (1)創(chuàng)建Workbook和Sheet (2)創(chuàng)建單元格 (...
    長城ol閱讀 8,422評論 2 25
  • 最近在網(wǎng)上爬取奧運項目資料烂翰,并寫入Excel中夯缺。在寫到Excel中是用到了OpenPyXL,翻譯了一部分自己用到的...
    LeeLom閱讀 199,822評論 7 78
  • Simple Excel Export 簡單的Excel導出推薦http://www.cnblogs.com/hy...
    地獄咆哮Zzzzz閱讀 15,628評論 0 6
  • 轉(zhuǎn)自鏈接 目錄 1.認識NPOI 2.使用NPOI生成xls文件 2.1創(chuàng)建基本內(nèi)容 2.1.1創(chuàng)建Workboo...
    腿毛褲閱讀 10,542評論 1 3
  • 本例為設置密碼窗口 (1) If Application.InputBox(“請輸入密碼:”) = 1234 Th...
    浮浮塵塵閱讀 13,646評論 1 20