요청이 있는 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
어떤 이유로 인해 이 방법은 작동하지 않습니다. 파일에 저장되기 전에 응답을 메모리에 로드합니다.
다음 스트리밍 코드를 사용하면 다운로드된 파일의 크기에 관계없이 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; 이것은 종종 훨씬 더 큰 난수가 될 것으로 예상되며, 모든 반복에서 다를 것으로 예상됩니다.
자세한 내용은 body-content-workflow 및 Response.iter_content를 참조하십시오.
및 을 사용하면 훨씬 쉬워집니다.
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
이렇게 하면 과도한 메모리를 사용하지 않고 파일을 디스크로 스트리밍할 수 있으며 코드는 간단합니다.
주의: 설명서에 따르면Response.raw디코딩되지 않음gzip그리고.deflatetransfer-encoding(전송 부호화)을 실시하기 때문에, 수동으로 실시할 필요가 있습니다.
작전본부가 부탁한 건 아니지만...그것을 하는 것은 터무니없이 쉽다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
덧붙여서, 응답이 메모리에 로드된 것을 어떻게 추측하고 있습니까?
Python이 다른 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())
사용하다wget대신 python 모듈입니다.여기 토막이 있습니다.
import wget
wget.download(url)
위의 Roman의 가장 높은 코멘트에 근거해, 「다운로드 as」와 「재시도」메커니즘을 포함한 나의 실장을 이하에 나타냅니다.
def download(url: str, file_path='', attempts=2):
"""Downloads a URL content into a file (with large file support by streaming)
:param url: URL to download
:param file_path: Local file name to contain the data downloaded
:param attempts: Number of attempts
:return: New file path. Empty string if the download failed
"""
if not file_path:
file_path = os.path.realpath(os.path.basename(url))
logger.info(f'Downloading {url} content to {file_path}')
url_sections = urlparse(url)
if not url_sections.scheme:
logger.debug('The given url is missing a scheme. Adding http scheme')
url = f'http://{url}'
logger.debug(f'New url: {url}')
for attempt in range(1, attempts+1):
try:
if attempt > 1:
time.sleep(10) # 10 seconds wait time between downloads
with requests.get(url, stream=True) as response:
response.raise_for_status()
with open(file_path, 'wb') as out_file:
for chunk in response.iter_content(chunk_size=1024*1024): # 1MB chunks
out_file.write(chunk)
logger.info('Download finished successfully')
return file_path
except Exception as ex:
logger.error(f'Attempt #{attempt} failed with error: {ex}')
return ''
requests좋긴 한데, 어때?socket해결 방법?
def stream_(host):
import socket
import ssl
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
context = ssl.create_default_context(Purpose.CLIENT_AUTH)
with context.wrap_socket(sock, server_hostname=host) as wrapped_socket:
wrapped_socket.connect((socket.gethostbyname(host), 443))
wrapped_socket.send(
"GET / HTTP/1.1\r\nHost:thiscatdoesnotexist.com\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9\r\n\r\n".encode())
resp = b""
while resp[-4:-1] != b"\r\n\r":
resp += wrapped_socket.recv(1)
else:
resp = resp.decode()
content_length = int("".join([tag.split(" ")[1] for tag in resp.split("\r\n") if "content-length" in tag.lower()]))
image = b""
while content_length > 0:
data = wrapped_socket.recv(2048)
if not data:
print("EOF")
break
image += data
content_length -= len(data)
with open("image.jpeg", "wb") as file:
file.write(image)
다음은 모든 파일 내용을 메모리에 읽지 않고 비동기 청크다운로드의 사용 사례에 대한 추가 접근법입니다.
즉, URL로부터의 판독과 파일에의 기입은 모두,asyncioaiohttp에서 aiofiles을 사용하다
는 ᄃ다에 적용되어야 합니다.Python 3.7그리고 나중에.
만 하면 .SRC_URL ★★★★★★★★★★★★★★★★★」DEST_FILE복사하여 붙여넣기 전에 변수를 선택합니다.
import aiofiles
import aiohttp
import asyncio
async def async_http_download(src_url, dest_file, chunk_size=65536):
async with aiofiles.open(dest_file, 'wb') as fd:
async with aiohttp.ClientSession() as session:
async with session.get(src_url) as resp:
async for chunk in resp.content.iter_chunked(chunk_size):
await fd.write(chunk)
SRC_URL = "/path/to/url"
DEST_FILE = "/path/to/file/on/local/machine"
asyncio.run(async_http_download(SRC_URL, DEST_FILE))
언급URL : https://stackoverflow.com/questions/16694907/download-large-file-in-python-with-requests
'programing' 카테고리의 다른 글
| 3가지 다른 점은 (0) | 2022.09.25 |
|---|---|
| 값 오류:요소가 두 개 이상인 배열의 참값 값이 모호합니다.a.any() 또는 a.all()을 사용합니다. (0) | 2022.09.25 |
| Ruby on Rails 3 소켓 '/tmp/mysql'을 통해 로컬 MySQL 서버에 연결할 수 없습니다.OSX에서 '삭 (0) | 2022.09.25 |
| C에서 extern 키워드를 올바르게 사용하는 방법 (0) | 2022.09.25 |
| MYSQL을 사용하여 난수 생성 (0) | 2022.09.25 |