2014-10-16 1 views
13

Il problema che sto avendo è che ho un file pickle molto grande (2.6 Gb) che sto cercando di aprire ma ogni volta che faccio così ottengo un errore di memoria. Ora mi rendo conto che avrei dovuto usare un database per archiviare tutte le informazioni, ma è troppo tardi adesso. Il file pickle contiene le date e il testo del registro del Congresso degli Stati Uniti che è stato scansionato da Internet (sono state necessarie circa 2 settimane per essere eseguito). C'è un modo per accedere alle informazioni che ho scaricato nel file pickle in modo incrementale o un modo per convertire il file pickle in un database SQL o qualcos'altro che posso aprire senza dover re-inserire tutti i dati. Non voglio davvero passare altre 2 settimane a riannodare il record del Congresso e imputare i dati in un database.Pickle File troppo grande per caricare

Grazie mille per il vostro aiuto

EDIT *

codice

per come l'oggetto viene decapato:

def save_objects(objects): 
    with open('objects.pkl', 'wb') as output: 
     pickle.dump(objects, output, pickle.HIGHEST_PROTOCOL) 

def Main(): 
    Links() 
    file = open("datafile.txt", "w") 
    objects=[] 
    with open('links2.txt', 'rb') as infile: 
     for link in infile: 
      print link 
      title,text,date=Get_full_text(link) 
      article=Doccument(title,date,text) 
      if text != None: 
       write_to_text(date,text) 
       objects.append(article) 
       save_objects(objects) 

Questo è il programma con l'errore:

def Main(): 
    file= open('objects1.pkl', 'rb') 
    object = pickle.load(file) 
+0

Quanta RAM ha la macchina che esegue il programma? – IanAuld

+0

Usa sqlite, per decapare un file da 2.6 Gb è abbastanza vicino alla pazzia. Sqlite è abbastanza facile da aggirare;) – brunsgaard

+0

Condividi i dati e me ne interromperò la distribuzione o noleggi una grande macchina AWS per il lavoro. Sono abbastanza sicuro che @IanAuld abbia ragione ... ti manca la memoria. – brunsgaard

risposta

8

Non hai decodificato i tuoi dati in modo incrementale. Hai decapitato i tuoi dati in modo monolitico e ripetitivo. Ogni volta che cerchi il loop, hai distrutto tutti i dati di output che avevi (open(...,'wb') distrugge il file di output) e riscritto tutti i dati di nuovo. Inoltre, se il programma si fermava e si riavviava con nuovi dati di input, i vecchi dati di output andavano persi.

Non so perché objects non ha causato un errore di memoria esaurita durante il decollo, poiché ha raggiunto la stessa dimensione dell'oggetto che pickle.load() desidera creare.

Ecco come si potrebbe aver creato il file salamoia in modo incrementale:

def save_objects(objects): 
    with open('objects.pkl', 'ab') as output: # Note: `ab` appends the data 
     pickle.dump(objects, output, pickle.HIGHEST_PROTOCOL) 

def Main(): 
    ... 
    #objects=[] <-- lose the objects list 
    with open('links2.txt', 'rb') as infile: 
     for link in infile: 
      ... 
      save_objects(article) 

Poi si potrebbe avere in modo incrementale leggere il file di salamoia in questo modo:

import pickle 
with open('objects.pkl', 'rb') as pickle_file: 
    try: 
     while True: 
      article = pickle.load(pickle_file) 
      print article 
    except EOFError: 
     pass 

Le scelte mi vengono in mente sono:

  • Prova cPickle. Potrebbe aiutare.
  • Prova lo streaming-salamoia
  • leggere il file sottaceto in un ambiente a 64 bit con un sacco e un sacco di RAM
  • Re-crawl i dati originali, questa volta in realtà in modo incrementale la memorizzazione dei dati, o la memorizzazione in un database . Senza l'inefficienza della costante riscrittura del file di output pickle, la scansione potrebbe essere notevolmente più veloce questa volta.
+1

ringrazia un mucchio. Ho eseguito il crawler su un computer e poi ho provato a visualizzare il file pickle su un'altra macchina. Funziona sulla macchina originale che ha più memoria. –

+5

Oh, allora la risposta è ovvia: estrai i dati dal file pickle sul computer originale. –

29

Sembra che tu sia un po 'sottaceto! ;-). Spero che dopo questo, non userete MAI MAI IL MAIALE. Non è un ottimo formato di archiviazione dei dati.

In ogni caso, per questa risposta presumo che la tua classe Document assomigli un po 'a questo.In caso contrario, lasciare un commento con il vostro attuale classe Document:

class Document(object): # <-- object part is very important! If it's not there, the format is different! 
    def __init__(self, title, date, text): # assuming all strings 
     self.title = title 
     self.date = date 
     self.text = text 

In ogni modo, ho fatto alcuni dati di test semplici con questa classe:

d = [Document(title='foo', text='foo is good', date='1/1/1'), Document(title='bar', text='bar is better', date='2/2/2'), Document(title='baz', text='no one likes baz :(', date='3/3/3')] 

in salamoia con formato 2 (pickle.HIGHEST_PROTOCOL per Python 2.x)

>>> s = pickle.dumps(d, 2) 
>>> s 
'\x80\x02]q\x00(c__main__\nDocument\nq\x01)\x81q\x02}q\x03(U\x04dateq\x04U\x051/1/1q\x05U\x04textq\x06U\x0bfoo is goodq\x07U\x05titleq\x08U\x03fooq\tubh\x01)\x81q\n}q\x0b(h\x04U\x052/2/2q\x0ch\x06U\rbar is betterq\rh\x08U\x03barq\x0eubh\x01)\x81q\x0f}q\x10(h\x04U\x053/3/3q\x11h\x06U\x13no one likes baz :(q\x12h\x08U\x03bazq\x13ube.' 

e smontato con pickletools:

>>> pickletools.dis(s) 
    0: \x80 PROTO  2 
    2: ] EMPTY_LIST 
    3: q BINPUT  0 
    5: ( MARK 
    6: c  GLOBAL  '__main__ Document' 
    25: q  BINPUT  1 
    27:)  EMPTY_TUPLE 
    28: \x81  NEWOBJ 
    29: q  BINPUT  2 
    31: }  EMPTY_DICT 
    32: q  BINPUT  3 
    34: (  MARK 
    35: U   SHORT_BINSTRING 'date' 
    41: q   BINPUT  4 
    43: U   SHORT_BINSTRING '1/1/1' 
    50: q   BINPUT  5 
    52: U   SHORT_BINSTRING 'text' 
    58: q   BINPUT  6 
    60: U   SHORT_BINSTRING 'foo is good' 
    73: q   BINPUT  7 
    75: U   SHORT_BINSTRING 'title' 
    82: q   BINPUT  8 
    84: U   SHORT_BINSTRING 'foo' 
    89: q   BINPUT  9 
    91: u   SETITEMS (MARK at 34) 
    92: b  BUILD 
    93: h  BINGET  1 
    95:)  EMPTY_TUPLE 
    96: \x81  NEWOBJ 
    97: q  BINPUT  10 
    99: }  EMPTY_DICT 
    100: q  BINPUT  11 
    102: (  MARK 
    103: h   BINGET  4 
    105: U   SHORT_BINSTRING '2/2/2' 
    112: q   BINPUT  12 
    114: h   BINGET  6 
    116: U   SHORT_BINSTRING 'bar is better' 
    131: q   BINPUT  13 
    133: h   BINGET  8 
    135: U   SHORT_BINSTRING 'bar' 
    140: q   BINPUT  14 
    142: u   SETITEMS (MARK at 102) 
    143: b  BUILD 
    144: h  BINGET  1 
    146:)  EMPTY_TUPLE 
    147: \x81  NEWOBJ 
    148: q  BINPUT  15 
    150: }  EMPTY_DICT 
    151: q  BINPUT  16 
    153: (  MARK 
    154: h   BINGET  4 
    156: U   SHORT_BINSTRING '3/3/3' 
    163: q   BINPUT  17 
    165: h   BINGET  6 
    167: U   SHORT_BINSTRING 'no one likes baz :(' 
    188: q   BINPUT  18 
    190: h   BINGET  8 
    192: U   SHORT_BINSTRING 'baz' 
    197: q   BINPUT  19 
    199: u   SETITEMS (MARK at 153) 
    200: b  BUILD 
    201: e  APPENDS (MARK at 5) 
    202: . STOP 

Sembra complesso! Ma davvero, non è così male. pickle è fondamentalmente una macchina stack, ogni identificatore ALL_CAPS che vedi è un codice operativo, che manipola lo "stack" interno in qualche modo per la decodifica. Se stessimo cercando di analizzare una struttura complessa, questo sarebbe più importante, ma per fortuna stiamo solo facendo una semplice lista di essenzialmente-tuple. Tutto questo "codice" sta facendo costruire un mucchio di oggetti nello stack e poi spingere l'intero stack in un elenco.

L'unica cosa di cui dobbiamo preoccuparci sono i codici "BINPUT"/"BINGET" che vedete sparsi in giro. Fondamentalmente, questi sono per 'memoization', per ridurre l'impronta dei dati, pickle salva stringhe con BINPUT <id>, e poi se vengono di nuovo, invece di ri-scaricarli, semplicemente mette uno BINGET <id> per recuperarli dalla cache.

Inoltre, un'altra complicazione! C'è oltre il SHORT_BINSTRING - c'è il normale BINSTRING per stringhe> 256 byte e anche alcune divertenti varianti Unicode. Immagino che tu stia usando Python 2 con tutte le stringhe ASCII. Di nuovo, commenta se questo non è un presupposto corretto.

OK, quindi è necessario eseguire lo streaming del file fino a quando non si preme un byte '\ 81' (NEWOBJ). Quindi, dobbiamo eseguire la scansione in avanti fino a quando non si preme un carattere '(' (MARK). Quindi, finché non si preme su 'u' (SETITEMS), leggiamo coppie di stringhe chiave/valore: ci dovrebbero essere 3 coppie in totale, una per ogni campo

Quindi, facciamo questo: ecco la mia sceneggiatura per leggere i dati di pickle in streaming. È tutt'altro che perfetto, dal momento che ho appena hackerato insieme per questa risposta, e avrete bisogno di modificarlo molto, ma è un buon inizio

pickledata = '\x80\x02]q\x00(c__main__\nDocument\nq\x01)\x81q\x02}q\x03(U\x04dateq\x04U\x051/1/1q\x05U\x04textq\x06U\x0bfoo is goodq\x07U\x05titleq\x08U\x03fooq\tubh\x01)\x81q\n}q\x0b(h\x04U\x052/2/2q\x0ch\x06T\x14\x05\x00\x00bar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterq\rh\x08U\x03barq\x0eubh\x01)\x81q\x0f}q\x10(h\x04U\x053/3/3q\x11h\x06U\x13no one likes baz :(q\x12h\x08U\x03bazq\x13ube.' 

# simulate a file here 
import StringIO 
picklefile = StringIO.StringIO(pickledata) 

import pickle # just for opcode names 
import struct # binary unpacking 

def try_memo(f, v, cache): 
    opcode = f.read(1) 
    if opcode == pickle.BINPUT: 
     cache[f.read(1)] = v 
    elif opcode == pickle.LONG_BINPUT: 
     print 'skipping LONG_BINPUT to save memory, LONG_BINGET will probably not be used' 
     f.read(4) 
    else: 
     f.seek(f.tell() - 1) # rewind 

def try_read_string(f, opcode, cache): 
    if opcode in [ pickle.SHORT_BINSTRING, pickle.BINSTRING ]: 
     length_type = 'b' if opcode == pickle.SHORT_BINSTRING else 'i' 
     str_length = struct.unpack(length_type, f.read(struct.calcsize(length_type)))[0] 
     value = f.read(str_length) 
     try_memo(f, value, memo_cache) 
     return value 
    elif opcode == pickle.BINGET: 
     return memo_cache[f.read(1)] 
    elif opcide == pickle.LONG_BINGET: 
     raise Exception('Unexpected LONG_BINGET? Key ' + f.read(4)) 
    else: 
     raise Exception('Invalid opcode ' + opcode + ' at pos ' + str(f.tell())) 

memo_cache = {} 
while True: 
    c = picklefile.read(1) 
    if c == pickle.NEWOBJ: 
     while picklefile.read(1) != pickle.MARK: 
      pass # scan forward to field instantiation 
     fields = {} 
     while True: 
      opcode = picklefile.read(1) 
      if opcode == pickle.SETITEMS: 
       break 
      key = try_read_string(picklefile, opcode, memo_cache) 
      value = try_read_string(picklefile, picklefile.read(1), memo_cache) 
      fields[key] = value 
     print 'Document', fields 
     # insert to sqllite 
    elif c == pickle.STOP: 
     break 

Questa legge correttamente i miei dati di test in formato salamoia 2 (modificato per avere una lunga serie):.

$ python picklereader.py 
Document {'date': '1/1/1', 'text': 'foo is good', 'title': 'foo'} 
Document {'date': '2/2/2', 'text': 'bar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is betterbar is better', 'title': 'bar'} 
Document {'date': '3/3/3', 'text': 'no one likes baz :(', 'title': 'baz'} 

Buona fortuna!