Извлечение текста из файлов MS Word в Python

Для работы с файлами MS Word в Python существуют расширения Python Win32, которые можно использовать в Windows. Как мне сделать то же самое в Linux? Есть ли библиотека?

Ответ 1

Вы можете сделать вызов подпроцесса для antiword. Antiword — это утилита командной строки Linux для выгрузки текста из документа word. Работает довольно хорошо для простых документов (очевидно, при этом теряется форматирование). Она доступна через apt, и, вероятно, в виде RPM, или вы можете скомпилировать ее самостоятельно.

Ответ 2

Я недавно пытался найти способ извлечения текста из файлов MS Word, и лучшее решение, которое я нашел, — это wvLib:

http://wvware.sourceforge.net/

После установки библиотеки использовать ее в Python довольно просто:

import commands

exe = 'wvText ' + word_file + ' ' + output_txt_file

out = commands.getoutput(exe)

exe = 'cat ' + output_txt_file

out = commands.getoutput(exe)

Вот и все. В основном мы используем функцию commands.getoutput для запуска нескольких сценариев оболочки, а именно wvText (который извлекает текст из документа Word, и cat для чтения вывода файла). После этого весь текст из документа Word окажется в переменной out, готовый к использованию. Надеюсь, это поможет всем, у кого возникнут подобные проблемы в будущем.

Ответ 3

Посмотрите, как работает формат doc, и создайте документ Word с помощью PHP в Linux. Первый вариант особенно полезен. Abiword — рекомендуемый мною инструмент. Однако здесь есть свои ограничения: «если в документе есть сложные таблицы, текстовые поля, встроенные электронные таблицы и так далее, то он может работать не так, как ожидается. Разработка хороших фильтров для MS Word — очень сложный процесс, поэтому, пожалуйста, потерпите, пока мы работаем над тем, чтобы документы Word открывались правильно. Если у вас есть документ Word, который не загружается, пожалуйста, откройте сообщение об ошибке и укажите документ, чтобы мы могли улучшить импортер».

Ответ 4

Если вы намерены использовать исключительно модули Python без вызова подпроцесса, вы можете использовать zip-файл Python module. 

content = ""

# Загрузка DocX  из zipfile

docx = zipfile.ZipFile('/home/whateverdocument.docx')

# Распаковка zipфайла

unpacked = docx.infolist()

# Найдите файл /word/document.xml в пакете и присвойте его переменной

    if item.orig_filename == 'word/document.xml':

        content = docx.read(item.orig_filename)

    else:

        pass

Однако ваша строка содержимого нуждается в очистке. Один из способов сделать это:

# Очистите строку содержимого от xml-тегов для лучшего поиска

fullyclean = []

halfclean = content.split('<')

for item in halfclean:

    if '>' in item:

        bad_good = item.split('>')

        if bad_good[-1] != '':

            fullyclean.append(bad_good[-1])

        else:

            pass

    else:

        pass

# Соберите новую строку со всем чистым содержимым

content = " ".join(fullyclean)

Но наверняка существует более элегантный способ очистки строки, возможно, с помощью модуля «re». Надеюсь, это поможет.

Ответ 5

Методы чтения документов *.docx (MS Word 2007 и более поздних версий) без использования COM interop — это множество вариантов. А вот методы извлечения текста из *.doc (MS Word 97-2000), используя только Python, отсутствуют. Сложно ли это?

Не найдя готового кода, я прочитал несколько спецификаций форматов и откопал несколько предложенных алгоритмов на других языках.

Файл MS Word (*.doc) — это составной файл OLE2. Чтобы не утомлять вас множеством ненужных деталей, думайте о нем как о файловой системе, хранящейся в файле. На самом деле он использует структуру FAT.  Таким образом, вы можете хранить больше файлов внутри файла, например, картинки и т. д. То же самое можно сделать в *.docx, используя вместо него ZIP-архив. На PyPI есть пакеты, которые могут читать OLE-файлы. Например: olefile, compoundfiles... Я использовал пакет compoundfiles для открытия файла *.doc. Однако в MS Word 97-2000 внутренние подфайлы представляют собой не XML или HTML, а бинарные файлы. А поскольку этой информации недостаточно, каждый из них содержит информацию о другом файле. Таким образом, приходится читать как минимум два из них и, соответственно, разгадывать сохраненную информацию. Для полного понимания прочитайте PDF-документ, из которого я взял алгоритм.

Код ниже составлен очень поспешно и протестирован на небольшом количестве файлов. Насколько я могу судить, он работает так, как задумано. Иногда в начале и почти всегда в конце текста появляется какой-то нечитабельный текст. Между ними также могут быть странные символы. 

doc2text module:

"""

Это реализация на Python алгоритма, предложенного для C#:

http://b2xtranslator.sourceforge.net/howtos/How_to_retrieve_text_from_a_binary_doc_file.pdf

 Автор реализации на Python — Дален Бернака.

Код нуждается в доработке и, возможно, в исправлении ошибок!

Поскольку я не являюсь экспертом по C#, я бы хотел, чтобы кто-нибудь перепроверил код.

Части, в которых я не уверен, — это:

    * Правильно ли автор оригинального алгоритма использовал uint32 и int32 при распаковке?

      Я скопировал каждый случай, как в оригинальном алгоритме.

    * Является ли длина FIB для MS Word 97 1472 байта, как в MS Word 2000, и есть ли разница, или нет?

"""

from compoundfiles import CompoundFileReader, CompoundFileError

from struct import unpack

 

__all__ = ["doc2text"]

 

def doc2text (path):

    text = u""

    cr = CompoundFileReader(path)

    # Загрузка WordDocument потока:

    try:

        f = cr.open("WordDocument")

        doc = f.read()

        f.close()

    except: cr.close(); raise CompoundFileError, "Файл разрушен или не является файлом MS Word."

    # Извлеките из него блок информации о файле и потоковую информацию о таблице фрагментов:

    fib = doc[:1472]

    fcClx  = unpack("L", fib[0x01a2l:0x01a6l])[0]

    lcbClx = unpack("L", fib[0x01a6l:0x01a6+4l])[0]

    tableFlag = unpack("L", fib[0x000al:0x000al+4l])[0] & 0x0200l == 0x0200l

    tableName = ("0Table", "1Table")[tableFlag]

    # Загрузите поток таблиц кусков:

    try:

        f = cr.open(tableName)

        table = f.read()

        f.close()

    except: cr.close(); raise CompoundFileError, "Файл поврежден. '%s' piece table stream отсутствует." % tableName

    cr.close()

    # Найти часть таблицы внутри потока таблиц:

    clx = table[fcClx:fcClx+lcbClx]

    pos = 0

    pieceTable = ""

    lcbPieceTable = 0

    while True:

        if clx[pos]=="\x02":

            # This is piece table, we store it:

            lcbPieceTable = unpack("l", clx[pos+1:pos+5])[0]

            pieceTable = clx[pos+5:pos+5+lcbPieceTable]

            break

        elif clx[pos]=="\x01":

            # Это начало какой-то другой подструктуры, мы ее пропускаем:

            pos = pos+1+1+ord(clx[pos+1])

        else: break

    if not pieceTable: raise CompoundFileError, "Файл поврежден. Не удается найти таблицу деталей."

    # Считываем информацию из pieceTable о каждом фрагменте и извлекаем ее из потока WordDocument:

    pieceCount = (lcbPieceTable-4)/12

    for x in xrange(pieceCount):

        cpStart = unpack("l", pieceTable[x*4:x*4+4])[0]

        cpEnd   = unpack("l", pieceTable[(x+1)*4:(x+1)*4+4])[0]

        ofsetDescriptor = ((pieceCount+1)*4)+(x*8)

        pieceDescriptor = pieceTable[ofsetDescriptor:ofsetDescriptor+8]

        fcValue = unpack("L", pieceDescriptor[2:6])[0]

        isANSII = (fcValue & 0x40000000) == 0x40000000

        fc      = fcValue & 0xbfffffff

        cb = cpEnd-cpStart

        enc = ("utf-16", "cp1252")[isANSII]

        cb = (cb*2, cb)[isANSII]

        text += doc[fc:fc+cb].decode(enc, "ignore")

    return "\n".join(text.splitlines())