Summary
URLを記述したtextファイルを読み込んで、そこに書かれているファイルを順にダウンロードするpythonスクリプトを作成
【2022.7.15追記】 指定したURLからローカルのフォルダにダウンロードする際に階層フォルダも引き継ぐように仕様変更
-uオプションで root URLを指定し、list.txtファイル内ではroot URLからの相対ディレクトリでファイルを指定する。ダウンロード後は -o ./outdirなどで指定したoutdirの中にフォルダを作って配置
ただし、-dオプションが指定されていればoutdirの直下にファイルをベタ置きする。
1行に1ファイルのURLを記載したlist.txtファイルを準備
listファイルと出力先のフォルダを指定する
$ fetchfile.py -i list.txt -d -o ./outdir
list.txtの例
# this line is comment (to be specified with "-c #")
https://www.hogehoge1.com/folder1/file1.zip
https://www.hogehoge1.com/folder2/file2.wave ...
ダウンロード後に書き出すファイルのファイル名は、URLに記載されたものがそのまま使われる
-c オプションで指定した文字または文字列で始まる行はコメント行として読み飛ばされる(たとえば、 -c #
とすれば#で始まる行はコメント行として読み飛ばす)
-v オプションを指定するとプログレスバーを表示(tqdmを使用)
$ fetchfile.py -v -i list.txt -o ./outdir -c #
listファイルにはファイル名のみを指定し、 -u オプションでroot URLを指定
$ fetchfile.py -i list.txt -o ./outdir -u "https://www.hogehoge1.com/"
root URLとファイル名を結合したURLからダウンロード
ただし出力は./outdirにフォルダを作ってlist.txtで指定したファイル名で保存される(2022.5.15版で修正)
folder1/file1.zip folder2/file2.zip
User-Agentを設定しないとerrorを返すサイトもあるので、headersで指定する。アクセスに失敗した場合は、total_sizeには0が入る
import requests
in_url = "http://weburl.com/"
headers = {"User-Agent":"Mozilla/5.0"}
response = requests.get(in_url, headers=headers, stream=True)
total_size = int(response.headers.get('content-length', 0))
【2021.10.3追記】
Transfer-Encoding: chunkedが指定されている場合には、content-lengthが提供されないことがあり、上記のコードではファイルをうまくダウンロードできない。
このため、その様な場合にはtotal_size = -1としてエラー終了せずに継続する様に修正。
tqdmを使ってプログレスバーを表示する。 使っているコマンドラインターミナルによっては表示が乱れることがある
from tqdm import tqdm
...= int(response.headers.get('content-length', 0))
total_size = 1024
block_size = tqdm(total=total_size, unit=' iB', unit_scale=True)
t with open(out_fname, "wb") as handle:
for data in response.iter_content(block_size):
len(data))
t.update(
handle.write(data)
t.close()
# Check
if total_size != 0 and t.n != total_size:
print("ERROR reading file")
else:
print(" ok!")
ファイルを順に開く。 -v オプションをつけるとダウンロード中にプログレスバーを表示する
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# =================================================
# download files located listed URLs in input text
#
# fetchfile2.py
# Coded by Noboru Harada (noboru@ieee.org)
#
# Changes:
# 2020/05/10 First version
# 2020/05/11 added -v option for verbose mode
# 2020/05/12 added -c option for commentout
# 2021/10/03 fixed code for cases with no content-length provided
# 2022/07/15 added -d option to align outfolder names
#
# Usage:
# > fetchfile.py [-v] [-d] -i infile.txt -o outdir [-u root_url] [-c prefix]
#
# =================================================
import sys
import os
import argparse
from tqdm import tqdm
import requests
from urllib.parse import urlparse
import re
# parse commandline args
def getargs():
= argparse.ArgumentParser(
parser ="fetch foles from URL listed in infile.txt")
description"-i", "--infile", type=str,
parser.add_argument(help="infile.txt")
"-o", "--outdir", type=str,
parser.add_argument(help="outdir")
"-u", "--url", type=str,
parser.add_argument(help="specify root URL")
"-v", "--verbose", action='store_true', default=None,
parser.add_argument(help="generate more info during the process")
"-d", "--direct", action='store_true', default=None,
parser.add_argument(help="store files directly to the outdir")
"-c", "--comment", type=str, default=None,
parser.add_argument(help="specify comment-out prefix (e.g. \"-c #\")")
= parser.parse_args()
args
if args.outdir is None:
= ""
args.outdir
parser.print_help()-1)
exit(
return args
def fetch_file(infile, outroot, root_url, verbose, comment, direct):
= 0
row_no
= open(infile, "r", encoding="utf-8")
infileobj while True:
= infileobj.readline()
line += 1
row_no
= line.rstrip('\n')
in_url
# skip lines began with comment prefix
if comment is not None:
if re.search("^"+comment, in_url):
if verbose is not None:
print("\"" + in_url + "\"")
print(" comment line --> skip")
continue
# when root URL string is specified with -u option
if root_url is not None:
if in_url is not None:
= root_url + in_url
data_url
if not in_url:
print(str(row_no) + ": \""+in_url+"\"")
print(" Blank line --> skip")
break
else:
#fname = os.path.basename(urlparse(in_url).path)
dir, fname = os.path.split(urlparse(in_url).path)
if not fname:
continue
if direct is not None:
= outroot+fname
out_fname else:
= outroot + dir
out_dir = out_dir + "/" + fname
out_fname print(out_fname)
if not os.path.exists(out_dir):
os.makedirs(out_dir)
if verbose is not None:
print(str(row_no) + ": \""+in_url+"\"")
print(" fetching a file from URL: ")
print(" "+data_url)
print(" to: \""+out_fname+"\"")
# do nothing if the file already exists
if os.path.exists(out_fname):
print(" already exists --> skip")
else:
= {"User-Agent":"Mozilla/5.0"}
headers #response = requests.get(data_url, headers=headers, stream=True, verify=False)
= requests.get(data_url, headers=headers, stream=True)
response
= 0
n = 1024
block_size = 0
total_size if "content-length" in response.headers:
= int(response.headers.get('content-length', 0))
total_size if(total_size == 0):
print(" ERROR reading URL --> skip")
continue
else:
if verbose is not None:
print(" Size: "+str(int(total_size/1024))+" KB")
if verbose is not None:
= tqdm(total=total_size, unit=' iB', unit_scale=True)
t with open(out_fname, "wb") as handle:
for data in response.iter_content(block_size):
len(data))
t.update(
handle.write(data)
t.close()= t.n
n else:
with open(out_fname, "wb") as handle:
for data in response.iter_content(block_size):
+= len(data)
n
handle.write(data)elif "chunked" in response.headers.get("Transfer-Encoding", ""):
if verbose is not None:
print(" Size info does not exist.")
print(response.headers)
with open(out_fname, "wb") as handle:
for data in response.iter_content(block_size):
+= len(data)
n
handle.write(data)else:
print(" ERROR reading URL --> skip")
print(" no content-length nor transfer-encoding chunked exist.")
if verbose is not None:
print(response.headers)
continue
# Check
if total_size > 0 and n != total_size:
print("ERROR reading file (read %d KB)" % (n))
else:
print(" ok! (read %d KB)" % (n))
infileobj.close()print("processed "+str(row_no)+" lines")
if __name__ == '__main__':
# read command line options
= getargs()
args
= args.outdir.rstrip('/¥')+"/"
out_path = args.infile
infile
= ""
root_url if args.url is not None:
= args.url.rstrip('/¥')+"/"
root_url
# check folder path
if not os.path.exists(out_path):
print(out_path+" does not exist")
-1)
exit(
if not os.path.exists(infile):
print(infile+" does not exist")
-1)
exit(
fetch_file(infile, out_path, root_url, args.verbose, args.comment, args.direct)