Подтвердить что ты не робот

Файл соленья слишком большой для загрузки

Проблема, с которой я сталкиваюсь, заключается в том, что у меня есть очень большой файл pickle (2.6 Gb), который я пытаюсь открыть, но каждый раз, когда я делаю это, я получаю ошибку памяти. Теперь я понимаю, что я должен был использовать базу данных для хранения всей информации, но ее слишком поздно. Файл pickle содержит даты и текст из записи в Конгрессе США, которая была просканирована из Интернета (заняло около 2 недель для запуска). Есть ли способ получить доступ к информации, которую я сбрасывал в файл pickle, поэтапно, или преобразовать файл pickle в базу данных sql или что-то еще, что я могу открыть без повторного ввода всех данных. Я действительно не хочу тратить еще 2 недели на повторное обследование конгресса и вменение данных в базу данных.

Спасибо за вашу помощь

ИЗМЕНИТЬ *

код для того, как объект маринован:

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)

Это программа с ошибкой:

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

Ответ 1

Похоже, ты немного рассол!;-). Надеюсь, после этого вы НИКОГДА НЕ ИСПОЛЬЗУЙТЕ PICKLE EVER. Это не очень хороший формат хранения данных.

В любом случае, для этого ответа я предполагаю, что ваш класс Document выглядит примерно так. Если нет, прокомментируйте свой фактический класс Document:

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

В любом случае, я сделал несколько простых тестовых данных с этим классом:

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

Заманили его в формате 2 (pickle.HIGHEST_PROTOCOL для 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.'

И разобрал его с помощью 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

Выглядит сложным! Но на самом деле, это не так уж плохо. pickle - это в основном машина стека, каждый идентификатор ALL_CAPS, который вы видите, является кодом операции, который каким-то образом управляет внутренним "стеком" для декодирования. Если бы мы пытались разобрать некоторую сложную структуру, это было бы более важно, но, к счастью, мы просто составляем простой список по существу кортежей. Весь этот "код" выполняет построение кучи объектов в стеке, а затем нажатие всего стека в список.

Единственное, о чем мы должны заботиться, - это коды "BINPUT" / "BINGET", которые вы видите разбросанными вокруг. В основном, это для "memoization", для уменьшения объема данных, pickle сохраняет строки с помощью BINPUT <id>, а затем, если они появляются снова, вместо повторного сброса их, просто помещает BINGET <id> для извлечения их из кэш.

Кроме того, еще одно осложнение! Там больше, чем просто SHORT_BINSTRING - там нормальный BINSTRING для строк > 256 байт, а также некоторые забавные юникодные варианты. Я просто предполагаю, что вы используете Python 2 со всеми строками ASCII. Опять же, комментируйте, если это неверное предположение.

ОК, поэтому нам нужно передать файл до тех пор, пока мы не нажмем байты '\ 81' (NEWOBJ). Затем нам нужно отсканировать вперед, пока мы не нажмем символ '(' (MARK). Затем, пока мы не нажмем "u" (SETITEMS), мы будем читать пары строк ключа/значения - должно быть 3 пары всего, по одному для каждого поля.

Итак, давайте сделаем это. Здесь my script для чтения рассолов данных в потоковом режиме. Это далеко не идеально, так как я просто взломал его для ответа, и вам нужно будет его модифицировать, но это хороший старт.

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

Это правильно читает мои тестовые данные в формате pickle 2 (изменено на длинную строку):

$ 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'}

Удачи!

Ответ 2

Вы не увеличивали свои данные постепенно. Вы мариновали свои данные монолитно и многократно. Каждый раз вокруг цикла вы уничтожали любые выходные данные, которые у вас были (open(...,'wb') уничтожает выходной файл), и снова перезаписывали все данные. Кроме того, если ваша программа когда-либо остановилась, а затем перезапущена с новыми входными данными, старые выходные данные были потеряны.

Я не знаю, почему objects не вызывал ошибку вне памяти во время травления, потому что она росла до того же размера, что и объект, который pickle.load() хочет создать.

Вот как вы могли бы создать файл pickle постепенно:

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)

Затем вы могли бы постепенно считывать файл pickle следующим образом:

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

Возможные варианты:

  • Попробуйте cPickle. Это может помочь.
  • Попробуйте раскачку потока
  • Прочитайте файл pickle в 64-битной среде с большим количеством оперативной памяти.
  • Повторно сканируйте исходные данные, на этот раз фактически инкрементно сохраняя данные или сохраняя их в базе данных. Без неэффективности постоянно переписывания выходного файла рассола, на этот раз ваш обход может значительно ускориться.

Ответ 3

У меня был очень похожий случай в последнее время - 11 ГБ. Я не пытался загружать его на свою машину постепенно, так как у меня не было достаточно времени, чтобы реализовать собственный инкрементальный загрузчик или улучшить существующие для моего случая.

Я запустил большой экземпляр с достаточным объемом памяти у провайдера облачного хостинга (цена невелика, если запускать только в течение небольшого промежутка времени, например, нескольких часов), загрузил этот файл через SSH (SCP) на этот сервер и просто загрузил это в этом случае, чтобы проанализировать это там + переписать это в более подходящий формат.

Не программное решение, но эффективное по времени (без особых усилий).