Как оптимизировать (в том числе с точки зрения ОЗУ) код, который сохраняет слова из PDF в объект Python, а затем в базу данных?

Я ищу наиболее эффективный способ сохранения текста из файлов PDF в свою базу данных. В настоящее время я использую pdfplumber со стандартным кодом, который выглядит следующим образом:

      my_string = ''

with pdfplumber.open(text_file_path) as pdf:
    for page in pdf.pages:
        if page.extract_text():
            my_string += str(page.extract_text().replace('\n', ' ').split(' '))

Но текущий код буквально убивает мою машину (для PDF с 600 страницами требуется от 3 до 6 ГБ ОЗУ), и моя цель - разместить его на мобильных телефонах.

Я провел несколько тестов, и мне кажется, что чтение PDF не проблема, но сохранить или сохранить эти слова проблематично. Я попытался создать dict, где каждая строка страницы представляет собой один ключ / значение, но это было не намного лучше.

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

Буду благодарен за любые подсказки, спасибо!

РЕДАКТИРОВАТЬ:

          with pdfplumber.open(text_file_path) as pdf:
        for page in pdf.pages:
            connection = sqlite3.connect('my_db.db')
            cursor = connection.cursor()
            cursor.execute("INSERT INTO temp_text VALUES (?, ?)",
                           (text_file_path, str(page.extract_text()).replace('\n', ' ')))
            connection.commit()
            connection.close()

Я изменил код на этот, и он стал немного лучше (теперь он занимает около 2,9 ГБ ОЗУ), но это все еще много. Могу я сделать что-нибудь еще?

1 ответ

Решение

Проблема в том, что вы храните данные в течение длительного времени, а это означает, что по мере того, как вы постепенно обрабатываете все больше и больше данных, вы по-прежнему ссылаетесь на все это в памяти. Это то, что база данных стремится предотвратить: любое эффективное хранение и извлечение данных без необходимости хранить все это в ОЗУ. Вот простой пример использования PyMongo (для приложения iOS вы, вероятно, захотите использовать SQLite):

      import pdfplumbder
import poymongo
import os


def process_file(path, collection):
    '''Process a single file, from a path.'''

    basename = os.path.splitext(os.path.basename(path))[0]
    with pdfplumber.open(path) as pdf:
        for index, page in enumerate(pdf.pages):
            # Don't store any long-term references to the data
            text = page.extract_text()
            data = { 'text': text, 'filename': basename, 'page': index }
            collection.insert_one(data)

def main(paths):
    '''Just a dummy entry point, pass args normally here.'''

    client = pymongo.MongoClient('localhost', 27017)
    database = client['myapp']
    collection = database['pdfs']
    # Sort by filename, then by page.
    collection.create_index([('filename', 1), ('page', 1)])
    for path in paths:
        process_file(path, collection)

    # Do what you want here

Как видите, мы создаем соединение с локальным клиентом, создаем или получаем доступ к базе данных, которую используем, и создаем коллекцию для хранения PDF. Затем мы индексируем по имени файла, а затем по номеру страницы.

Затем мы перебираем все пути и итеративно их обрабатываем. Мы не храним текст более чем для одной страницы за раз и записываем данные в базу данных каждый цикл. С точки зрения производительности это может быть неоптимальным (хотя движок все равно может оптимизировать это прилично), но он минимизирует требуемую память.

Избегайте использования глобального состояния, которое обрабатывает много гигабайт данных: вы заставляете Python сохранять ссылку на все эти данные, когда они ему не нужны.

Другие вопросы по тегам