0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Pythonによるtarファイルのダウンロードと解凍

Last updated at Posted at 2021-04-17

概要

機械学習の学習用データをダウンロードする際に、
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ファイルのダウンロードと解凍でした。
まぁ半分以上拝借したものですけどね。

もし何か間違いや、こうした方がいいんじゃないかという部分がありましたら、教えていただけたら嬉しいです。
あと、拝借した書籍、とてもおもしろかったのでぜひご覧ください。

参考

ゼロから作るDeep Learning ❸
tarfile公式

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?