2009-12-28 13 views
10

का उपयोग कर रिमोट फाइल पर खोजते हैं मैं दूरस्थ (HTTP) फ़ाइल पर किसी विशेष स्थिति की तलाश कैसे करूं ताकि मैं केवल उस भाग को डाउनलोड कर सकूं?पाइथन HTTP

चलें कहना एक दूरस्थ फ़ाइल पर बाइट्स गया: 1234567890

मैं 4 के लिए की तलाश है और वहाँ से 3 बाइट्स डाउनलोड तो मैं चाहता हूँ के लिए होता है: 456

और यह भी, मैं कैसे एक दूरस्थ अगर जाँच करते हैं फाइल मौजूद है? मैंने कोशिश की, os.path.isfile() लेकिन जब मैं रिमोट फ़ाइल यूआरएल पास कर रहा हूं तो यह गलत हो जाता है।

+2

: वहाँ वास्तव में एक ज्यादातर समान, अगर थोड़ा और अधिक कम से कम, इस जवाब में कार्यान्वयन है "रिमोट" से आपका क्या मतलब है? –

+0

आप किस प्रोटोकॉल का उपयोग कर रहे हैं? एचटीटीपी? एफ़टीपी? एनएफएस? SFTP? रिमोट द्वारा –

+0

मेरा मतलब है w/बेहतर कार्यान्वयन के लिए http – Marconi

उत्तर

15

यदि आप HTTP के माध्यम से दूरस्थ फ़ाइल डाउनलोड कर रहे हैं, तो आपको Range शीर्षलेख सेट करने की आवश्यकता है।

in this example जांचें कि यह कैसे किया जा सकता है। इस तरह दिखता है:

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

संपादित: I just found a better implementation। यह वर्ग उपयोग करने में बहुत आसान है, क्योंकि इसे डॉकस्ट्रिंग में देखा जा सकता है।

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

अद्यतन: "बेहतर कार्यान्वयन" byterange.py फ़ाइल में github: excid3/urlgrabber ले जाया गया है।

+0

+1। –

+0

बस मुझे जो चाहिए था। धन्यवाद। – Marconi

1

मुझे लगता है कि आपके प्रश्न की कुंजी यह है कि आपने "दूरस्थ फ़ाइल यूआरएल" कहा था। इसका तात्पर्य यह है कि आप एक HTTP "प्राप्त" ऑपरेशन के साथ फ़ाइल डाउनलोड करने के लिए एक HTTP यूआरएल का उपयोग कर रहे हैं।

तो मैं बस के लिए "HTTP GET" एक गूगल खोज किया था और मुझे आपके लिए यह पाया:

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

ऐसा लगता है कि आप एक HTTP प्राप्त में एक बाइट सीमा निर्दिष्ट कर सकते हैं।

तो, आपको एक HTTP लाइब्रेरी का उपयोग करने की आवश्यकता है जो आपको बाइट रेंज निर्दिष्ट करने देता है। और जब मैं इसे टाइप कर रहा था, तो जब्ची ने एक उदाहरण के लिए एक लिंक पोस्ट किया।

4

AFAIK, यह fseek() या इसी तरह का उपयोग करना संभव नहीं है। इसे प्राप्त करने के लिए आपको HTTP रेंज हेडर का उपयोग करने की आवश्यकता है। यह हेडर सर्वर द्वारा समर्थित हो सकता है या नहीं भी हो सकता है, इसलिए आपका माइलेज भिन्न हो सकता है।

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

संपादित करें: इस कोर्स यह सोचते हैं कि रिमोट फाइल से आप एक HTTP सर्वर पर संग्रहीत कोई फ़ाइल मतलब है ...

फ़ाइल आप चाहते हैं एक 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() 
+0

आपको 206 प्रतिक्रिया कोड का भी इलाज करना चाहिए, क्योंकि यदि आप HTTP श्रेणी शीर्षलेख का उपयोग कर रहे हैं तो वे स्वीकार्य हो सकते हैं। – jbochi

+0

पर्याप्त मेला। आपका उत्तर ऐसा करता है :) –

5

मैं अत्यधिक 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() 
+0

तब पुस्तकालय वापस अनुरोध नहीं था, लेकिन हाँ यह चीजों को अब आसान बनाता है। – Marconi

0

मैं HTTP URL को करना चाहते हैं (के साथ एक फ़ाइल की तरह इंटरफेस के किसी भी मौजूदा कार्यान्वयन) नहीं मिल रहा था, इसलिए मैं अपने खुद के सरल लुढ़का संस्करण: 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