アカデミック

【Pythonお悩み解決】web上の大量のファイルをダウンロードしたい

この記事は, Pythonを利用して研究を行なっていく中で私がつまずいてしまったポイントをまとめていくものです。同じような状況で苦しんでいる方々の参考になれば嬉しいです。Pythonつまずきポイント集の目次は以下のページをご覧ください。

【超初心者お悩み解決】Pythonつまずきポイント記事まとめページ この記事は,Pythonを利用して研究を行なっていく中で私がつまずいてしまったポイントをまとめていくものです。同じような状況で苦しんで...

本記事で紹介する解決策がBestという保証はできません。正確な情報を発信するように心掛けていますが図らずも誤った情報を記載してしまう場合があります。もしご指摘等がありましたら,コメント欄またはお問い合わせページよりご連絡下さい。

環境

●Ubuntu 18.04
●Python 3.7.3
●conda 4.8.3
●pytorch 1.2.0

やりたいこと

Web上のファイルを手作業で一回一回ダウンロードするのは面倒くさいから取得のためのURLを利用してPythonスクリプトを回して一括保存したい。

実現方法

「urllib.request.urlretrieve」を利用します。例えば,「https://hoge.com」というサイトからファイルをダウンロードしたいとします。試しに手作業で1つのファイルをダウンロードしてみると,「https://hoge.com/download/0」というURLに一瞬リダイレクトされ,ローカルのダウンロード先にしているディレクトリにファイルが保存されることが分かったとします。

すると,単純には「https://hoge.com/download/0」「https://hoge.com/download/1」…のようにしてURLの最後の部分を0,1,2…のように変えていってURLにアクセスすればよいことになります。そこで便利な関数が「urllib.request.urlretrieve」です。

この関数は返り値は「local_filename」と「headers」です。例えば,headersにはファイルサイズの情報やファイル名の情報などが格納されています。

import urllib.request
local_filename, headers = urllib.request.urlretrieve("https://python.org/")
print(local_filename)
print(headers)
print(int(headers["content-length"]))
# local_filename
/tmp/tmp7b_gcasi

# headers
Server: nginx
Content-Type: text/html; charset=utf-8
X-Frame-Options: DENY
Via: 1.1 vegur
Via: 1.1 varnish
Content-Length: 48797
Accept-Ranges: bytes
Date: Sun, 12 Apr 2020 03:55:28 GMT
Via: 1.1 varnish
Age: 650
Connection: close
......

# int(headers["content_length"])
48797

さて,この関数の2つ目の引数に「output_path」つまりダウンロード先のパスを指定してあげると,指定先にURLで指定したファイルがダウンロードされるという仕様になっています。

# ダウンロードの実行
url = "https://hoge.com/0"
output_path = "/home/[your username]/Downloads/dataset"
urllib.request.urlretrieve(url, output_path)

これをひたすら繰り返せば良いわけです。単純にはwhileを使ってループしていけばOKです。その際,tryを使えば例えばファイルのサイズが極端に大きい場合や小さい場合にダウンロードをスキップするなどの工夫が可能です。

import urllib.request
import os

url = "https://hoge.com/"
output_path = "/home/[your username]/Downloads/dataset/"

count = 0
while count < 999:
  this_file = url + str(count)
  # 例えば拡張子が.txtのとき
  this_name = output_path + str(count) + ".txt"
  try:
    _, headers = urllib.request.urlretrieve(this_file, this_name)
    # 10bytes以下か1GB以上は消去
    if (int(headers["content-length"]) < 10) or (int(headers["content-length"]) > 1e9:
      os.remove(this_name)
  except:
    print("ダウンロード失敗")

  count += 1

ここで,urllib.request.urlretrieveを実行している時点でos.removeしなくては小さすぎるファイルや大きすぎるファイルを削除することができませんので注意して下さい。

COMMENT

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です