[python] 요청으로 파이썬에서 큰 파일 다운로드

요청 은 정말 좋은 라이브러리입니다. 큰 파일 (> 1GB)을 다운로드하는 데 사용하고 싶습니다. 문제는 전체 파일을 메모리에 보관할 수 없다는 것입니다. 청크 단위로 읽어야합니다. 그리고 이것은 다음 코드의 문제입니다

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 

어떤 이유로 든이 방법으로 작동하지 않습니다. 파일에 저장하기 전에 여전히 응답을 메모리에로드합니다.

최신 정보

FTP에서 큰 파일을 다운로드 할 수있는 작은 클라이언트 (Python 2.x /3.x)가 필요한 경우 여기에서 찾을 수 있습니다 . 멀티 스레딩 및 재 연결 (연결 모니터링)도 지원하며 다운로드 작업에 대한 소켓 매개 변수를 조정합니다.



답변

다음 스트리밍 코드를 사용하면 다운로드 한 파일의 크기에 관계없이 Python 메모리 사용이 제한됩니다.

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 you have chunk encoded response uncomment if
                # and set chunk_size parameter to None.
                #if chunk: 
                f.write(chunk)
    return local_filename

사용하여 반환 된 바이트 수는 iter_content정확히 chunk_size; 종종 훨씬 더 큰 난수 일 것으로 예상되며 매 반복마다 다를 것으로 예상됩니다.

자세한 내용은 https://requests.readthedocs.io/en/latest/user/advanced/#body-content-workflowhttps://requests.readthedocs.io/en/latest/api/#requests.Response.iter_content 를 참조 하십시오. 참고.


답변

당신이 사용하는 경우 그것은 훨씬 쉽게 Response.rawshutil.copyfileobj():

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

이것은 과도한 메모리를 사용하지 않고 파일을 디스크로 스트리밍하며 코드는 간단합니다.


답변

아니 정확히 무엇 영업 이익은 요청했지만 … 그것과 그렇게 안되게 쉽게 urllib:

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)

또는 임시 파일로 저장하려면 다음과 같이하십시오.

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)

나는 과정을 보았다 :

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

파일이 커지는 것을 보았지만 메모리 사용량은 17MB로 유지되었습니다. 뭔가 빠졌습니까?


답변

청크 크기가 너무 클 수 있습니다. 한 번에 1024 바이트를 떨어 뜨려 보셨습니까? (또한 with구문을 정리하는 데 사용할 수 있습니다 )

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 

또한 응답이 메모리에로드되었다고 어떻게 추론하고 있습니까?

파이썬은 다른에서 파일의 데이터를 플러시되지 않은 것처럼 소리 SO 질문 당신이 시도 할 수 f.flush()os.fsync()파일 쓰기 및 사용 가능한 메모리를 강제로를;

    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())


답변