Python ищет удаленный файл, используя HTTP

Как мне искать конкретную позицию в удаленном (HTTP) файле, чтобы я мог загрузить только эту часть?

Предположим, что байты в удаленном файле: 1234567890

Я хочу искать 4 и загружать 3 байта, чтобы у меня было: 456

а также, как проверить, существует ли удаленный файл? Я попытался, os.path.isfile(), но он возвращает False, когда я передаю удаленный файл url.

Ответ 1

Если вы загружаете удаленный файл через HTTP, вам нужно установить заголовок Range.

Отметьте в этом примере, как это можно сделать. Выглядит так:

myUrlclass.addheader("Range","bytes=%s-" % (existSize))

EDIT: Я просто нашел лучшую реализацию. Этот класс очень прост в использовании, как это видно в docstring.

class HTTPRangeHandler(urllib2.BaseHandler):
"""Handler that enables HTTP Range headers.

This was extremely simple. The Range header is a HTTP feature to
begin with so all this class does is tell urllib2 that the 
"206 Partial Content" reponse from the HTTP server is what we 
expected.

Example:
    import urllib2
    import byterange

    range_handler = range.HTTPRangeHandler()
    opener = urllib2.build_opener(range_handler)

    # install it
    urllib2.install_opener(opener)

    # create Request and set Range header
    req = urllib2.Request('http://www.python.org/')
    req.header['Range'] = 'bytes=30-50'
    f = urllib2.urlopen(req)
"""

def http_error_206(self, req, fp, code, msg, hdrs):
    # 206 Partial Content Response
    r = urllib.addinfourl(fp, hdrs, req.get_full_url())
    r.code = code
    r.msg = msg
    return r

def http_error_416(self, req, fp, code, msg, hdrs):
    # HTTP Range Not Satisfiable error
    raise RangeError('Requested Range Not Satisfiable')

Обновить. "Лучшая реализация" переместилась в github: excid3/urlgrabber в byterange.py.

Ответ 2

Я настоятельно рекомендую использовать библиотеку requests. Это просто лучшая библиотека HTTP, которую я когда-либо использовал. В частности, чтобы выполнить то, что вы описали, вы сделали бы что-то вроде:

import requests

url = "http://www.sffaudio.com/podcasts/ShellGameByPhilipK.Dick.pdf"

# Retrieve bytes between offsets 3 and 5 (inclusive).
r = requests.get(url, headers={"range": "bytes=3-5"})

# If a 4XX client error or a 5XX server error is encountered, we raise it.
r.raise_for_status()

Ответ 3

AFAIK, это невозможно с помощью fseek() или аналогичного. Для этого вам нужно использовать заголовок HTTP Range. Этот заголовок может поддерживаться или не поддерживаться сервером, поэтому ваш пробег может отличаться.

import urllib2

myHeaders = {'Range':'bytes=0-9'}

req = urllib2.Request('http://www.promotionalpromos.com/mirrors/gnu/gnu/bash/bash-1.14.3-1.14.4.diff.gz',headers=myHeaders)

partialFile = urllib2.urlopen(req)

s2 = (partialFile.read())

EDIT: Это, конечно, предполагает, что удаленным файлом вы имеете в виду файл, хранящийся на HTTP-сервере...

Если файл, который вы хотите, находится на FTP-сервере, FTP только позволяет указать начальное смещение, а не диапазон. Если это то, что вы хотите, тогда следующий код должен сделать это (не проверено!)

import ftplib
fileToRetrieve = 'somefile.zip'
fromByte = 15
ftp = ftplib.FTP('ftp.someplace.net')
outFile = open('partialFile', 'wb')
ftp.retrbinary('RETR '+ fileToRetrieve, outFile.write, rest=str(fromByte))
outFile.close()

Ответ 4

Я думаю, что ключ к вашему вопросу заключается в том, что вы сказали "удаленный файл url". Это означает, что вы используете HTTP-URL для загрузки файла с помощью операции HTTP get.

Итак, я просто выполнил поиск Google для "HTTP get", и я нашел это для вас:

http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35

Похоже, вы можете указать диапазон байтов в HTTP-получателе.

Итак, вам нужно использовать библиотеку HTTP, которая позволяет указать диапазон байтов. И когда я печатал это, jbochi отправил ссылку на пример.

Ответ 5

Вы можете использовать httpio для доступа к удаленным HTTP файлам, как если бы они были локальными:

pip install httpio
import zipfile
import httpio

url = "http://some/large/file.zip"
with httpio.open(url) as fp:
    zf = zipfile.ZipFile(fp)
    print(zf.namelist())