Работа с файлами без записи на физический диск
В большинстве случаев файлы внутри zip-файла должны быть извлечены в первую очередь для обработки. Но это можно сделать в памяти. Дорожное препятствие заключается в том, как вызвать утилиту, которая использует только путь сопоставленной файловой системы в качестве аргумента для обработки текста в заархивированных файлах без записи на физический диск.
Внутренне textract
вызывает утилиту командной строки (antiword ), который делает фактическое извлечение текста. Таким образом, подход, который решает эту проблему, может быть применен в целом к другим инструментам командной строки, которым необходим доступ к zip-содержимому через путь файловой системы.
Ниже приведено несколько возможных решений, чтобы обойти это ограничение для файлов:
- Монтирование RAM-диска.
- Это работает хорошо, но требует подсказки
sudo
, но это можно автоматизировать.
- Смонтируйте zip-файл в файловую систему. (хороший вариант)
- Хорошим Linux инструментом для монтажа является
fuse-zip
.
- Используйте модуль
tempfile
. (самый простой) - Обеспечивает автоматическое удаление файлов.
- Недостаток: файлы могут быть записаны на диск.
- Доступ к XML в. DOCX файлы.
- Может выполнять регулярное выражение через raw XML или использовать читатель XML.
- Хотя только небольшая часть ваших файлов является .docx.
- Найдите еще один экстрактор. (не охвачено)
- Я посмотрел и ничего не смог найти.
docx2txt
- это еще один модуль Python, но похоже, что он будет обрабатывать только файлы .docx (как его имя подразумевается), а не старые файлы Word .do c.
Почему я сделал всю эту работу, вы можете спросить. Я действительно нашел это полезным для одного из моих собственных проектов.
1) RAM Drive
Если tempfile
не удовлетворяет файл ограничивающие цели, и вы хотите убедиться, что все файлы, используемые инструментом, находятся в ОЗУ, создание ОЗУ является отличным вариантом. После завершения работы инструмент должен отключить диск, что приведет к удалению всех сохраненных файлов.
Плюсом этой опции является то, что все системы Linux поддерживают это изначально. Это не влечет за собой никаких дополнительных программных зависимостей; по крайней мере для Linux, Windows, вероятно, потребуется ImDisk.
Это соответствующие команды bash для Linux:
$ mkdir ./temp_drive
$ sudo mount -t tmpfs -o size=512m temp_drive ./temp_drive
$
$ mount | tail -n 1 # To see that it was mounted.
$ sudo umount ./temp_drive # To unmount.
В MacOS:
$ diskutil erasevolume HFS+ 'RAM Disk' `hdiutil attach -nomount ram://1048576 `
$ # 512M drive created: 512 * 2048 == 1048576
Вкл. Windows:
Вкл. Windows, возможно, вам придется использовать стороннее приложение, например ImDisk:
Чтобы автоматизировать процесс, этот короткий сценарий запрашивает у пользователя пароль sudo, затем вызывает mount
для создания ОЗУ:
import subprocess as sp
import tempfile
import platform
import getpass
ramdrv = tempfile.TemporaryDirectory()
if platform.system() == 'Linux':
sudo_pw = getpass.getpass("Enter sudo password: ")
# Mount RAM drive on Linux.
p = sp.Popen(['sudo', '-S', 'bash', '-c',
f"mount -t tmpfs -o size=512m tmpfs {ramdrv.name}"],
stderr=sp.STDOUT, stdout=sp.PIPE, stdin=sp.PIPE, bufsize=1,
encoding='utf-8')
print(sudo_pw, file=p.stdin)
del sudo_pw
print(p.stdout.readline())
elif platform.system() == 'Darwin':
# And so on...
В любом пакете GUI, используемом вашим приложением, скорее всего, есть диалоговое окно ввода пароля, но getpass
хорошо работает для консольных приложений.
Кому получить доступ к диску RAM, использовать папку, в которой он смонтирован, как и любой другой файл в системе. Записывать в него файлы, читать из него файлы, создавать подпапки и т. Д. c.
2) Смонтировать Zip-файл
Если Zip-файл может быть смонтирован в файловой системе ОС, тогда его файлы будут иметь пути, которые можно передать в textract
. Это может быть лучшим вариантом.
Для Linux, хорошо работающая утилита - fuse-zip
. В нескольких строках ниже установите его и смонтируйте zip-файл.
$ sudo apt-get install fuse-zip
...
$ mkdir ~/archivedrive
$
$ fuse-zip ~/myarchive.zip ~/archivedrive
$ cd ~/archivedrive/myarchive # I'm inside the zip!
Начиная с Python, создайте временную точку монтирования, смонтируйте zip, извлеките текст, затем размонтируйте zip:
>>> import subprocess as sp, tempfile, textract
>>>
>>> zf_path = '/home/me/marine_life.zip'
>>> zipdisk = tempfile.TemporaryDirectory() # Temp mount point.
>>>
>>> cp = sp.run(['fuse-zip', zf_path, zipdisk.name]) # Mount.
>>> cp.returncode
0
>>> all_text = textract.process(f"{zipdisk.name}/marine_life/octopus.doc")
>>>
>>> cp = sp.run(['fusermount', '-u', zipdisk.name]) # Unmount.
>>> cp.returncode
0
>>> del zipdisk # Delete mount point.
>>> all_text[:88]
b'The quick Octopuses live in every ocean, and different species have\n
adapted to different'
>>>
>>> # Convert bytes to str if needed.
>>> as_string = all_text.decode('latin-1', errors='replace')
Большим плюсом использования этого подхода является то, что для монтирования архива не требуется sudo
- пароль не требуется. Единственным недостатком было бы то, что он добавляет зависимость к проекту. Вероятно, не главная проблема. Автоматизировать монтаж и демонтаж должно быть легко с subprocess.run()
.
Я считаю, что конфигурация по умолчанию для дистрибутивов Linux позволяет пользователям монтировать файловые системы Fuse без использования sudo
; но это необходимо проверить для поддерживаемых целей.
Для Windows ImDisk также может монтировать архивы и имеет интерфейс командной строки. Так что это может быть автоматизировано для поддержки Windows. Подход XML и этот подход хороши тем, что они получают информацию непосредственно из zip-файла без дополнительного этапа записи ее в файл.
Относительно кодировки символов: я сделал предположение в примере в старых документах Word в Восточной Европе, выпущенных до 2006 года, может использоваться некоторая кодировка, отличная от utf-8 (iso-8859-2, latin-1, windows -1250, cyrilli c, et c.). Возможно, вам придется немного поэкспериментировать, чтобы убедиться, что каждый из файлов правильно преобразован в строки.
Ссылки:
3) tempfile.NamedTemporaryFile
Этот подход не требует специальных разрешений. Это должно просто работать. Однако гарантируется, что создаваемые файлы не будут находиться только в памяти.
Если проблема заключается в том, что ваш инструмент перенасыщет диски пользователей файлами, такой подход предотвратит это. Временные файлы надежно удаляются автоматически.
Пример кода для создания NamedTemporaryFile
, открытия zip-файла и извлечения из него файла, а затем передачи его пути к textract
.
>>> zf = zipfile.ZipFile('/temp/example.docx')
>>> wf = zf.open('word/document.xml')
>>> tf = tempfile.NamedTemporaryFile()
>>>
>>> for line in wf:
... tf.file.write(line)
>>>
>>> tf.file.seek(0)
>>> textract.process(tf.name)
# Lines and lines of text dumped to screen - it worked!
>>> tf.close()
>>>
>>> # The file disappears.
Вы можете многократно использовать один и тот же объект NamedTemporaryFile
, используя tf.seek(0)
для сброса его положения.
Не закрывайте файл, пока не закончите с ним. Когда вы закроете его, оно исчезнет sh. Экземпляры NamedTemporaryFile
автоматически удаляются при закрытии, их повторный счет становится равным 0 или ваша программа завершает работу.
Опция, если вы хотите иметь временную папку, которая гарантированно исчезнет после завершения вашей программы, будет tempfile.TemporaryDirectory
.
В том же модуле tempfile.SpooledTemporaryFile
- это файл, который существует в памяти. Однако, путь к ним трудно найти (мы знаем только их файловый дескриптор). И если вы найдете хороший способ получить путь, этот путь не будет использоваться textract
.
textract
выполняется в отдельном процессе, но он наследует файловые дескрипторы родительского объекта. Это то, что позволяет обмениваться этими временными файлами между двумя.
4) Извлечение текста Word.docx через XML
Этот подход пытается Удалите необходимость использования сторонней утилиты, выполнив работу в Python или воспользовавшись другим инструментом, для которого не требуются пути FS.
.Docx-файлы в zip-файлах также являются zip-файлами, содержащими XML. XML - это текст, который может быть проанализирован в необработанном виде с помощью регулярных выражений или передан читателю XML вначале.
Модуль Python, docx2txt
делает почти то же самое, что и во втором примере ниже. Я посмотрел на его источники, и он открывает документ Word в виде zip-файла и использует синтаксический анализатор XML для получения текстовых узлов. Он не будет работать по тем же причинам, что и этот подход.
Два приведенных ниже примера читают файл непосредственно из архива .docx - файл не извлекается на диск.
Если если вы хотите преобразовать необработанный текст XML в словарь и списки, вы можете использовать xmltodict
:
import zipfile
import xmltodict
zf = zipfile.ZipFile('/temp/example.docx')
data = xmltodict.parse(zf.open('word/document.xml'))
some_text = data['w:document']['w:body']['w:p'][46]['w:r']['w:t']
print(some_text)
Я нашел этот формат немного громоздким из-за сложной структуры вложенности XML элементов, и это не дает вам преимуществ, которые читатель XML делает в плане определения местоположения узлов.
Используя xml.etree.ElementTree
, выражение XPATH может извлечь все текстовые узлы за один снимок.
import re
import xml.etree.ElementTree as ET
import zipfile
_NS_DICT = {'w': 'http://schemas.openxmlformats.org/wordprocessingml/2006/main'}
def get_docx_text(docx_path):
"""
Opens the .docx file at 'docx_path', parses its internal document.xml
document, then returns its text as one (possibly large) string.
"""
with zipfile.ZipFile(docx_path) as zf:
tree = ET.parse(zf.open('word/document.xml'))
all_text = '\n'.join(n.text for n in tree.findall('.//w:t', _NS_DICT))
return all_text
Использование модуля xml.etree.ElementTree
, как указано выше, делает возможным извлечение текста всего за несколько строк кода.
В get_docx_text()
эта строка захватывает весь текст:
all_text = '\n'.join(n.text for n in tree.findall('.//w:t', _NS_DICT))
Строка: './/w:t'
- это выражение XPATH, которое указывает модулю выбрать все t
(текст ) узлы документа Word. Затем понимание списка объединяет весь текст.
Получив текст, возвращенный из get_docx_text()
, вы можете применять свои регулярные выражения, построчно перебирать его или делать все, что вам нужно. Пример выражения re
захватывает все фразы в скобках.
Ссылки
Файловая система Fuse: https://github.com/libfuse/libfuse
Страница справочника на молнии: https://linux.die.net/man/1/fuse-zip
Предохранитель MacOS: https://osxfuse.github.io/
ImDisk (Windows): http://www.ltr-data.se/opencode.html/#ImDisk
Список программного обеспечения привода ОЗУ: https://en.wikipedia.org/wiki/List_of_RAM_drive_software
Формат файла MS docx: https://wiki.fileformat.com/word-processing/docx/
xml .ElementTree do c: https://docs.python.org/3/library/xml.etree.elementtree.html?highlight=xml%20etree#module - xml .etree.ElementTree
XPATH: https://docs.python.org/3/library/xml.etree.elementtree.html?highlight=xml%20etree#elementtree -xpath
Пример XML заимствовал некоторые идеи у: https://etienned.github.io/posts/extract-text-from-word-docx-simply/