概要
機械学習の学習用データをダウンロードする際に、
tarファイルのダウンロード&解凍方法について調べたので、ついでに記事にいたしました。
すでに一部解凍してある場合には、上書きしないような処理も加えてあります。
環境
動作確認をした環境を載せておきます(他の環境ではためしていませんが、おそらく大丈夫だと思います)。
Windows10
Python3.8
コード
まずはコードのご紹介。
3つの関数を使っていますが、最初の2つは以下のURLから拝借させていただきました。
(おそらく大丈夫だと思いますが、問題があれば削除いたします)
https://github.com/oreilly-japan/deep-learning-from-scratch-3/blob/master/dezero/utils.py
import os
import tarfile
import urllib.request
cache_dir = os.path.join(os.path.expanduser('~'), '.data')
def show_progress(block_num, block_size, total_size):
"""function to display progress bar for urllib.request.urlretrieve.
"""
bar_template = "\r[{}] {:.2f}%"
downloaded = block_num * block_size
p = downloaded / total_size * 100
i = int(downloaded / total_size * 30)
if p >= 100.0: p = 100.0
if i >= 30: i = 30
bar = "#" * i + "." * (30 - i)
print(bar_template.format(bar, p), end='')
def get_file(url, file_name=None, file_dir=cache_dir):
"""Download a file from the 'url' if it is not in the cache.
By default, the file at the 'url' is downloaded to the '~/.kdezero'.
Args:
url (str): URL of the file.
file_name (str, optional):
Name of the file. It 'None' is specified the original
file name is used.
file_dir (str, optional):
Output directory.
Returns:
str: path to the saved file. (cache_dir / file_name)
"""
if file_name is None:
file_name = url[url.rfind('/') + 1:]
file_path = os.path.join(file_dir, file_name)
if not os.path.exists(file_dir):
os.mkdir(file_dir)
if os.path.exists(file_path):
return file_path
print("Downloading: " + file_name)
try:
urllib.request.urlretrieve(url, file_path, show_progress)
except(Exception, KeyboardInterrupt) as e:
if os.path.exists(file_path):
os.remove(file_path)
raise
print(" Done")
return file_path
def get_tar_file(url, file_dir=cache_dir):
"""Download and unzip a tar file from the 'url' if it is not in the cache.
By default, the file at the 'url' is downloaded to the '~/.kdezero'.
Args:
url (str): URL of the file.
file_name (str, optional):
Name of the file. It 'None' is specified the original
file name is used.
file_dir (str, optional):
Output directory.
Returns:
str: Path of the saved and unzipped file (cache_dir / file_name)
"""
try:
tar_file_path = get_file(url, None, file_dir)
except:
raise
tar = tarfile.TarFile(tar_file_path)
tar_members = tar.getmembers()
file_path = os.path.join(file_dir, tar_members[0].name)
members = []
for member in tar_members:
path = os.path.join(file_dir, member.name)
if not os.path.exists(path):
members.append(member)
print('{} file unfreezing'.format(len(members)))
if members:
tar.extractall(path=file_dir, members=members)
tar.close()
print("Done")
return file_path
解説
それではコードの解説になります。
show_progress & get_file
こちらの2つの関数は、拝借したものですので、詳しくはこちらの書籍をぜひご覧ください。
https://github.com/oreilly-japan/deep-learning-from-scratch-3
ちなみに「show_progress」はダウンロード時のプログレスバーの表示を行う関数で、
「get_file」はダウンロードをする関数です。
get_tar_file
ということで、私が書いたのはget_tar_fileだけなのですが、そちらの解説にうつります。
get_tar_file(url, file_dir=cache_dir)
この関数は、tarファイルをurlからfile_dirにダウンロードして、ファイルの解凍も行うというものです。
デフォルト設定となっているcache_dirは以下のようになっています。
cache_dir = os.path.join(os.path.expanduser('~'), '.data')
os.path.expanduser('~')で環境に依存せず、ホームディレクトリのpathをとってくることができます。
ダウンロード
次は関数の中身に入って、こちらのダウンロードを行う部分からです。
try:
tar_file_path = get_file(url, None, file_dir)
except:
raise
get_file関数は、urlからfile_dirにダウンロードして、指定のファイル名で保存する関数です。
今回はファイル名は指定せずNoneを渡しているので、urlの最後の方の部分がファイル名になります。
例:
url: aaaa/bbbb/cccc.tar -> cccc.tar
また、get_file関数は、すでに対象のファイルがダウンロードしてある場合には、ダウンロードをしません。
tar_file_path はダウンロードをしたファイルのパスになります。
メンバーの取得と、解答していないファイルの探索
次はどのファイルを解凍するかという部分になります。
もしすでに解凍してあるファイルだったりした場合は、ここではじくことができるので、
余計に解凍しなくてすみます。
tar = tarfile.TarFile(tar_file_path)
tar_members = tar.getmembers()
file_path = os.path.join(file_dir, tar_members[0].name)
members = []
for member in tar_members:
path = os.path.join(file_dir, member.name)
if not os.path.exists(path):
members.append(member)
まず、ここでtarファイルを開きます。
tar = tarfile.TarFile(tar_file_path)
次に、getmembersメソッドで、tarファイルに入っているファイルの情報を取得します。
tar_membersは、tarファイルの中に入っているファイルの情報のリストになっていて、
例えば、ファイルの名前などを一つ一つ取得したりすることもできます。
tar_members = tar.getmembers()
最後に解凍したファイルのパスを返したいので、以下のようにしてパスを取得しておきます。
tar_membersの最初の要素は、他の解凍されるファイルが入るディレクトリなので、その名前を
tar_members[0].nameで取得しています。
そして、os.path.joinでpathをくっつけています。
file_path = os.path.join(file_dir, tar_members[0].name)
次に、tarファイルの中身のリストであるtar_membersからファイル名を順番にとりだして、
そのファイルがすでに存在しているかを確認しています。
存在していなければ、membersリストに加えています。
members = []
for member in tar_members:
path = os.path.join(file_dir, member.name)
if not os.path.exists(path):
members.append(member)
これで解凍していないファイルを抽出することができました。
解凍
最後にtarファイルを解凍して、中身を取り出していきます。
tarファイルの解凍には、extractallメソッドを使います。
引数には、解凍先のpathと、何を取り出すかとしてmembersにリストを渡します。
(指定されなかった場合は、path='.', members=tar.getmembers()になります)
membersにわたすリストは、tar.getmembers()で取得できるリストの一部になっていないといけません。
そして最後に忘れないように、tar.close()をします。
print('{} file unfreezing'.format(len(members)))
if members:
tar.extractall(path=file_dir, members=members)
tar.close()
print("Done")
return file_path
終わりに
ということで、tarファイルのダウンロードと解凍でした。
まぁ半分以上拝借したものですけどね。
もし何か間違いや、こうした方がいいんじゃないかという部分がありましたら、教えていただけたら嬉しいです。
あと、拝借した書籍、とてもおもしろかったのでぜひご覧ください。