Python — zipfile對壓縮檔的操作方式
搬家時,通常都會把要搬運的物品裝箱並打包起來,透過運送公司的卡車將一箱箱事先打包好的箱子運送到指定地點,也就是新的家。當物品到達指定地點後,再將一個個打包的箱子拆箱還原。為了減少運送的體積,可能會將物品中的空氣抽空,讓體積變小。這樣做的好處是,我們可以使用較小體積的箱子來裝箱,同時讓搬運卡車也可以一次多送幾箱,節省運送的費用。
與搬家的概念一樣,使用ZIP壓縮檔案也會產生類似的效果。在一個ZIP壓縮檔案中,可以包括很多檔案。這些被壓縮的檔案會被打包在一個副檔名為zip的檔案中。除了把檔案包裹再一起外,壓縮的過程也會讓檔案縮小其佔用的空間。整體來說,體積會比原來所有檔案加總起來要來得小。因此非常便於在網路上面傳輸與交換。
壓縮檔案裡面的資料,如果需要被使用,可以將Zip檔案解壓縮(如同包裹拆箱)。檔案一旦被解壓縮之後,裡面的檔案就會被還原回來。即使裡面有子資料夾,也會原封不動的還原回來。
電腦上的壓縮解壓縮
以目前大家使用的電腦來說,其作業系統(Windows或MacOS)中大多內建壓縮與解壓縮的程式。要執行壓縮或解壓縮檔案,可以說是非常的容易。以Mac電腦為例,只要先將要壓縮的檔案或資料夾選取起來,並按下右鍵,就會出現下面的選單,其中會看到「壓縮」的項目,可供我們壓縮檔案。
Python與Zip壓縮
若不想要凡事都「徒手」處理的話,是否可以透過程式來幫我們「大量的」壓縮與解壓縮檔案?
的確,Python連這個也會。真是無所不能!
在Python程式中,有一個zipfile模組,我們可以使用其模組中的函式來撰寫程式壓縮ZIP檔案或解壓縮ZIP檔案。以下針對zipfile模組的使用方式分別說明之。
匯入zipfile模組
要使用zipfile模組模組,必須先匯入zipfile模組。
import zipfile
檢查是否為ZIP 壓縮檔
若要確認某一個檔案是否為ZIP壓縮檔,可以使用zipfile模組的 is_zipfilec
函式。
import zipfile# 檔案名稱
file='abc.zip'# 檢查是否為壓縮檔
print(f'{file}: {zipfile.is_zipfile(file)}')
上面的程式碼會在螢幕上印出壓縮檔案的名稱與是否為壓縮檔(zipfile.is_zipfile(file)
),如果是壓縮檔就會印出True,反之會印出False。
讀取ZIP壓縮檔
若要讀取ZIP檔案的內容,必須建立ZipFile物件。我們可以透過ZipFile()函式建立ZipFile物件。呼叫ZipFile()函式時,需要傳入一個副檔名為zip(.zip)的壓縮檔案。
import zipfilepath = '檔案路徑'
ZIP = zipfile.ZipFile(f'{path}/ZIP檔案')
請注意zipfile模組與函式兩者的書寫方式。作為模組名稱時,zipfile全部為小寫,而當zipfile作為函式時,要寫成ZipFile,其中Z與F都是大寫。
如果把上面的ZIP印出來的話,會得到下面的結果:
<zipfile.ZipFile filename=’file/demo.zip’ mode=’r’>
這是一個物件,路徑為file,檔名為demo.zip以及模式為讀取。
查看壓縮檔內容
ZipFile物件中有個namelist()
函式可以取得壓縮檔案內的所有檔案與資料夾的名稱,這些資訊透過list串列方式返回給我們。如果需要進一步的資料,可以再對串列中的每一個項目使用getinfo()
與 infolist()
函式,取得其餘更詳細的資訊。
取得被壓縮的檔名
以下使用 namelist()
函式取得 ZIP 壓縮檔案之中所有檔案名稱。
ZIP = zipfile.ZipFile(f'ZIP檔案.zip')ZIP.namelist()
取得被壓縮檔的詳細資訊
使用infolist()
函式取得詳細資訊
ZIP.infolist()
或針對個別檔案使用getinfo()
取得資訊,getinfo()
裡面需要帶入個別檔案作為參數:
for name in ZIP.namelist():
info = ZIP.getinfo(name)
print(info)
解壓縮ZIP檔
全部解壓縮
ZipFile物件中的extractall()
函式可以解壓縮ZIP檔案,並且將解壓縮後的檔案存放在目前的工作資料夾下面。程式碼示範如下:
import zipfile# 檔案名稱
file='abc.zip'
ZIP = zipfile.ZipFile(file)
ZIP.extractall()
ZIP.close()
當執行這段程式碼之後,就會將檔案(例如上面的abc.zip
)解壓縮到目前的工作資料夾下面。如果想要將解壓縮的「檔案們」存在與工作資料夾不同的其他位置時,只要需要將新的路徑傳入extractall()
函式中即可。例如下面的程式碼,我們將新的路徑(url
)傳入extractall()
函式中,檔案解壓縮後就會被存到新的file資料夾裡面。這裡需注意的是,新路徑的資料夾必須已經被建立,若指定一個尚未建立的資料夾,就會出現錯誤。
import zipfile# 檔案名稱
file='abc.zip'
url = '/file/'
ZIP = zipfile.ZipFile(file)
ZIP.extractall(url)
ZIP.colse()
局部解壓縮
如果不想要將所有已壓縮的檔案全部解壓縮出來,只想從中取出單獨的一個檔案來解壓縮時,可以改用extrat()
函式。例如:我們想要單獨將ziped_file壓縮檔案裡面符合副檔名為csv的檔案單獨解壓縮時,可以使用下面的方式處理。為了方便起見,這裡使用了with 來建立一個zipfile物件:
import zipfile# 檔案名稱
ziped_file='abc.zip'with zipfile.ZipFile(f'{ziped_file}','r') as ZIP:
list_files = ZIP.namelist()
for file_name in list_files:
if file_name.endswith('.csv'):
ZIP.extract(file_name)
同理可知,使用for迴圈加上extrat()
函式,也可以將壓縮檔裡面的所有檔案解壓縮出來。
import zipfile# 檔案名稱
ziped_file='abc.zip'ZIP = zipfile.ZipFile(ziped_file)for name in ZIP.namelist():
ZIP.extract(name)
ZIP.colse()
或使用with的語法來進行,如此就不需要在程式碼最後面使用close關閉:
import zipfile# 檔案名稱
file='abc.zip'
with zipfile.ZipFile(file, 'r') as zf:
for name in zf.namelist():
zf.extract(name)
建立ZIP壓縮檔
ZIP壓縮檔的建立,一樣需要先有ZipFile物件。這裡與前面較為不一樣的地方是,ZipFile物件必須是以「寫入」模式開啟。因此我們除了傳入檔案給物件外,尚且需要傳入 w
作為參數。
import zipfile# 建立壓縮檔案
with zipfile.ZipFile('abc.zip', mode='w') as zf:
...略...
ZipFile物件建立後,即可將檔案加入壓縮檔打包。此時需使用 write()
函式並添加要加入的檔案名稱。
以上面的程式碼來說,我們可以在with
後面加上:
import zipfile# 建立壓縮檔案
with zipfile.ZipFile('abc.zip', mode='w') as zf:
# 將要壓縮的檔案加入
zf.write("file_a.pdf")
zf.write("file_b.pdf")
zf.write("file_c.pdf")
此外,我們也可以對 write()
加上 compress_type
參數來指定壓縮類型方式。這個參數會告訴電腦要以哪一種演算法來對檔案進行壓縮。以下面例子中的 ZIP_DEFLATED 來說,是一種比較常見的壓縮方式。
zf.write("file_a.pdf", compress_type=zipfile.ZIP_DEFLATED)
添加檔案到既有ZIP壓縮檔中
如前面的程式碼,我們使用mode='w'
的方式將檔案寫入壓縮檔案中。這樣子的模式會覆蓋並刪掉原本在ZIP檔案中的內容。如果您希望的是將某的檔案「添加」到既有的ZIP壓縮檔之中,就需要將模式改為 a(亦即 mode='a'
)。這樣子才會用「添加」的方式將檔案加入ZIP中。
import zipfile# 建立壓縮檔案
with zipfile.ZipFile('zipfile.zip', mode='a') as zf:
# 將要添加的檔案加入zipfile.zip壓縮檔中
zf.write("file_d.pdf")
實作:解壓縮程式碼
接下來我們就可以透過上面的說明,來實際撰寫一個解壓縮檔案程式。只要將想要解壓縮的檔案放在指定的source資料夾中,執行該程式碼,就會自動將程式解壓縮完成。以下是本次實作程式碼的說明:
1. 特定的壓縮檔位置
這裡設定一開始都將壓縮檔放在工作目錄中的source資料夾。
# 工作目錄
project_dir = '/Users/to/prokect_path'#壓縮檔案位置
src_file = f'{project_dir}/source/{zip_file_name}'#暫時資料夾
temp_des_dir = f'{project_dir}/temp_des/'des_dir = f'{project_dir}/des/'
2. 避免中文亂碼
我們在先前的解壓縮說明中,並沒有將「非英數檔名」壓縮檔而解壓縮時所產生的亂碼考慮在內。在此我們透過encode與decode的方式( .encode('cp437').decode('big5')
),為檔名重新給定正確編碼(big5
)的名稱。
3. 刪除暫存檔
另外,在這裡使用了暫存資料夾儲存程式執行中間的壓縮檔案,當整個解壓縮過程完畢之後,我們即透過shutil
套件裡面的rmtree
順便把暫存區的壓縮檔案給刪除。
以下為實作解壓縮檔的完整程式碼:
from pathlib import Path
from zipfile import ZipFile
import shutil, oszip_file_name='zipedfile.zip'
# 專案路徑
project_dir = '/Users/to/prokect_path'
temp_des_dir = f'{project_dir}/temp_des/'
des_dir = f'{project_dir}/des/'
src_file = f'{project_dir}/source/{zip_file_name}'# zip物件
with ZipFile(src_file,'r') as zf:
for file_name in zf.namelist():
print(file_name)
unzip = zf.extract(file_name, temp_des_dir)
extracted_path = Path(unzip) #判斷Des資料夾是否存在
isExist = os.path.exists(des_dir)
if not isExist:
os.mkdir(des_dir)
print("建立Des資料夾")
new_path = Path(des_dir, file_name.encode('cp437').decode('big5'))
rename_path = extracted_path.rename(new_path)try:
print('解壓縮完成...刪除暫存資料夾')
# remove temp_des_dir
shutil.rmtree(Path(temp_des_dir))
except Exception as e:
raise e
結語
Python的zipfile模組可以用來壓縮ZIP檔案以及解壓縮ZIP檔案。如果能夠熟悉這類型的模組,就可以透過它們的使用拼組成一些自動化的系統。在各方面幫助我們,節省日常生活中不必要的瑣碎工作。