「Pythonを使用して、複数のファイル(またはURL)を並列でダウンロードする」

Pythonを使用して、複数のファイルを並列でダウンロードする

より少ない時間でより多くのデータを取得する

Photo by Wesley Tingey on Unsplash

私たちはビッグデータの世界に生きています。しばしば、ビッグデータは複数のファイルから構成される1つの大きなデータセットとして整理されます。これらのデータを取得することは、ダウンロード(または取得の負担)のためにしばしばイライラすることがあります。幸いなことに、少しのコードで、ファイルのダウンロードと取得を自動化して高速化する方法があります。

ファイルのダウンロードを自動化することで、多くの時間を節約できます。Pythonを使用してファイルをダウンロードする方法はいくつかあります。ファイルをダウンロードする最も簡単な方法は、ダウンロードするURLのリストを繰り返し処理するための単純なPythonループを使用することです。このシリアルアプローチは、いくつかの小さなファイルではうまく機能しますが、多くのファイルや大きなファイルをダウンロードする場合は、計算リソースを最大限に活用するために並列アプローチを使用する必要があります。

並列ファイルダウンロードルーチンを使用すると、コンピュータのリソースを効果的に活用して複数のファイルを同時にダウンロードすることができ、時間を節約することができます。このチュートリアルでは、Pythonで汎用のファイルダウンロード関数を開発し、シリアルおよび並列アプローチで複数のファイルをダウンロードする方法を示します。このチュートリアルのコードでは、Python標準ライブラリからのみ使用可能なモジュールのみを使用しているため、追加のインストールは必要ありません。

モジュールをインポートする

この例では、ファイルを並列でダウンロードするために、requestsmultiprocessingのPythonモジュールのみが必要です。requestsmultiprocessingモジュールは、どちらもPython標準ライブラリから利用できるため、インストールする必要はありません。

また、個々のファイルのダウンロードにかかる時間を追跡し、シリアルと並列のダウンロードルーチンのパフォーマンスを比較するためにtimeモジュールをインポートします。timeモジュールもPython標準ライブラリの一部です。

import requests import time from multiprocessing import cpu_count from multiprocessing.pool import ThreadPool

URLとファイル名を定義する

米国の日別降水データを含むgridMET NetCDFファイルを使用して、Pythonで並列ファイルダウンロードをデモンストレーションします。

ここでは、リスト内に4つのファイルのURLを指定しています。他のアプリケーションでは、ダウンロードするファイルのリストをプログラムで生成することもあります。

urls = ['https://www.northwestknowledge.net/metdata/data/pr_1979.nc', 'https://www.northwestknowledge.net/metdata/data/pr_1980.nc', 'https://www.northwestknowledge.net/metdata/data/pr_1981.nc', 'https://www.northwestknowledge.net/metdata/data/pr_1982.nc']

各URLはそのダウンロード場所と関連付けられる必要があります。ここでは、ファイルをWindowsの「ダウンロード」ディレクトリにダウンロードしています。ファイル名はシンプルさと透明性のためにリストにハードコードされています。アプリケーションに応じて、入力URLを解析して特定のディレクトリにダウンロードするコードを記述することもできます。

fns = [r'C:\Users\konrad\Downloads\pr_1979.nc', r'C:\Users\konrad\Downloads\pr_1980.nc', r'C:\Users\konrad\Downloads\pr_1981.nc', r'C:\Users\konrad\Downloads\pr_1982.nc']

マルチプロセッシングでは、並列関数は1つの引数のみを持つ必要があります(いくつかの回避策はありますが、ここでは触れません)。ファイルをダウンロードするには、URLとファイル名の2つの引数が必要です。したがって、urlsfnsのリストを結合して、2つの情報を含むタプルのリストを取得します。リスト内の各タプルには、URLとURLのダウンロードファイル名の2つの要素が含まれます。これにより、2つの情報を含む単一の引数(タプル)を渡すことができます。

inputs = zip(urls, fns)

URLをダウンロードするための関数

これでダウンロードするURLとそれに関連するファイル名を指定したので、URLをダウンロードするための関数(download_url)が必要です。

download_urlには1つの引数(arg)を渡します。この引数は、ダウンロードするURL(url)とファイル名(fn)の2要素を持つイテラブル(リストまたはタプル)です。要素は可読性のために変数(urlfn)に割り当てられます。

URLが作成された後、試行文を作成し、URLが取得されたらファイルに書き込まれるようにします。ファイルが書き込まれたら、URLとダウンロード時間が返されます。例外が発生した場合はメッセージが表示されます。

download_url関数はコードの肝です。実際のダウンロードとファイル作成の作業を行います。この関数を使用して、ファイルをシリアルに(ループを使用して)および並列にダウンロードできます。それぞれの例を見ていきましょう。

def download_url(args):   t0 = time.time()   url, fn = args[0], args[1]   try:     r = requests.get(url)     with open(fn, 'wb') as f:       f.write(r.content)       return(url, time.time() - t0)   except Exception as e:     print('download_url()で例外が発生しました:', e)

Pythonのループを使用して複数のファイルをダウンロードする

関連するファイルへのURLのリストをダウンロードするには、作成したイテラブル(inputs)をループして、各要素をdownload_urlに渡します。各ダウンロードが完了するたびに、ダウンロードしたURLとダウンロードにかかった時間を表示します。

すべてのURLをダウンロードするまでの合計時間は、すべてのダウンロードが完了した後に表示されます。

t0 = time.time() for i in inputs:   result = download_url(i)   print('url:', result[0], 'time:', result[1])   print('合計時間:', time.time() - t0)

出力:

url: https://www.northwestknowledge.net/metdata/data/pr_1979.nc time: 16.381176710128784 url: https://www.northwestknowledge.net/metdata/data/pr_1980.nc time: 11.475878953933716 url: https://www.northwestknowledge.net/metdata/data/pr_1981.nc time: 13.059367179870605url: https://www.northwestknowledge.net/metdata/data/pr_1982.nc time: 12.232381582260132 合計時間: 53.15849542617798

個々のファイルのダウンロードには11〜16秒かかりました。合計のダウンロード時間は1分未満でした。ダウンロード時間は、特定のネットワーク接続に基づいて異なる場合があります。

このシリアル(ループ)アプローチを以下の並列アプローチと比較しましょう。

Pythonを使って複数のファイルを並列にダウンロードする

まず、並列ダウンロードを処理する関数(download_parallel)を作成します。関数(download_parallel)は、URLと関連するファイル名(前述のinputs変数)が含まれるイテラブルを1つの引数として受け取ります。

次に、処理に使用できるCPUの数を取得します。これにより、並列で実行するスレッドの数が決まります。

そして、multiprocessingThreadPoolを使用して、inputsdownload_url関数にマッピングします。ここでは、ThreadPoolimap_unorderedメソッドを使用し、download_url関数とdownload_urlへの入力引数(inputs変数)を渡します。imap_unorderedメソッドは、指定されたスレッド数で(つまり、並列ダウンロードで)download_urlを同時に実行します。

したがって、4つのファイルと4つのスレッドがある場合、すべてのファイルは同時にダウンロードされます。これにより、処理時間をかなり節約できます。

download_parallel関数の最後の部分では、ダウンロードされたURLと各URLのダウンロードに必要な時間が表示されます。

def download_parallel(args):   cpus = cpu_count()   results = ThreadPool(cpus - 1).imap_unordered(download_url, args)   for result in results:     print('url:', result[0], 'time (s):', result[1])

inputsdownload_parallelが定義されたら、1行のコードでファイルを並列にダウンロードできます。

download_parallel(inputs)

出力:

url: https://www.northwestknowledge.net/metdata/data/pr_1980.nc 時間 (秒): 14.641696214675903 url: https://www.northwestknowledge.net/metdata/data/pr_1981.nc 時間 (秒): 14.789752960205078 url: https://www.northwestknowledge.net/metdata/data/pr_1979.nc 時間 (秒): 15.052601337432861 url: https://www.northwestknowledge.net/metdata/data/pr_1982.nc 時間 (秒): 23.287317752838135 合計時間: 23.32273244857788

各個ファイルのダウンロードがアプローチによってより長い時間がかかったことに注意してください。これはネットワークの速度が変わったこと、またはダウンロードを対応するスレッドにマップするために必要なオーバーヘッドの結果かもしれません。個々のファイルのダウンロードには時間がかかりましたが、並列処理の方法により、総ダウンロード時間は50%減少しました。

並列処理によって、複数のファイルの処理時間を大幅に短縮することができることがわかります。ファイルの数が増えるにつれて、並列ダウンロードアプローチを使用することでさらに多くの時間を節約することができます。

結論

開発や分析ルーティンでのファイルダウンロードを自動化することは、多くの時間を節約することができます。このチュートリアルでは、並列ダウンロードルーティンを実装することで、多くのファイルや大きなファイルが必要な場合にファイルの取得時間を大幅に短縮することができることが示されています。

元記事は https://opensourceoptions.com で公開されています。

We will continue to update VoAGI; if you have any questions or suggestions, please contact us!

Share:

Was this article helpful?

93 out of 132 found this helpful

Discover more