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

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

Допустим, байты в удаленном файле: 1234567890

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

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

    5 Solutions collect form web for “Python ищет удаленный файл, используя HTTP”

    Если вы загружаете удаленный файл через 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's Range Not Satisfiable error raise RangeError('Requested Range Not Satisfiable') 

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

    Я настоятельно рекомендую использовать библиотеку запросов . Это просто лучшая библиотека 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() 

    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() 

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

    Поэтому я просто выполнил поиск Google для «HTTP get», и я нашел это для вас:

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

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

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

    Я не обнаружил каких-либо существующих реализаций файлового интерфейса с URL-адресами seek () для HTTP, поэтому я развернул свою собственную простую версию: https://github.com/valgur/pyhttpio . Это зависит от urllib.request но, вероятно, может быть легко изменено для использования requests , если это необходимо.

    Полный код:

     import cgi import time import urllib.request from io import IOBase from sys import stderr class SeekableHTTPFile(IOBase): def __init__(self, url, name=None, repeat_time=-1, debug=False): """Allow a file accessible via HTTP to be used like a local file by utilities that use `seek()` to read arbitrary parts of the file, such as `ZipFile`. Seeking is done via the 'range: bytes=xx-yy' HTTP header. Parameters ---------- url : str A HTTP or HTTPS URL name : str, optional The filename of the file. Will be filled from the Content-Disposition header if not provided. repeat_time : int, optional In case of HTTP errors wait `repeat_time` seconds before trying again. Negative value or `None` disables retrying and simply passes on the exception (the default). """ super().__init__() self.url = url self.name = name self.repeat_time = repeat_time self.debug = debug self._pos = 0 self._seekable = True with self._urlopen() as f: if self.debug: print(f.getheaders()) self.content_length = int(f.getheader("Content-Length", -1)) if self.content_length < 0: self._seekable = False if f.getheader("Accept-Ranges", "none").lower() != "bytes": self._seekable = False if name is None: header = f.getheader("Content-Disposition") if header: value, params = cgi.parse_header(header) self.name = params["filename"] def seek(self, offset, whence=0): if not self.seekable(): raise OSError if whence == 0: self._pos = 0 elif whence == 1: pass elif whence == 2: self._pos = self.content_length self._pos += offset return self._pos def seekable(self, *args, **kwargs): return self._seekable def readable(self, *args, **kwargs): return not self.closed def writable(self, *args, **kwargs): return False def read(self, amt=-1): if self._pos >= self.content_length: return b"" if amt < 0: end = self.content_length - 1 else: end = min(self._pos + amt - 1, self.content_length - 1) byte_range = (self._pos, end) self._pos = end + 1 with self._urlopen(byte_range) as f: return f.read() def readall(self): return self.read(-1) def tell(self): return self._pos def __getattribute__(self, item): attr = object.__getattribute__(self, item) if not object.__getattribute__(self, "debug"): return attr if hasattr(attr, '__call__'): def trace(*args, **kwargs): a = ", ".join(map(str, args)) if kwargs: a += ", ".join(["{}={}".format(k, v) for k, v in kwargs.items()]) print("Calling: {}({})".format(item, a)) return attr(*args, **kwargs) return trace else: return attr def _urlopen(self, byte_range=None): header = {} if byte_range: header = {"range": "bytes={}-{}".format(*byte_range)} while True: try: r = urllib.request.Request(self.url, headers=header) return urllib.request.urlopen(r) except urllib.error.HTTPError as e: if self.repeat_time is None or self.repeat_time < 0: raise print("Server responded with " + str(e), file=stderr) print("Sleeping for {} seconds before trying again".format(self.repeat_time), file=stderr) time.sleep(self.repeat_time) 

    Небольшой пример использования:

     url = "https://www.python.org/ftp/python/3.5.0/python-3.5.0-embed-amd64.zip" f = SeekableHTTPFile(url, debug=True) zf = ZipFile(f) zf.printdir() zf.extract("python.exe") 

    Изменить: на самом деле в этом ответе есть фактически идентичная, хотя и немного более минимальная, реализация: https://stackoverflow.com/a/7852229/2997179

      Python - лучший язык программирования в мире.