8

これは、このサイトの別の質問に基づいています: urllib3 を使用してファイルをダウンロードする最良の方法は何ですか。 ただし、そこにコメントすることはできないため、別の質問をします:

urllib3 で (より大きな) ファイルをダウンロードするには?

urllib2 ( Python 3 で web からファイルをダウンロード) で動作する同じコードを使用しようとしましたが、urllib3 では失敗します。

http = urllib3.PoolManager()

with http.request('GET', url) as r, open(path, 'wb') as out_file:       
    #shutil.copyfileobj(r.data, out_file) # this writes a zero file
    shutil.copyfileobj(r.data, out_file)

これは、「バイト」オブジェクトには「読み取り」属性がないことを示しています

次に、その質問のコードを使用しようとしましたが、データが常に「0」であるため、無限ループに陥ります。

http = urllib3.PoolManager()
r = http.request('GET', url)

with open(path, 'wb') as out:
    while True:
        data = r.read(4096)         
        if data is None:
            break
        out.write(data)
r.release_conn()

ただし、メモリ内のすべてを読み取ると、ファイルは正しくダウンロードされます。

http = urllib3.PoolManager()
r = http.request('GET', url)
with open(path, 'wb') as out:
    out.write(data)

非常に大きなファイルをダウンロードする可能性があるため、これはしたくありません。urllib のドキュメントがこのトピックのベスト プラクティスをカバーしていないのは残念です。

(また、リクエストや urllib2 を提案しないでください。自己署名証明書に関しては十分な柔軟性がないためです。)

4

1 に答える 1

11

あなたは非常に近かったです。欠落していた部分は設定ですpreload_content=False(これは次のバージョンのデフォルトになります)。また、応答を属性ではなく、ファイルのようなオブジェクトとして扱うこともでき.dataます (これはいつか非推奨になる魔法のプロパティです)。

- with http.request('GET', url) ...
+ with http.request('GET', url, preload_content=False) ...

このコードは動作するはずです:

http = urllib3.PoolManager()

with http.request('GET', url, preload_content=False) as r, open(path, 'wb') as out_file:       
    shutil.copyfileobj(r, out_file)

urllib3 の応答オブジェクトもioインターフェイスを尊重するため、次のようなこともできます...

import io
response = http.request(..., preload_content=False)
buffered_response = io.BufferedReader(response, 2048)

3 回の試行のいずれかに追加preload_content=Falseし、応答をファイルのようなオブジェクトとして扱う限り、それらはすべて機能するはずです。

urllib のドキュメントがこのトピックのベスト プラクティスをカバーしていないのは残念です。

おっしゃる通りです。https ://github.com/shazow/urllib3 からプル リクエストを送信して、このユース ケースの文書化にご協力いただければ幸いです。

于 2014-12-09T21:18:57.000 に答える