डिस्क में लिखे बिना एक .zip फ़ाइल को डाउनलोड करना और खोलना


85

मैं अपनी पहली पायथन स्क्रिप्ट प्राप्त करने में कामयाब रहा हूं, जो URL से .ZIP फ़ाइलों की सूची डाउनलोड करती है और फिर ज़िप फ़ाइलों को निकालने के लिए आगे बढ़ती है और उन्हें डिस्क पर लिखती है।

मैं अब अगले चरण को प्राप्त करने के लिए एक नुकसान में हूं।

मेरा प्राथमिक लक्ष्य ज़िप फ़ाइल को डाउनलोड करना और निकालना है और टीसीपी स्ट्रीम के माध्यम से सामग्री (सीएसवी डेटा) को पास करना है। मैं वास्तव में जिप या एक्सट्रेक्टेड फाइल्स को डिस्क में नहीं लिखना चाहूंगा अगर मैं इससे दूर हो सकूं।

यहाँ मेरी वर्तमान स्क्रिप्ट है जो काम करती है लेकिन दुर्भाग्य से डिस्क पर फाइल लिखना है।

import urllib, urllister
import zipfile
import urllib2
import os
import time
import pickle

# check for extraction directories existence
if not os.path.isdir('downloaded'):
    os.makedirs('downloaded')

if not os.path.isdir('extracted'):
    os.makedirs('extracted')

# open logfile for downloaded data and save to local variable
if os.path.isfile('downloaded.pickle'):
    downloadedLog = pickle.load(open('downloaded.pickle'))
else:
    downloadedLog = {'key':'value'}

# remove entries older than 5 days (to maintain speed)

# path of zip files
zipFileURL = "http://www.thewebserver.com/that/contains/a/directory/of/zip/files"

# retrieve list of URLs from the webservers
usock = urllib.urlopen(zipFileURL)
parser = urllister.URLLister()
parser.feed(usock.read())
usock.close()
parser.close()

# only parse urls
for url in parser.urls: 
    if "PUBLIC_P5MIN" in url:

        # download the file
        downloadURL = zipFileURL + url
        outputFilename = "downloaded/" + url

        # check if file already exists on disk
        if url in downloadedLog or os.path.isfile(outputFilename):
            print "Skipping " + downloadURL
            continue

        print "Downloading ",downloadURL
        response = urllib2.urlopen(downloadURL)
        zippedData = response.read()

        # save data to disk
        print "Saving to ",outputFilename
        output = open(outputFilename,'wb')
        output.write(zippedData)
        output.close()

        # extract the data
        zfobj = zipfile.ZipFile(outputFilename)
        for name in zfobj.namelist():
            uncompressed = zfobj.read(name)

            # save uncompressed data to disk
            outputFilename = "extracted/" + name
            print "Saving extracted file to ",outputFilename
            output = open(outputFilename,'wb')
            output.write(uncompressed)
            output.close()

            # send data via tcp stream

            # file successfully downloaded and extracted store into local log and filesystem log
            downloadedLog[url] = time.time();
            pickle.dump(downloadedLog, open('downloaded.pickle', "wb" ))

3
ज़िप प्रारूप को स्ट्रीम करने के लिए डिज़ाइन नहीं किया गया है। यह पादलेख का उपयोग करता है, जिसका अर्थ है कि आपको यह पता लगाने के लिए फ़ाइल के अंत की आवश्यकता है कि चीजें इसके अंदर कहां हैं, इसका अर्थ यह है कि इससे पहले कि आप कुछ भी कर सकते हैं आपको इसके सबसेट के साथ कुछ भी करने की आवश्यकता है।
चार्ल्स डफी

जवाबों:


65

मेरा सुझाव किसी StringIOवस्तु का उपयोग करना होगा । वे फाइलों का अनुकरण करते हैं, लेकिन स्मृति में रहते हैं। तो आप ऐसा कुछ कर सकते हैं:

# get_zip_data() gets a zip archive containing 'foo.txt', reading 'hey, foo'

import zipfile
from StringIO import StringIO

zipdata = StringIO()
zipdata.write(get_zip_data())
myzipfile = zipfile.ZipFile(zipdata)
foofile = myzipfile.open('foo.txt')
print foofile.read()

# output: "hey, foo"

या अधिक बस (विशाल से माफी):

myzipfile = zipfile.ZipFile(StringIO(get_zip_data()))
for name in myzipfile.namelist():
    [ ... ]

पायथन 3 में स्ट्रिंग के बजाय बाइट्सियो का उपयोग करें:

import zipfile
from io import BytesIO

filebytes = BytesIO(get_zip_data())
myzipfile = zipfile.ZipFile(filebytes)
for name in myzipfile.namelist():
    [ ... ]

"स्ट्रिंगिंग ऑब्जेक्ट यूनिकोड या 8-बिट स्ट्रिंग्स को स्वीकार कर सकता है" इसका मतलब यह नहीं है कि यदि आप लिखने के लिए बाइट्स की संख्या 0 मॉड 8 के अनुरूप नहीं है, तो आप या तो एक अपवाद फेंक देंगे या गलत डेटा लिखेंगे?
निंजाजेको 3

1
बिल्कुल नहीं - आप एक बार में केवल 8 बाइट्स ही क्यों लिख पाएंगे? इसके विपरीत, जब आप एक बार में 8 बिट से कम लिखते हैं?
प्रेषिती

@ninjagecko: आपको एक समस्या का डर लगता है यदि लिखित होने की उम्मीद बाइट्स की संख्या 8 से अधिक नहीं है। यह स्ट्रिंगो के बारे में बयान से व्युत्पन्न नहीं है और काफी निराधार है। स्ट्रिंगरियो के साथ समस्या यह है कि जब उपयोगकर्ता उन वस्तुओं के साथ वस्तुओं को मिलाता है जो सिस्टम डिफ़ॉल्ट एन्कोडिंग (जो आमतौर पर ) द्वारा डिकोडेबल नहीं होते हैं । unicodestrascii
जॉन माचिन

1
उपरोक्त कोड पर छोटी टिप्पणी: जब आप .zip से कई फाइलें पढ़ते हैं, तो सुनिश्चित करें कि आप डेटा को एक-एक करके पढ़ते हैं, क्योंकि दो बार zipfile.open पर कॉल करने से पहले संदर्भ को हटा दिया जाएगा।
scippie

15
ध्यान दें कि 3 पायथन के रूप में आपको उपयोग करना हैfrom io import StringIO
जॉर्ज लेइताओ

81

नीचे एक कोड स्निपेट है जिसका उपयोग मैंने ज़िपेड सीएसवी फ़ाइल लाने के लिए किया है, कृपया एक नज़र डालें:

अजगर 2 :

from StringIO import StringIO
from zipfile import ZipFile
from urllib import urlopen

resp = urlopen("http://www.test.com/file.zip")
zipfile = ZipFile(StringIO(resp.read()))
for line in zipfile.open(file).readlines():
    print line

अजगर 3 :

from io import BytesIO
from zipfile import ZipFile
from urllib.request import urlopen
# or: requests.get(url).content

resp = urlopen("http://www.test.com/file.zip")
zipfile = ZipFile(BytesIO(resp.read()))
for line in zipfile.open(file).readlines():
    print(line.decode('utf-8'))

यहाँ fileएक तार है। वास्तविक स्ट्रिंग प्राप्त करने के लिए जिसे आप पास करना चाहते हैं, आप उपयोग कर सकते हैं zipfile.namelist()। उदाहरण के लिए,

resp = urlopen('http://mlg.ucd.ie/files/datasets/bbc.zip')
zipfile = ZipFile(BytesIO(resp.read()))
zipfile.namelist()
# ['bbc.classes', 'bbc.docs', 'bbc.mtx', 'bbc.terms']

26

मैं विशाल के उत्कृष्ट उत्तर के एक अद्यतन पायथन 3 संस्करण की पेशकश करना चाहूंगा, जो कि अनुकूलन / परिवर्तनों के कुछ स्पष्टीकरण के साथ, पायथन 2 का उपयोग कर रहा था, जो पहले से ही उल्लेख किया गया हो सकता है।

from io import BytesIO
from zipfile import ZipFile
import urllib.request
    
url = urllib.request.urlopen("http://www.unece.org/fileadmin/DAM/cefact/locode/loc162txt.zip")

with ZipFile(BytesIO(url.read())) as my_zip_file:
    for contained_file in my_zip_file.namelist():
        # with open(("unzipped_and_read_" + contained_file + ".file"), "wb") as output:
        for line in my_zip_file.open(contained_file).readlines():
            print(line)
            # output.write(line)

आवश्यक परिवर्तन:

  • StringIOपायथन 3 में कोई मॉड्यूल नहीं है (इसे स्थानांतरित कर दिया गया है io.StringIO)। इसके बजाय, मैं io.BytesIO] 2 का उपयोग करता हूं , क्योंकि हम एक बाइटस्ट्रीम - डॉक्स , इस थ्रेड को भी संभालेंगे ।
  • urlopen:
    • " urllib.urlopenपायथन 2.6 और पहले से विरासत समारोह बंद कर दिया गया urllib.request.urlopen()है, पुराने से मेल खाती है urllib2.urlopen।", डॉक्स और यह धागा

ध्यान दें:

  • अजगर 3 में, मुद्रित आउटपुट लाइनों इसलिए तरह दिखेगा: b'some text'। यह अपेक्षित है, क्योंकि वे तार नहीं हैं - याद रखें, हम एक बायस्ट्रीम पढ़ रहे हैं। Dan04 के उत्कृष्ट उत्तर पर एक नज़र डालें ।

कुछ छोटे बदलाव जो मैंने किए:

  • मैं डॉक्सwith ... as के zipfile = ...अनुसार उपयोग करता हूं ।
  • स्क्रिप्ट अब .namelist()ज़िप में सभी फ़ाइलों के माध्यम से चक्र और उनकी सामग्री को प्रिंट करने के लिए उपयोग करती है।
  • मैंने कथन ZipFileमें वस्तु के निर्माण को स्थानांतरित किया with, हालांकि मुझे यकीन नहीं है कि अगर यह बेहतर है।
  • मैंने NumenorForLife की टिप्पणी के जवाब में फ़ाइल (ज़िप में प्रति फ़ाइल) लिखने के लिए एक विकल्प जोड़ा (और टिप्पणी की); यह "unzipped_and_read_"फ़ाइल नाम और एक ".file"एक्सटेंशन की शुरुआत में जोड़ता है (मुझे पसंद है कि ".txt"बाइटस्ट्रेस के साथ फ़ाइलों का उपयोग न करें )। यदि आप इसका उपयोग करना चाहते हैं तो कोड की इंडेंटिंग को निश्चित रूप से समायोजित करने की आवश्यकता है।
    • यहां सावधान रहने की आवश्यकता है - क्योंकि हमारे पास एक बाइट स्ट्रिंग है, हम बाइनरी मोड का उपयोग करते हैं, इसलिए "wb"; मुझे ऐसा लग रहा है कि बाइनरी लिखने से किसी भी तरह के कीड़े हो सकते हैं ...
  • मैं एक उदाहरण फ़ाइल, UN / LOCODE टेक्स्ट संग्रह का उपयोग कर रहा हूं :

मैंने क्या नहीं किया:

  • NumenorForLife ने जिप को डिस्क में सेव करने के बारे में पूछा। मुझे यकीन नहीं है कि वह इसका क्या मतलब है - ज़िप फ़ाइल डाउनलोड करना? यह एक अलग काम है; देखिए ओलेह प्रिनपिन का शानदार जवाब

यहाँ एक तरीका है:

import urllib.request
import shutil

with urllib.request.urlopen("http://www.unece.org/fileadmin/DAM/cefact/locode/2015-2_UNLOCODE_SecretariatNotes.pdf") as response, open("downloaded_file.pdf", 'w') as out_file:
    shutil.copyfileobj(response, out_file)

यदि आप सभी फाइलों को लिखना चाहते हैं तो आसान तरीका है कि आप लूपिंग के बजाय my_zip_file.extractall ('my_target') का उपयोग करें। लेकिन यह बहुत अच्छा है!
MCMZL

क्या आप मुझे इस सवाल में मदद कर सकते हैं: stackoverflow.com/questions/62417455/…
हर्षित कक्कड़

18

एक अस्थायी फ़ाइल में लिखें जो RAM में रहती है

यह पता चला है कि tempfileमॉड्यूल ( http://docs.python.org/library/tempfile.html ) में बस बात है:

tempfile। छलनी

यह फ़ंक्शन ठीक उसी तरह से काम करता है जैसा कि TemporaryFile () करता है, सिवाय इसके कि डेटा को मेमोरी में स्पूल किया जाता है जब तक कि फ़ाइल का आकार max_size से अधिक नहीं हो जाता है, या जब तक कि फाइल का फाइलो () विधि नहीं कहा जाता है, तब तक अस्थायी डिस्क के साथ सामग्री डिस्क और ऑपरेशन के लिए लिखी जाती है। ()।

परिणामी फ़ाइल में एक अतिरिक्त विधि, रोलओवर () है, जिसके कारण फ़ाइल अपने आकार की परवाह किए बिना ऑन-डिस्क फ़ाइल को रोल करती है।

लौटी हुई वस्तु एक फ़ाइल की तरह की वस्तु है जिसका _file गुण या तो एक स्ट्रिंग ऑब्जेक्ट या एक सच्ची फ़ाइल ऑब्जेक्ट है, जो इस बात पर निर्भर करता है कि रोलओवर () कहा गया है। इस फाइल की तरह की वस्तु का उपयोग सामान्य फाइल की तरह ही स्टेटमेंट के साथ किया जा सकता है।

संस्करण 2.6 में नया।

या यदि आप आलसी हैं और आपके पास /tmpलिनक्स पर एक टैम्पो-माउंटेड है, तो आप बस वहां एक फ़ाइल बना सकते हैं, लेकिन आपको इसे स्वयं हटाना होगा और नामकरण से निपटना होगा


3
+1 - SpooledTemporaryFile के बारे में नहीं जानता था। मेरा झुकाव अभी भी स्ट्रिंगियो का स्पष्ट रूप से उपयोग करना होगा, लेकिन यह जानना अच्छा है।
प्रेषक

16

मैं पूर्णता के लिए अपने पायथन 3 उत्तर को जोड़ना चाहूंगा:

from io import BytesIO
from zipfile import ZipFile
import requests

def get_zip(file_url):
    url = requests.get(file_url)
    zipfile = ZipFile(BytesIO(url.content))
    zip_names = zipfile.namelist()
    if len(zip_names) == 1:
        file_name = zip_names.pop()
        extracted_file = zipfile.open(file_name)
        return extracted_file
    return [zipfile.open(file_name) for file_name in zip_names]

14

अनुरोधों का उपयोग करके अन्य उत्तरों को जोड़ना :

 # download from web

 import requests
 url = 'http://mlg.ucd.ie/files/datasets/bbc.zip'
 content = requests.get(url)

 # unzip the content
 from io import BytesIO
 from zipfile import ZipFile
 f = ZipFile(BytesIO(content.content))
 print(f.namelist())

 # outputs ['bbc.classes', 'bbc.docs', 'bbc.mtx', 'bbc.terms']

एक्स्ट्रेक्ट () के लिए अधिक फ़ंक्शन विवरण प्राप्त करने के लिए मदद (एफ) का उपयोग करें जो ज़िप फ़ाइल में सामग्री को निकालता है जिसे बाद में खुले के साथ उपयोग किया जा सकता है ।


अपना CSV पढ़ने के लिए, करें:with f.open(f.namelist()[0], 'r') as g: df = pd.read_csv(g)
कोरी लेविंसन

3

विशाल का उदाहरण, हालांकि महान है, जब यह फ़ाइल नाम में आता है, तो भ्रमित करता है, और मुझे 'zipfile' को फिर से परिभाषित करने की योग्यता नहीं दिखती है।

यहाँ मेरा उदाहरण है कि एक ज़िप को डाउनलोड किया जाता है जिसमें कुछ फाइलें होती हैं, जिनमें से एक csv फाइल है जिसे मैंने बाद में एक पांडा डेटा में पढ़ा है:

from StringIO import StringIO
from zipfile import ZipFile
from urllib import urlopen
import pandas

url = urlopen("https://www.federalreserve.gov/apps/mdrm/pdf/MDRM.zip")
zf = ZipFile(StringIO(url.read()))
for item in zf.namelist():
    print("File in zip: "+  item)
# find the first matching csv file in the zip:
match = [s for s in zf.namelist() if ".csv" in s][0]
# the first line of the file contains a string - that line shall de ignored, hence skiprows
df = pandas.read_csv(zf.open(match), low_memory=False, skiprows=[0])

(ध्यान दें, मैं 2.7.13 पायथन का उपयोग करता हूं)

यह सटीक समाधान है जो मेरे लिए काम करता है। मैंने अभी इसे स्ट्रिंग 3 के लिए पायथन 3 संस्करण के लिए थोड़ा सा ट्विक किया और स्ट्रिंग लाइब्रेरी को जोड़कर

पायथन 3 संस्करण

from io import BytesIO
from zipfile import ZipFile
import pandas
import requests

url = "https://www.nseindia.com/content/indices/mcwb_jun19.zip"
content = requests.get(url)
zf = ZipFile(BytesIO(content.content))

for item in zf.namelist():
    print("File in zip: "+  item)

# find the first matching csv file in the zip:
match = [s for s in zf.namelist() if ".csv" in s][0]
# the first line of the file contains a string - that line shall de     ignored, hence skiprows
df = pandas.read_csv(zf.open(match), low_memory=False, skiprows=[0])

1

यह विशाल के जवाब में स्पष्ट नहीं था कि फाइल का नाम उन मामलों में होना चाहिए जहां डिस्क पर कोई फ़ाइल नहीं है। मैंने अधिकांश आवश्यकताओं के लिए संशोधन के बिना काम करने के लिए उनके जवाब को संशोधित किया है।

from StringIO import StringIO
from zipfile import ZipFile
from urllib import urlopen

def unzip_string(zipped_string):
    unzipped_string = ''
    zipfile = ZipFile(StringIO(zipped_string))
    for name in zipfile.namelist():
        unzipped_string += zipfile.open(name).read()
    return unzipped_string

यह पायथन 2 का उत्तर है।
बोरिस

0

zipfileमॉड्यूल का उपयोग करें । URL से फ़ाइल निकालने के लिए, आपको urlopenकिसी BytesIOऑब्जेक्ट में कॉल का परिणाम लपेटना होगा । इसका कारण यह है कि वेब अनुरोध द्वारा लौटाए गए अनुरोध का परिणाम urlopenनहीं है:

from urllib.request import urlopen

from io import BytesIO
from zipfile import ZipFile

zip_url = 'http://example.com/my_file.zip'

with urlopen(zip_url) as f:
    with BytesIO(f.read()) as b, ZipFile(b) as myzipfile:
        foofile = myzipfile.open('foo.txt')
        print(foofile.read())

यदि आपके पास पहले से स्थानीय रूप से डाउनलोड की गई फ़ाइल है, तो आपको इसकी आवश्यकता नहीं है BytesIO, बस इसे बाइनरी मोड में खोलें और ZipFileसीधे पास करें :

from zipfile import ZipFile

zip_filename = 'my_file.zip'

with open(zip_filename, 'rb') as f:
    with ZipFile(f) as myzipfile:
        foofile = myzipfile.open('foo.txt')
        print(foofile.read().decode('utf-8'))

फिर, ध्यान दें कि आपको openफ़ाइल बाइनरी'rb' में दर्ज करनी है ( ) मोड , पाठ के रूप में नहीं या आपको एक zipfile.BadZipFile: File is not a zip fileत्रुटि मिलेगी ।

withबयान के साथ संदर्भ प्रबंधकों के रूप में इन सभी चीजों का उपयोग करना अच्छा है , ताकि वे ठीक से बंद हो जाएं।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.