解决使用python提取word文档中所有的图片时图片丢失的问题
python解析word文档,提取文档中所有的图片并保存,并将原图位置用占位符替换。
问题描述
利用python-dox库解析word文档,并提取里面的所有图片时发现会出现一摸一样的图片只解析一次,导致图片丢失,数量不对的情况。
解决方法
通过例如 lxml 或直接解析 Word 的结构解决 例如,读取每个图片引用的具体关系
- 解析 Word 文档的内容:我们需要读取 document.xml 来找到所有的 <w:drawing> 元素。
- 解析关系文件:在 word/_rels/document.xml.rels 文件中,每张图片都有一个关系定义,你需要读取这个文件来找到图像的实际存储路径。
- 提取和保存图像:最后,从 Word 的 ZIP包里把真正的图像提取出来保存到本地。
代码
import os
from datetime import datetime
from docx import Document
from docx.oxml.ns import qn
import zipfile
import os
from zipfile import ZipFile
from lxml import etree
def extract_images_and_replace(docx_path, output_docx_path, images_dir):
# 打开 docx 文件作为 Zip
base_filename=os.path.splitext(os.path.basename(docx_path))[0]
with ZipFile(docx_path, 'r') as docx:
# 读取 word/document.xml 文件
document_xml = docx.read('word/document.xml')
root = etree.XML(document_xml)
# 读取 word/_rels/document.xml.rels 文件
rels_xml = docx.read('word/_rels/document.xml.rels')
rels_root = etree.XML(rels_xml)
# 查找所有的 <w:drawing> 元素
drawing_elements = root.findall('.//{http://schemas.openxmlformats.org/wordprocessingml/2006/main}drawing')
# 创建输出目录
os.makedirs(images_dir, exist_ok=True)
# 查找并保存所有图片
image_id=0
placelist=[]
for i, drawing in enumerate(drawing_elements):
# 获取 <a:blip> 的 r:embed 属性
blip_element = drawing.find('.//{http://schemas.openxmlformats.org/drawingml/2006/main}blip')
if blip_element is not None:
embed_attr = blip_element.get('{http://schemas.openxmlformats.org/officeDocument/2006/relationships}embed')
if embed_attr:
# 查找图像在 rels 中的路径
relationship = rels_root.find(f".//{{http://schemas.openxmlformats.org/package/2006/relationships}}Relationship[@Id='{embed_attr}']")
if relationship is not None:
timestamp = datetime.now().timestamp()
target = relationship.get('Target')
image_path = os.path.join('word', target)
placeholder = f"{base_filename}_{timestamp}<unused{image_id}>"
placelist.append(placeholder)
# 提取并保存图像
with docx.open(image_path) as image_file:
image_data = image_file.read()
image_ext = os.path.splitext(image_path)[1]
# 保存图片到指定目录
image_filename = os.path.join(images_dir, f"{placeholder}.png")
with open(image_filename, 'wb') as out_file:
out_file.write(image_data)
print(f'Image saved: {image_filename}')
image_id+=1
print(len(placelist))
# 替换文档中对应的图片为占位符
i=0
# 遍历所有段落及图片
doc = Document(docx_path)
for paragraph in doc.paragraphs:
for run in paragraph.runs:
# try:
if 'graphicData' in run._element.xml: # 搜索图片标记
# 删除图片并插入占位符文字
inline = run._element.xpath('.//a:graphic')[0]
inline.getparent().remove(inline)
run.text = placelist[i]
i+=1
# except:
# break
# 保存修改后的 Word 文档
doc.save(output_docx_path)
print(f"提取了 {image_id} 张图片并用占位符替换。")
print(f"图片保存在:{images_dir}")
print(f"修改后的 Word 文件保存在:{output_docx_path}")
# 示例调用
docx_path = "/data/xxxxx/ZHX财务管理系统.docx" # 输入的 Word 文档
output_docx_path = "/data/xxxxx/ZHX财务管理系统--.docx" # 替换图片后保存的 Word 文档
images_dir = "/data/xxxxx/样例图集/" # 提取图片保存的目录
extract_images_and_replace(docx_path, output_docx_path, images_dir)