我不确定我是否遗漏了任何明显的东西,但是我没有找到任何关于如何在文档中的某个特定位置插入Word元素(例如表格)的文档?
我使用以下方法加载现有的MS Word .docx文档:
my_document = Document('some/path/to/my/document.docx')
我的用例是获取文档中书签或部分的“位置”,然后继续在该点下面插入表格。
我正在考虑一个允许我按照这些方式做某事的API:
insertion_point = my_document.bookmarks['bookmark_name'].position
my_document.add_table(rows=10, cols=3, position=insertion_point+1)
我看到有计划实现类似于MS Word API的'range'对象的东西,这将有效地解决这个问题。在此期间,有没有办法指示document
对象方法在哪里插入新元素?
也许我可以粘贴一些lxml代码来查找节点并将其传递给这些python-docx方法?任何关于这个主题的帮助将不胜感激!感谢。
答案 0 :(得分:11)
我记得一句古老的格言,“使用来源,卢克!”,并且可以弄明白。 python-docx所有者在其git项目页面上发布的帖子也给了我一个提示:https://github.com/python-openxml/python-docx/issues/7。
可以使用其_document_part._element
属性访问完整的XML文档模型。它的行为与lxml etree元素完全相同。从那里,一切皆有可能。
为了解决我的特定插入点问题,我创建了一个临时docx.Document对象,用于存储生成的内容。
import docx
from docx.oxml.shared import qn
tmp_doc = docx.Document()
# Generate content in tmp_doc document
tmp_doc.add_heading('New heading', 1)
# more content generation using docx API.
# ...
# Reference the tmp_doc XML content
tmp_doc_body = tmp_doc._document_part._element.body
# You could pretty print it by using:
#print(docx.oxml.xmlchemy.serialize_for_reading(tmp_doc_body))
然后我将docx模板(包含名为'insertion_point'的书签)加载到第二个docx.Document对象中。
doc = docx.Document('/some/path/example.docx')
doc_body = doc._document_part._element.body
#print(docx.oxml.xmlchemy.serialize_for_reading(doc_body))
下一步是解析doc XML以查找插入点的索引。我为手头的任务定义了一个小函数,它返回一个命名书签父段落元素:
def get_bookmark_par_element(document, bookmark_name):
"""
Return the named bookmark parent paragraph element. If no matching
bookmark is found, the result is '1'. If an error is encountered, '2'
is returned.
"""
doc_element = document._document_part._element
bookmarks_list = doc_element.findall('.//' + qn('w:bookmarkStart'))
for bookmark in bookmarks_list:
name = bookmark.get(qn('w:name'))
if name == bookmark_name:
par = bookmark.getparent()
if not isinstance(par, docx.oxml.CT_P):
return 2
else:
return par
return 1
新定义的函数用于获取书签'insertion_point'父段落。错误控制留给读者。
bookmark_par = get_bookmark_par_element(doc, 'insertion_point')
我们现在可以使用bookmark_par的etree索引在正确的位置插入我们的tmp_doc生成的内容:
bookmark_par_parent = bookmark_par.getparent()
index = bookmark_par_parent.index(bookmark_par) + 1
for child in tmp_doc_body:
bookmark_par_parent.insert(index, child)
index = index + 1
bookmark_par_parent.remove(bookmark_par)
该文档现已完成,生成的内容已插入现有Word文档的书签位置。
# Save result
# print(docx.oxml.xmlchemy.serialize_for_reading(doc_body))
doc.save('/some/path/generated_doc.docx')
我希望这可以帮助某人,因为有关此问题的文件仍未编写。
答案 1 :(得分:1)
Python-docx所有者建议如何将表插入现有文档的中间: https://github.com/python-openxml/python-docx/issues/156
这里有一些改进:
import re
from docx import Document
def move_table_after(document, table, search_phrase):
regexp = re.compile(search_phrase)
for paragraph in document.paragraphs:
if paragraph.text and regexp.search(paragraph.text):
tbl, p = table._tbl, paragraph._p
p.addnext(tbl)
return paragraph
if __name__ == '__main__':
document = Document('Existing_Document.docx')
table = document.add_table(rows=..., cols=...)
...
move_table_after(document, table, "your search phrase")
document.save('Modified_Document.docx')
答案 2 :(得分:1)
看看python-docx-template,它允许jinja2样式模板在docx文件而不是Word书签中插入点:
答案 3 :(得分:0)
非常感谢您花时间解释所有这些。
我或多或少地遇到了同样的问题。我的具体观点是如何在最后合并两个或多个docx文档。
这不是你问题的完全解决方案,但这是我带来的功能:
def combinate_word(main_file, files, output):
main_doc = Document(main_file)
for file in files:
sub_doc = Document(file)
for element in sub_doc._document_part.body._element:
main_doc._document_part.body._element.append(element)
main_doc.save(output)
不幸的是,使用python-docx复制图像还不太可能也不容易。我回到win32com ......
答案 4 :(得分:0)
您将[image]作为令牌放在模板文档中:
for paragraph in document.paragraphs:
if "[image]" in paragraph.text:
paragraph.text = paragraph.text.strip().replace("[image]", "")
run = paragraph.add_run()
run.add_picture(image_path, width=Inches(3))
你也有一个表格单元格中的段落。只需找到单元格并按上述操作。