【问题标题】:extracting text from MS word files in python从python中的MS word文件中提取文本
【发布时间】:2010-09-12 14:50:40
【问题描述】:

为了在 python 中处理 MS word 文件,有 python win32 扩展,可以在 windows 中使用。我如何在linux中做同样的事情? 有图书馆吗?

【问题讨论】:

  • 你能定义“合作”吗?只读,还是也写?

标签: python linux ms-word


【解决方案1】:

看看how the doc format workscreate word document using PHP in linux。前者特别有用。 Abiword 是我推荐的工具。不过有limitations

但是,如果文档包含复杂的表格、文本框、嵌入的电子表格等,那么它可能无法按预期工作。开发好的 MS Word 过滤器是一个非常困难的过程,所以请耐心等待我们努力让 Word 文档正确打开。如果您有一个无法加载的 Word 文档,请打开一个 Bug 并包含该文档,以便我们改进导入器。

【讨论】:

  • 不仅如此!即使是保存在 Word 97 格式中的最基本的文本,如果不依靠 word 为您 (COM) 进行操作,也几乎不可能轻松获取。大多数word文档都不是HTML!
  • Abiword 并不认为它是一个 HTML 文档,并且考虑到该工具的广泛性……我认为实现它并不“容易”。 Abiword 是一款可以帮助您阅读 MS Word 文件的工具……由于作者关注文本检索,这就足够了。
  • 啊,我一直以为 abiword 只是另一个文字处理器!伙计,那会在不久前让我免去一些头痛。
【解决方案2】:

我不确定如果不使用 COM,您是否会很幸运。 .doc 格式非常复杂,在保存时通常被称为 Word 的“内存转储”!

在 Swati,那是 HTML 格式,很好很漂亮,但大多数 word 文档都不是很好!

【讨论】:

    【解决方案3】:

    OpenOffice.org 可以使用 Python 编写脚本:see here

    由于 OOo 可以完美加载大多数 MS Word 文件,我认为这是您最好的选择。

    【讨论】:

    • 并非完美无缺。接近,但在我的经验中远非完美(OO 2.0 - 3.0)。
    • 就像 MS Word N+1 打开 MS Words N 文件一样完美,并且比 MS Word N+1 打开 MS Words N-1 文件更好,恕我直言
    【解决方案4】:

    您可以对antiword 进行子进程调用。 Antiword 是一个 linux 命令行实用程序,用于从 word doc 中转储文本。适用于简单文档(显然它会丢失格式)。它可以通过 apt 获得,并且可能以 RPM 的形式提供,或者您可以自己编译。

    【讨论】:

    • antiword 可以将 word 文档转换为 DocBook XML,这将保留(至少部分)格式。
    【解决方案5】:

    我知道这是一个老问题,但我最近试图找到一种从 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.getouput 函数来运行几个 shell 脚本,即 wvText(从 Word 文档中提取文本,并使用 cat 读取文件输出)。之后,Word 文档中的整个文本将在 out 变量中,可供使用。

    希望这对以后遇到类似问题的人有所帮助。

    【讨论】:

      【解决方案6】:

      (注意:我也在this question上发布了这个,但在这里似乎相关,所以请原谅转发。)

      现在,这很丑陋而且很hacky,但它似乎对我来说适用于基本的文本提取。显然,要在 Qt 程序中使用它,您必须为其生成一个进程等,但我一起破解的命令行是:

      unzip -p file.docx | grep '<w:t' | sed 's/<[^<]*>//g' | grep -v '^[[:space:]]*$'
      

      那就是:

      unzip -p file.docx: -p == "unzip to stdout"

      grep ':只抓取包含 ' 是 Word 2007 XML 元素中的“文本”能看出来)

      sed 's/>//g'*:删除标签内的所有内容

      grep -v '^[[:space:]]$'*: 删除空行

      可能有一种更有效的方法可以做到这一点,但它似乎在我测试过的少数文档上对我有用。

      据我所知,unzip、grep 和 sed 都有适用于 Windows 和任何 Unix 的端口,因此它应该是合理的跨平台的。尽管有点丑陋 ;)

      【讨论】:

        【解决方案7】:

        如果您打算使用纯 python 模块而不调用子进程,则可以使用 zipfile python 模块。

        content = ""
        # Load DocX into zipfile
        docx = zipfile.ZipFile('/home/whateverdocument.docx')
        # Unpack zipfile
        unpacked = docx.infolist()
        # Find the /word/document.xml file in the package and assign it to variable
        for item in unpacked:
            if item.orig_filename == 'word/document.xml':
                content = docx.read(item.orig_filename)
        
            else:
                pass
        

        但是,您的内容字符串需要清理,这样做的一种方法是:

        # Clean the content string from xml tags for better search
        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
        
        # Assemble a new string with all pure content
        content = " ".join(fullyclean)
        

        但肯定有一种更优雅的方式来清理字符串,可能使用 re 模块。 希望这会有所帮助。

        【讨论】:

        • 删除 XML 实体,如  从“文本”:>>>从 xml.sax.saxutils 导入 unescape >>>text=unescape(内容)
        • 使用 re 模块,清理会容易很多:stripped_content = re.compile(b'&lt;.*?&gt;').sub(b' ', content ) # strip tags 在你的代码中我无法理解的一件事是,在以前的 sn-p 中你为什么不 breaking out在if 块内?
        【解决方案8】:

        benjamin 的回答非常好。我刚刚巩固了...

        import zipfile, re
        
        docx = zipfile.ZipFile('/path/to/file/mydocument.docx')
        content = docx.read('word/document.xml').decode('utf-8')
        cleaned = re.sub('<(.|\n)*?>','',content)
        print(cleaned)
        

        【讨论】:

        • 我应该重申这仅适用于 docx(Word 2007 或更高版本)。对于 .doc 文件,wvware 是您最好的选择。根据您的环境,设置起来可能会很痛苦,但它确实做得很好。
        • 删除 XML 实体,如  从“文本”:>>>从 xml.sax.saxutils 导入 unescape >>>text=unescape(cleaned)
        • content = docx.read('word/document.xml').decode('utf-8') 否则清理时会出错:TypeError: cannot use a string pattern on a bytes-像对象
        【解决方案9】:

        使用原生 Python docx 模块。以下是如何从文档中提取所有文本:

        document = docx.Document(filename)
        docText = '\n\n'.join(
            paragraph.text for paragraph in document.paragraphs
        )
        print(docText)
        

        Python DocX site

        还可以查看Textract,它会拉出表格等。

        使用正则表达式解析 XML 会调用 cthulu。不要这样做!

        【讨论】:

        • 你在from docx import *这里吗?如果没有,你是怎么得到getdocumenttext等的?
        • opendocx 不在模块中(可能是在 2009 年)。文档通过 Document 类打开,例如import docx; document = docx.Document('Hello world.docx').
        • @egpbos 我已经更新了示例代码以使用新一代 python-docx。
        • 这段代码导致我出错:paragraph.text.encode('utf-8') for paragraph in document.paragraphs TypeError: sequence item 0: expected str instance, bytes found
        • @MyopicVisage 检查官方网站 - 最新版本可能有不同的签名。
        【解决方案10】:

        Unoconv 也可能是一个不错的选择:http://linux.die.net/man/1/unoconv

        【讨论】:

          【解决方案11】:

          只是一个不使用 COM 读取“doc”文件的选项:miette。应该可以在任何平台上工作。

          【讨论】:

            【解决方案12】:

            如果您安装了 LibreOffice,you can simply call it from the command line to convert the file to text,然后将文本加载到 Python 中。

            【讨论】:

            • 啊菲利普!我只是在寻找一种方法来拒绝您对我的另一篇文章所做的微不足道的风格编辑。我试着直接联系你。您能否更清楚地说明您在这里的建议?我在这里给出的这个答案是对这个问题的回答。这还不够好吗?
            • 回复。你对风格和语法的编辑:我更喜欢我自己的风格和语法,谢谢。一个好的编辑不会强加自己的风格。真的,我们都没有足够的空闲时间来做琐碎的拼写和语法检查,是吗?我想你可能会觉得这有点过分。
            【解决方案13】:

            这是一个老问题吗? 我相信这样的事情是不存在的。 只有回答的和未回答的。 如果您愿意,这个问题几乎没有答案,或者回答了一半。 好吧,在不使用 COM 互操作的情况下读取 *.docx(MS Word 2007 及更高版本)文档的方法都已涵盖。 但是,仅使用 Python 从 *.doc (MS Word 97-2000) 中提取文本的方法缺乏。 这很复杂吗? 要做:不是真的,要理解:嗯,那是另一回事。

            当我没有找到任何完成的代码时,我阅读了一些格式规范并挖掘了一些其他语言的建议算法。

            MS Word (*.doc) 文件是一个 OLE2 复合文件。 不要用很多不必要的细节来打扰您,将其视为存储在文件中的文件系统。它实际上使用了FAT结构,所以定义成立。 (嗯,也许你可以在 Linux 中循环挂载它???) 这样,您可以在一个文件中存储更多文件,例如图片等。 在 *.docx 中也可以使用 ZIP 存档代替。 PyPI 上有可以读取 OLE 文件的包。喜欢(olefile,compoundfiles,...) 我使用 Compoundfiles 包打开 *.doc 文件。 但是,在 MS Word 97-2000 中,内部子文件不是 XML 或 HTML,而是二进制文件。 由于这还不够,每个都包含关于另一个的信息,因此您必须阅读其中至少两个并相应地解开存储的信息。 要完全理解,请阅读我从中获取算法的 PDF 文档。

            下面的代码非常仓促地编写并在少量文件上进行了测试。 据我所知,它按预期工作。 有时会在开头出现一些乱码,而且几乎总是在文本的结尾。 中间也可能有一些奇怪的字符。

            那些只想搜索文本的人会很高兴。 尽管如此,我还是敦促任何可以帮助改进此代码的人这样做。

            
            doc2text module:
            """
            This is Python implementation of C# algorithm proposed in:
            http://b2xtranslator.sourceforge.net/howtos/How_to_retrieve_text_from_a_binary_doc_file.pdf
            
            Python implementation author is Dalen Bernaca.
            Code needs refining and probably bug fixing!
            As I am not a C# expert I would like some code rechecks by one.
            Parts of which I am uncertain are:
                * Did the author of original algorithm used uint32 and int32 when unpacking correctly?
                  I copied each occurence as in original algo.
                * Is the FIB length for MS Word 97 1472 bytes as in MS Word 2000, and would it make any difference if it is not?
                * Did I interpret each C# command correctly?
                  I think I did!
            """
            
            from compoundfiles import CompoundFileReader, CompoundFileError
            from struct import unpack
            
            __all__ = ["doc2text"]
            
            def doc2text (path):
                text = u""
                cr = CompoundFileReader(path)
                # Load WordDocument stream:
                try:
                    f = cr.open("WordDocument")
                    doc = f.read()
                    f.close()
                except: cr.close(); raise CompoundFileError, "The file is corrupted or it is not a Word document at all."
                # Extract file information block and piece table stream informations from it:
                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]
                # Load piece table stream:
                try:
                    f = cr.open(tableName)
                    table = f.read()
                    f.close()
                except: cr.close(); raise CompoundFileError, "The file is corrupt. '%s' piece table stream is missing." % tableName
                cr.close()
                # Find piece table inside a table stream:
                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":
                        # This is beggining of some other substructure, we skip it:
                        pos = pos+1+1+ord(clx[pos+1])
                    else: break
                if not pieceTable: raise CompoundFileError, "The file is corrupt. Cannot locate a piece table."
                # Read info from pieceTable, about each piece and extract it from WordDocument stream:
                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())
            

            【讨论】:

              【解决方案14】:

              要读取 Word 2007 及更高版本的文件,包括 .docx 文件,您可以使用 python-docx 包:

              from docx import Document
              document = Document('existing-document-file.docx')
              document.save('new-file-name.docx')
              

              要从 Word 2003 及更早版本中读取 .doc 文件,请对 antiword 进行子进程调用。你需要先安装antiword:

              sudo apt-get install antiword
              

              然后从你的 python 脚本中调用它:

              import os
              input_word_file = "input_file.doc"
              output_text_file = "output_file.txt"
              os.system('antiword %s > %s' % (input_word_file, output_text_file))
              

              【讨论】:

                【解决方案15】:

                Aspose.Words Cloud SDK for Python 是一个独立于平台的解决方案,用于将 MS Word/Open Office 文件转换为文本。它是一个商业产品,但免费试用计划提供每月 150 次 API 调用。

                P.S:我是 Aspose 的开发者传道者。

                # For complete examples and data files, please go to https://github.com/aspose-words-cloud/aspose-words-cloud-python
                # Import module
                import asposewordscloud
                import asposewordscloud.models.requests
                from shutil import copyfile
                
                # Please get your Client ID and Secret from https://dashboard.aspose.cloud.
                client_id='xxxxxxx-xxxx-xxxx-xxxxx-xxxxxxxxxx'
                client_secret='xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
                
                words_api = asposewordscloud.WordsApi(client_id,client_secret)
                words_api.api_client.configuration.host='https://api.aspose.cloud'
                
                filename = 'C:/Temp/02_pages.docx'
                dest_name = 'C:/Temp/02_pages.txt'
                #Convert RTF to text
                request = asposewordscloud.models.requests.ConvertDocumentRequest(document=open(filename, 'rb'), format='txt')
                result = words_api.convert_document(request)
                copyfile(result, dest_name)
                

                【讨论】:

                  猜你喜欢
                  • 2020-04-10
                  • 2011-10-06
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2014-10-30
                  • 2012-01-05
                  • 2011-06-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多