Pobierz duży plik w Pythonie z żądaniami


Zapytania
https://requests.readthedocs.io/
to naprawdę dobra biblioteka. Chciałbym go używać do pobierania dużych plików (> 1 GB).
Problem w tym, że nie jest możliwe przechowywanie w pamięci całego pliku, muszę go czytać kawałek po kawałku. I to jest problem z następującym kodem
import requestsdef 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

Z jakiegoś powodu to nie działa w ten sposób. Nadal ładuje odpowiedź do pamięci przed zapisaniem jej do pliku.

UPDATE

Jeśli potrzebujesz małego klienta (Python 2.x/3.x), który może pobierać duże pliki z FTP, możesz go znaleźć

tutaj
https://github.com/keepitsimple/pyFTPclient... Obsługuje wielowątkowość & amp; ponowne połączenia (monitoruje połączenia), a także konfiguruje ustawienia gniazda dla zadania rozruchu.
Zaproszony:
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Korzystając z następującego kodu przesyłania strumieniowego, użycie pamięci Python jest ograniczone niezależnie od rozmiaru przesłanego pliku:
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

Zwróć uwagę, że liczba bajtów zwrócona przez
iter_content
nie jest dokładnie równa
chunk_size
; oczekuje się, że będzie to liczba losowa, która jest często znacznie większa i oczekuje się, że będzie się różnić w każdej iteracji.
Widzieć

https://requests.readthedocs.i ... kflow
https://requests.readthedocs.i ... kflow
i

https://requests.readthedocs.i ... ntent
https://requests.readthedocs.i ... ntent
do wykorzystania w przyszłości.
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Jest to o wiele łatwiejsze, jeśli używasz
Response.raw
https://requests.readthedocs.i ... e.raw
i
shutil.copyfileobj()
https://docs.python.org/3/libr ... leobj
:
import requests
import shutildef 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

Spowoduje to przeniesienie pliku na dysk bez nadmiernego wykorzystania pamięci, a kod jest prosty.
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Nie jest to dokładnie to, o co prosił OP, ale ... śmiesznie łatwo to zrobić za pomocą
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)

Lub w ten sposób, jeśli chcesz zapisać go w pliku tymczasowym:
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)

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

Widziałem, jak plik rośnie, ale użycie pamięci pozostało na poziomie 17 MB. Czy coś mi brakuje?
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Twój rozmiar może być zbyt duży, czy próbowałeś go wyrzucić - może 1024 bajty na raz? (możesz również użyć
with
, aby uporządkować składnię)
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

Swoją drogą, jak ustalisz, że odpowiedź została załadowana do pamięci?
Wygląda na to, że Python nie zrzuca danych do pliku od innych

pytania
https://coderoad.ru/7127075/
Więc możesz spróbować
f.flush ()
i
os.fsync ()
, aby wymusić zapis pliku i zwolnić pamięć;
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())
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Opierając się na najpopularniejszym rzymskim komentarzu powyżej, oto moja realizacja,
w tym mechanizm „pobierz jako” i „ponów”:
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 ''

Aby odpowiedzieć na pytania, Zaloguj się lub Zarejestruj się