2015-03-20 7 views
3

Я перехожу к Python с другого языка, и я не уверен, как правильно его решить. Использование библиотеки urllib2 это довольно легко настроить прокси-сервер и получить данные с сайта:Urllib2 Python - ответ и ответ на разделение

import urllib2 

req = urllib2.Request('http://www.voidspace.org.uk') 
response = urllib2.urlopen(req) 
the_page = response.read() 

У меня есть проблема в том, что текстовый файл, который извлекается очень большие (сотни мегабайт) и соединение часто проблематично. Код также должен улавливать ошибки соединения, сервера и передачи (он будет частью небольшого широко используемого конвейера).

Может ли кто-нибудь предложить, как изменить код выше, чтобы убедиться, что код автоматически повторно подключается n раз (например, 100 раз) и, возможно, разделяет ответ на куски, чтобы данные загружались быстрее и надежнее?

Я уже разложил запросы так сильно, как мог, теперь нужно убедиться, что код восстановления так хорош, насколько это возможно. Решения, основанные на основных библиотеках python, идеальны.

Возможно, библиотека уже делает выше, в этом случае есть ли способ улучшить загрузку больших файлов? Я использую UNIX и должен иметь дело с прокси.

Благодарим за помощь.

+0

Если вы не возражаете решение, которое использует внешнюю библиотеку, это дубликат http://stackoverflow.com/questions/16694907/how-to-download-large-file-in-python -with-requests-py –

+0

Поблагодарите Lie Ryan, я посмотрю, хотя я предпочитаю делать это с помощью основных библиотек, поэтому мне не нужно заставлять людей устанавливать что-либо дополнительное. –

ответ

0

Вы можете попробовать что-то вроде этого. Он читает файл по строкам и добавляет его в файл. Он также проверяет, чтобы вы не переходили по той же строке. Я напишу еще один скрипт, который сделает это кусками.

import urllib2 
file_checker = None 
print("Please Wait...") 
while True: 
    try: 
     req = urllib2.Request('http://www.voidspace.org.uk') 
     response = urllib2.urlopen(req, timeout=20) 
     print("Connected") 
     with open("outfile.html", 'w+') as out_data: 
      for data in response.readlines(): 
       file_checker = open("outfile.html") 
       if data not in file_checker.readlines(): 
        out_data.write(str(data)) 
     break 
    except urllib2.URLError: 
     print("Connection Error!") 
     print("Connecting again...please wait") 
file_checker.close() 
print("done") 

Вот как читать данные на куски, а не по линии

import urllib2 

CHUNK = 16 * 1024 
file_checker = None 
print("Please Wait...") 
while True: 
    try: 
     req = urllib2.Request('http://www.voidspace.org.uk') 
     response = urllib2.urlopen(req, timeout=1) 
     print("Connected") 
     with open("outdata", 'wb+') as out_data: 
      while True: 
       chunk = response.read(CHUNK) 
       file_checker = open("outdata") 
       if chunk and chunk not in file_checker.readlines(): 
       out_data.write(chunk) 
       else: 
        break 
     break 
    except urllib2.URLError: 
     print("Connection Error!") 
     print("Connecting again...please wait") 
file_checker.close() 
print("done") 
+0

С помощью того, как вы проверяете дублирующийся фрагмент, этот скрипт, вероятно, испортит ваши загрузки, если у вас есть файл с большим количеством повторяющейся структуры. –

+0

Знаешь, я об этом не думал. Неплохо подмечено! Как бы вы это сделали?Я подумывал об этом некоторое время. – reticentroot

+0

Если сервер поддерживает запрос HTTP Range, я бы послал это, чтобы пропустить необходимость повторной загрузки уже загруженных частей. В противном случае, если сервер не поддерживает Range для запроса, я бы прочитал текущий размер файла outfile и просто выбросил столько данных, прежде чем продолжить, как обычно. В любом случае, это хорошая идея, чтобы сделать контрольную сумму, чтобы убедиться, что ошибки, которые вызывают повреждение файлов, могут быть обнаружены. –

1

Я расклеивание пример того, как вы можете сделать это с помощью 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() 
+0

Вау, очень тщательный код, большое спасибо. Библиотека запросов выглядит довольно гибкой, я отдам ей все. –

Смежные вопросы