提取DOCX评论

问题描述 投票:0回答:5

我是一名老师。我想要一份对我布置的论文发表评论的所有学生以及他们所说内容的列表。 Drive API 的东西对我来说太具有挑战性,但我想我可以将它们下载为 zip 并解析 XML。

评论被标记在

w:comment
标签中,其中
w:t
代表评论文本, .这应该很容易,但是 XML (etree) 快要死了。

通过教程(和官方 Python 文档):

z = zipfile.ZipFile('test.docx')
x = z.read('word/comments.xml')
tree = etree.XML(x)

然后我这样做:

children = tree.getiterator()
for c in children:
    print(c.attrib)

结果是:

{}
{'{http://schemas.openxmlformats.org/wordprocessingml/2006/main}author': 'Joe Shmoe', '{http://schemas.openxmlformats.org/wordprocessingml/2006/main}id': '1', '{http://schemas.openxmlformats.org/wordprocessingml/2006/main}date': '2017-11-17T16:58:27Z'}
{'{http://schemas.openxmlformats.org/wordprocessingml/2006/main}rsidR': '00000000', '{http://schemas.openxmlformats.org/wordprocessingml/2006/main}rsidDel': '00000000', '{http://schemas.openxmlformats.org/wordprocessingml/2006/main}rsidP': '00000000', '{http://schemas.openxmlformats.org/wordprocessingml/2006/main}rsidRDefault': '00000000', '{http://schemas.openxmlformats.org/wordprocessingml/2006/main}rsidRPr': '00000000'}
{}
{'{http://schemas.openxmlformats.org/wordprocessingml/2006/main}val': '0'}
{'{http://schemas.openxmlformats.org/wordprocessingml/2006/main}val': '0'}
{'{http://schemas.openxmlformats.org/wordprocessingml/2006/main}val': '0'}

此后我完全陷入困境。我尝试过

element.get()
element.findall()
但没有运气。即使当我复制/粘贴值 (
'{http://schemas.openxmlformats.org/wordprocessingml/2006/main}val'
) 时,我也会得到
None
作为回报。

有人可以帮忙吗?

python xml openxml google-docs docx
5个回答
9
投票

考虑到 OOXML 是一种如此复杂的格式,您已经取得了很大的进展。

这里有一些示例 Python 代码,展示了如何通过 XPath 访问 DOCX 文件的注释:

from lxml import etree
import zipfile

ooXMLns = {'w':'http://schemas.openxmlformats.org/wordprocessingml/2006/main'}

def get_comments(docxFileName):
  docxZip = zipfile.ZipFile(docxFileName)
  commentsXML = docxZip.read('word/comments.xml')
  et = etree.XML(commentsXML)
  comments = et.xpath('//w:comment',namespaces=ooXMLns)
  for c in comments:
    # attributes:
    print(c.xpath('@w:author',namespaces=ooXMLns))
    print(c.xpath('@w:date',namespaces=ooXMLns))
    # string value of the comment:
    print(c.xpath('string(.)',namespaces=ooXMLns))

9
投票

感谢@kjhughes 提供了从文档文件中提取所有评论的惊人答案。我和该线程中的其他人一样面临着同样的问题,以获取评论相关的文本。我以 @kjhughes 的代码为基础,并尝试使用 python-docx 来解决这个问题。所以这是我对此的看法。

示例文档。

我将提取评论以及文档中引用的段落。

from docx import Document
from lxml import etree
import zipfile
ooXMLns = {'w':'http://schemas.openxmlformats.org/wordprocessingml/2006/main'}
#Function to extract all the comments of document(Same as accepted answer)
#Returns a dictionary with comment id as key and comment string as value
def get_document_comments(docxFileName):
    comments_dict={}
    docxZip = zipfile.ZipFile(docxFileName)
    commentsXML = docxZip.read('word/comments.xml')
    et = etree.XML(commentsXML)
    comments = et.xpath('//w:comment',namespaces=ooXMLns)
    for c in comments:
        comment=c.xpath('string(.)',namespaces=ooXMLns)
        comment_id=c.xpath('@w:id',namespaces=ooXMLns)[0]
        comments_dict[comment_id]=comment
    return comments_dict
#Function to fetch all the comments in a paragraph
def paragraph_comments(paragraph,comments_dict):
    comments=[]
    for run in paragraph.runs:
        comment_reference=run._r.xpath("./w:commentReference")
        if comment_reference:
            comment_id=comment_reference[0].xpath('@w:id',namespaces=ooXMLns)[0]
            comment=comments_dict[comment_id]
            comments.append(comment)
    return comments
#Function to fetch all comments with their referenced paragraph
#This will return list like this [{'Paragraph text': [comment 1,comment 2]}]
def comments_with_reference_paragraph(docxFileName):
    document = Document(docxFileName)
    comments_dict=get_document_comments(docxFileName)
    comments_with_their_reference_paragraph=[]
    for paragraph in document.paragraphs:  
        if comments_dict: 
            comments=paragraph_comments(paragraph,comments_dict)  
            if comments:
                comments_with_their_reference_paragraph.append({paragraph.text: comments})
    return comments_with_their_reference_paragraph
if __name__=="__main__":
    document="test.docx"  #filepath for the input document
    print(comments_with_reference_paragraph(document))

示例文档的输出如下所示

我已经在段落级别完成了此操作。这也可以在 python-docx 运行级别完成。 希望对您有帮助。


6
投票

我使用 Word 对象模型 从 Word 文档中提取评论和回复。有关 Comments 对象的文档可以在here 找到。本文档使用 Visual Basic for Applications (VBA)。但我只需稍作修改就能使用 Python 中的函数。 Word 对象模型的唯一问题是我必须使用 pywin32 中的 win32com 包,它在 Windows PC 上运行良好,但我不确定它是否可以在 macOS 上运行。

这是我用来提取评论和相关回复的示例代码:

    import win32com.client as win32
    from win32com.client import constants

    word = win32.gencache.EnsureDispatch('Word.Application')
    word.Visible = False 
    filepath = "path\to\file.docx"

    def get_comments(filepath):
        doc = word.Documents.Open(filepath) 
        doc.Activate()
        activeDoc = word.ActiveDocument
        for c in activeDoc.Comments: 
            if c.Ancestor is None: #checking if this is a top-level comment
                print("Comment by: " + c.Author)
                print("Comment text: " + c.Range.Text) #text of the comment
                print("Regarding: " + c.Scope.Text) #text of the original document where the comment is anchored 
                if len(c.Replies)> 0: #if the comment has replies
                    print("Number of replies: " + str(len(c.Replies)))
                    for r in range(1, len(c.Replies)+1):
                        print("Reply by: " + c.Replies(r).Author)
                        print("Reply text: " + c.Replies(r).Range.Text) #text of the reply
        doc.Close()

0
投票

如果您还想要评论相关的文字:

def get_document_comments(docxFileName):
       comments_dict = {}
       comments_of_dict = {}
       docx_zip = zipfile.ZipFile(docxFileName)
       comments_xml = docx_zip.read('word/comments.xml')
       comments_of_xml = docx_zip.read('word/document.xml')
       et_comments = etree.XML(comments_xml)
       et_comments_of = etree.XML(comments_of_xml)
       comments = et_comments.xpath('//w:comment', namespaces=ooXMLns)
       comments_of = et_comments_of.xpath('//w:commentRangeStart', namespaces=ooXMLns)
       for c in comments:
          comment = c.xpath('string(.)', namespaces=ooXMLns)
          comment_id = c.xpath('@w:id', namespaces=ooXMLns)[0]
          comments_dict[comment_id] = comment
       for c in comments_of:
          comments_of_id = c.xpath('@w:id', namespaces=ooXMLns)[0]
          parts = et_comments_of.xpath(
            "//w:r[preceding-sibling::w:commentRangeStart[@w:id=" + comments_of_id + "] and following-sibling::w:commentRangeEnd[@w:id=" + comments_of_id + "]]",
            namespaces=ooXMLns)
          comment_of = ''
          for part in parts:
             comment_of += part.xpath('string(.)', namespaces=ooXMLns)
             comments_of_dict[comments_of_id] = comment_of
        return comments_dict, comments_of_dict

0
投票

以上所有方法都很好,但是区分评论和回复的唯一方法是 win32com 方法,但对于包含大量评论的大型文档来说,该方法非常慢。而且,如果您尝试迭代一个充满 docx 的文件夹,它就会陷入困境。 etree 速度要快得多,但它会为您提供所有评论,而不会嵌套其回复(即 comments.xml 中的 w:comment 元素不是嵌套的),因此您无法重现确切的评论/回复线程。您只需在一个平面列表中获得所有评论/回复的汤。 有人知道使用 etree/xml/xpath 重现评论/回复线程的方法吗?

© www.soinside.com 2019 - 2024. All rights reserved.