Как оптимизировать (в том числе с точки зрения ОЗУ) код, который сохраняет слова из 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 сохранять ссылку на все эти данные, когда они ему не нужны.