Я расклеивание пример того, как вы можете сделать это с помощью python-requests библиотеки. В приведенном ниже скрипте проверяется, существует ли файл назначения. Если файл частично назначения существует, он считается частично загруженным файлом и пытается возобновить загрузку. Если сервер заявляет о поддержке частичного запроса HTTP (т. Е. Ответ на запрос HEAD содержит заголовок Accept-Range), то сценарий возобновляется в зависимости от размера частично загруженного файла; иначе он просто выполняет обычную загрузку и отбрасывает части, которые уже загружены. Я думаю, что было бы довольно просто преобразовать это, чтобы использовать только urllib2, если вы не хотите использовать python-запросы, это, вероятно, будет намного более подробным.
Обратите внимание, что возобновление загрузки может привести к повреждению файла, если файл на сервере изменен между начальной загрузкой и резюме. Это можно обнаружить, если сервер поддерживает сильный заголовок HTTP ETag, поэтому загрузчик может проверить, возобновляет ли он тот же файл.
Я не утверждаю, что это ошибка. Вероятно, вы должны добавить логику контрольной суммы вокруг этого скрипта, чтобы обнаружить ошибки загрузки и повторить попытку с нуля, если контрольная сумма не соответствует.
import logging
import os
import re
import requests
CHUNK_SIZE = 5*1024 # 5KB
logging.basicConfig(level=logging.INFO)
def stream_download(input_iterator, output_stream):
for chunk in input_iterator:
output_stream.write(chunk)
def skip(input_iterator, output_stream, bytes_to_skip):
total_read = 0
while total_read <= bytes_to_skip:
chunk = next(input_iterator)
total_read += len(chunk)
output_stream.write(chunk[bytes_to_skip - total_read:])
assert total_read == output_stream.tell()
return input_iterator
def resume_with_range(url, output_stream):
dest_size = output_stream.tell()
headers = {'Range': 'bytes=%s-' % dest_size}
resp = requests.get(url, stream=True, headers=headers)
input_iterator = resp.iter_content(CHUNK_SIZE)
if resp.status_code != requests.codes.partial_content:
logging.warn('server does not agree to do partial request, skipping instead')
input_iterator = skip(input_iterator, output_stream, output_stream.tell())
return input_iterator
rng_unit, rng_start, rng_end, rng_size = re.match('(\w+) (\d+)-(\d+)/(\d+|\*)', resp.headers['Content-Range']).groups()
rng_start, rng_end, rng_size = map(int, [rng_start, rng_end, rng_size])
assert rng_start <= dest_size
if rng_start != dest_size:
logging.warn('server returned different Range than requested')
output_stream.seek(rng_start)
return input_iterator
def download(url, dest):
''' Download `url` to `dest`, resuming if `dest` already exists
If `dest` already exists it is assumed to be a partially
downloaded file for the url.
'''
output_stream = open(dest, 'ab+')
output_stream.seek(0, os.SEEK_END)
dest_size = output_stream.tell()
if dest_size == 0:
logging.info('STARTING download from %s to %s', url, dest)
resp = requests.get(url, stream=True)
input_iterator = resp.iter_content(CHUNK_SIZE)
stream_download(input_iterator, output_stream)
logging.info('FINISHED download from %s to %s', url, dest)
return
remote_headers = requests.head(url).headers
remote_size = int(remote_headers['Content-Length'])
if dest_size < remote_size:
logging.info('RESUMING download from %s to %s', url, dest)
support_range = 'bytes' in [s.strip() for s in remote_headers['Accept-Ranges'].split(',')]
if support_range:
logging.debug('server supports Range request')
logging.debug('downloading "Range: bytes=%s-"', dest_size)
input_iterator = resume_with_range(url, output_stream)
else:
logging.debug('skipping %s bytes', dest_size)
resp = requests.get(url, stream=True)
input_iterator = resp.iter_content(CHUNK_SIZE)
input_iterator = skip(input_iterator, output_stream, bytes_to_skip=dest_size)
stream_download(input_iterator, output_stream)
logging.info('FINISHED download from %s to %s', url, dest)
return
logging.debug('NOTHING TO DO')
return
def main():
TEST_URL = 'http://mirror.internode.on.net/pub/test/1meg.test'
DEST = TEST_URL.split('/')[-1]
download(TEST_URL, DEST)
main()
Если вы не возражаете решение, которое использует внешнюю библиотеку, это дубликат http://stackoverflow.com/questions/16694907/how-to-download-large-file-in-python -with-requests-py –
Поблагодарите Lie Ryan, я посмотрю, хотя я предпочитаю делать это с помощью основных библиотек, поэтому мне не нужно заставлять людей устанавливать что-либо дополнительное. –