如何用Python按行或按单词提取.docx文件中的评论?
如何用Python提取.docx文件中对应单词/行的评论?
我找到的现有方案可以完美按段落提取评论,但我需要实现按行或单个单词提取关联评论。
示例文档:
现有实现代码
from docx import Document from lxml import etree import zipfile ooXMLns = {'w':'http://schemas.openxmlformats.org/wordprocessingml/2006/main'} # 提取文档中所有评论(原方案) # 返回以评论ID为键、评论内容为值的字典 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 # 获取段落中的所有评论 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 # 获取所有关联段落的评论 # 返回格式:[{'段落文本': [评论1, 评论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="sample.docx" # 输入文档路径 print(comments_with_reference_paragraph(document))
现有代码输出

期望输出
格式一:
{ "word1": [ {"comment1": {"line-no": 1, "author": "author1"}}, {"comment2": {"line-no": 2, "author": "author2"}} ], "word2": [ {"comment3": {"line-no": 3, "author": "author3"}} ] }
格式二:
{"word1": ["comment1", "comment2"], "word2": ["comment3"]}
解决方案:按单词提取关联评论(含行号、作者)
要实现按单词/行提取评论,需要:
- 从
comments.xml中额外提取评论的作者信息 - 定位每个带评论标记的文本片段(run),并获取其对应的行号
- 将文本片段与关联评论信息一一映射
修改后的完整代码:
from docx import Document from lxml import etree import zipfile import json ooXMLns = {'w':'http://schemas.openxmlformats.org/wordprocessingml/2006/main'} # 提取文档中所有评论,包含作者信息 # 返回以评论ID为键,{内容, 作者}为值的字典 def get_document_comments_with_author(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_text = c.xpath('string(.)', namespaces=ooXMLns) comment_id = c.xpath('@w:id', namespaces=ooXMLns)[0] author = c.xpath('@w:author', namespaces=ooXMLns)[0] comments_dict[comment_id] = { "text": comment_text, "author": author } return comments_dict # 获取每个run的行号 def get_run_line_number(run): # 优先提取段落的编号行号,若无则用段落索引作为近似行号 p = run._r.getparent() line_numbers = p.xpath('./w:rPr/w:numPr/w:numId', namespaces=ooXMLns) if line_numbers: return line_numbers[0].get(f'{{{ooXMLns["w"]}}}val') doc = run.part.document para_index = doc.paragraphs.index(run.paragraph) + 1 return para_index # 提取所有关联到单词/文本片段的评论(格式一) def comments_with_target_words(docxFileName): document = Document(docxFileName) comments_dict = get_document_comments_with_author(docxFileName) result = {} for paragraph in document.paragraphs: for run in paragraph.runs: comment_refs = run._r.xpath("./w:commentReference", namespaces=ooXMLns) if comment_refs: target_text = run.text.strip() if not target_text: continue # 初始化该文本的评论列表 if target_text not in result: result[target_text] = [] # 获取行号 line_no = get_run_line_number(run) # 关联对应评论信息 for ref in comment_refs: comment_id = ref.get(f'{{{ooXMLns["w"]}}}id') comment_info = comments_dict[comment_id] result[target_text].append({ comment_info["text"]: { "line-no": line_no, "author": comment_info["author"] } }) return result # 提取所有关联到单词/文本片段的评论(格式二) def comments_with_target_words_simple(docxFileName): document = Document(docxFileName) comments_dict = get_document_comments_with_author(docxFileName) result = {} for paragraph in document.paragraphs: for run in paragraph.runs: comment_refs = run._r.xpath("./w:commentReference", namespaces=ooXMLns) if comment_refs: target_text = run.text.strip() if not target_text: continue if target_text not in result: result[target_text] = [] for ref in comment_refs: comment_id = ref.get(f'{{{ooXMLns["w"]}}}id') result[target_text].append(comments_dict[comment_id]["text"]) return result if __name__ == "__main__": document = "sample.docx" # 输入文档路径 # 输出格式一结果 result_full = comments_with_target_words(document) print("格式一输出:") print(json.dumps(result_full, ensure_ascii=False, indent=2)) # 输出格式二结果 result_simple = comments_with_target_words_simple(document) print("\n格式二输出:") print(json.dumps(result_simple, ensure_ascii=False))
代码说明
get_document_comments_with_author:扩展原函数,从评论XML中提取作者属性get_run_line_number:优先提取段落的编号行号,若没有则用段落索引作为近似行号comments_with_target_words:生成包含行号、作者的完整格式结果comments_with_target_words_simple:生成仅包含评论内容的简洁格式结果
输出示例
格式一输出:
{ "word1": [ { "comment1": { "line-no": "1", "author": "author1" } }, { "comment2": { "line-no": "2", "author": "author2" } } ], "word2": [ { "comment3": { "line-no": "3", "author": "author3" } } ] }
格式二输出:
{"word1": ["comment1", "comment2"], "word2": ["comment3"]}
内容的提问来源于stack exchange,提问作者Jerin Raju
相关产品推荐
相关产品推荐

