it-swarm.com.de

Laden Sie eine große Datei in python mit Anfragen herunter

Anfragen ist eine wirklich nette Bibliothek. Ich möchte es zum Herunterladen großer Dateien (> 1 GB) verwenden. Das Problem ist, dass es nicht möglich ist, die gesamte Datei im Speicher zu behalten. Ich muss sie in Stücken lesen. Und das ist ein Problem mit dem folgenden Code

import requests

def DownloadFile(url)
    local_filename = url.split('/')[-1]
    r = requests.get(url)
    f = open(local_filename, 'wb')
    for chunk in r.iter_content(chunk_size=512 * 1024): 
        if chunk: # filter out keep-alive new chunks
            f.write(chunk)
    f.close()
    return 

Aus irgendeinem Grund funktioniert es nicht so. Die Antwort wird weiterhin in den Speicher geladen, bevor sie in einer Datei gespeichert wird.

UPDATE

Wenn Sie einen kleinen Client (Python 2.x /3.x) benötigen, der große Dateien von FTP herunterladen kann, finden Sie ihn hier . Es unterstützt Multithreading & Reconnects (überwacht Verbindungen) und optimiert Socket-Parameter für die Download-Aufgabe.

324
Roman Podlinov

Mit dem folgenden Streaming-Code wird die Speichernutzung von Python unabhängig von der Größe der heruntergeladenen Datei eingeschränkt:

def download_file(url):
    local_filename = url.split('/')[-1]
    # NOTE the stream=True parameter below
    with requests.get(url, stream=True) as r:
        r.raise_for_status()
        with open(local_filename, 'wb') as f:
            for chunk in r.iter_content(chunk_size=8192): 
                if chunk: # filter out keep-alive new chunks
                    f.write(chunk)
                    # f.flush()
    return local_filename

Beachten Sie, dass die Anzahl der mit iter_content zurückgegebenen Bytes nicht genau chunk_size ist. Es wird erwartet, dass es sich um eine Zufallszahl handelt, die oft viel größer ist und sich in jeder Iteration unterscheidet.

Weitere Informationen finden Sie unter http://docs.python-requests.org/en/latest/user/advanced/#body-content-workflow .

556
Roman Podlinov

Es ist viel einfacher, wenn Sie Response.raw und shutil.copyfileobj() verwenden:

_import requests
import shutil

def download_file(url):
    local_filename = url.split('/')[-1]
    with requests.get(url, stream=True) as r:
        with open(local_filename, 'wb') as f:
            shutil.copyfileobj(r.raw, f)

    return local_filename
_

Dadurch wird die Datei ohne übermäßigen Arbeitsspeicher auf die Festplatte gestreamt, und der Code ist einfach.

193
John Zwinck

Nicht genau das, wonach OP gefragt hat, aber ... es ist lächerlich einfach, das mit urllib zu tun:

from urllib.request import urlretrieve
url = 'http://mirror.pnl.gov/releases/16.04.2/ubuntu-16.04.2-desktop-AMD64.iso'
dst = 'ubuntu-16.04.2-desktop-AMD64.iso'
urlretrieve(url, dst)

Oder so, wenn Sie es in einer temporären Datei speichern möchten:

from urllib.request import urlopen
from shutil import copyfileobj
from tempfile import NamedTemporaryFile
url = 'http://mirror.pnl.gov/releases/16.04.2/ubuntu-16.04.2-desktop-AMD64.iso'
with urlopen(url) as fsrc, NamedTemporaryFile(delete=False) as fdst:
    copyfileobj(fsrc, fdst)

Ich habe den Prozess beobachtet:

watch 'ps -p 18647 -o pid,ppid,pmem,rsz,vsz,comm,args; ls -al *.iso'

Und ich sah die Datei wachsen, aber die Speichernutzung blieb bei 17 MB. Vermisse ich etwas?

41
x-yuri

Ihr Block könnte zu groß sein. Haben Sie versucht, das zu löschen - vielleicht 1024 Bytes gleichzeitig? (Sie können auch with verwenden, um die Syntax aufzuräumen.)

def DownloadFile(url):
    local_filename = url.split('/')[-1]
    r = requests.get(url)
    with open(local_filename, 'wb') as f:
        for chunk in r.iter_content(chunk_size=1024): 
            if chunk: # filter out keep-alive new chunks
                f.write(chunk)
    return 

Wie schließen Sie übrigens, dass die Antwort in den Speicher geladen wurde?

Es hört sich so an, als ob python die Daten nicht in eine Datei spült, von anderen SO Fragen Sie könnten f.flush() und os.fsync() versuchen, das Schreiben der Datei zu erzwingen und freier Speicher;

    with open(local_filename, 'wb') as f:
        for chunk in r.iter_content(chunk_size=1024): 
            if chunk: # filter out keep-alive new chunks
                f.write(chunk)
                f.flush()
                os.fsync(f.fileno())
40
danodonovan