Word文档图片批量提取工具
AI-摘要
阿狄 GPT
AI初始化中...
介绍自己
生成本文简介
推荐相关文章
前往主页
前往tianli博客
Word 文档图片批量提取工具
需求背景
在文档处理过程中,经常需要从 Word 文档中提取图片。手动保存不仅效率低,还容易出错。本文介绍一个基于 Python 的工具,可以自动从 .docx 文件中提取图片,并根据文档中的图片标题自动命名。
实现思路
核心原理
.docx 文件本质上是一个 ZIP 压缩包,包含以下关键文件:
word/document.xml- 文档内容和结构word/_rels/document.xml.rels- 资源关联关系word/media/- 图片文件存储目录
实现步骤
- 解析文档结构:解压
.docx文件,读取 XML 文件 - 提取图片信息:从
document.xml中查找图片元素(<a:blip>),获取 relationship ID - 匹配图片标题:在图片所在段落及后续段落中,使用正则表达式匹配"图X-X:标题"格式
- 关联文件路径:通过
document.xml.rels将 relationship ID 映射到实际的图片文件路径 - 保存图片:从
word/media/目录提取图片,使用标题命名并保存
核心代码
Python 脚本
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
智能提取Word文档图片
"""
import sys
import os
import re
from pathlib import Path
from zipfile import ZipFile
import xml.etree.ElementTree as ET
def sanitize_filename(filename):
"""清理文件名"""
illegal_chars = r'[<>:"/\\|?*]'
filename = re.sub(illegal_chars, '_', filename)
filename = filename.strip()
if len(filename) > 200:
filename = filename[:200]
return filename
def extract_image_title_mapping(document_xml):
"""从 document.xml 中提取图片和标题的对应关系"""
namespaces = {
'w': 'http://schemas.openxmlformats.org/wordprocessingml/2006/main',
'a': 'http://schemas.openxmlformats.org/drawingml/2006/main',
'r': 'http://schemas.openxmlformats.org/officeDocument/2006/relationships',
}
root = ET.fromstring(document_xml.encode('utf-8'))
image_title_map = {}
paragraphs = root.findall('.//w:p', namespaces)
for i, para in enumerate(paragraphs):
blips = para.findall('.//a:blip', namespaces)
if blips:
for blip in blips:
r_embed = blip.get('{http://schemas.openxmlformats.org/officeDocument/2006/relationships}embed')
if r_embed:
title = None
current_text = ''.join(para.itertext())
# 匹配图片标题格式:图1-1:标题 或 图1-1 标题
title_match = re.search(r'图\s*(\d+)[--]\s*(\d+)\s*[::]\s*([^。.\n\r]{2,50})', current_text)
if not title_match:
title_match = re.search(r'图\s*(\d+)[--]\s*(\d+)\s+([^。.\n\r]{2,50})', current_text)
if title_match:
fig_num = f"{title_match.group(1)}-{title_match.group(2)}"
title_text = title_match.group(3).strip()
title_text = re.sub(r'\s+', ' ', title_text)
title_text = title_text.strip('。.、,,::')
title = f"图{fig_num}_{title_text}"
# 如果当前段落没找到,检查后续2个段落
if not title and i + 1 < len(paragraphs):
for next_para in paragraphs[i+1:i+3]:
next_text = ''.join(next_para.itertext())
title_match = re.search(r'图\s*(\d+)[--]\s*(\d+)\s*[::]\s*([^。.\n\r]{2,50})', next_text)
if not title_match:
title_match = re.search(r'图\s*(\d+)[--]\s*(\d+)\s+([^。.\n\r]{2,50})', next_text)
if title_match:
fig_num = f"{title_match.group(1)}-{title_match.group(2)}"
title_text = title_match.group(3).strip()
title_text = re.sub(r'\s+', ' ', title_text)
title_text = title_text.strip('。.、,,::')
title = f"图{fig_num}_{title_text}"
break
if title:
image_title_map[r_embed] = sanitize_filename(title)
return image_title_map
def get_image_file_from_rid(rels_xml, rid):
"""从 _rels/document.xml.rels 中根据 rId 获取实际的图片文件路径"""
namespaces = {
'r': 'http://schemas.openxmlformats.org/package/2006/relationships'
}
root = ET.fromstring(rels_xml.encode('utf-8'))
for rel in root.findall('.//r:Relationship', namespaces):
if rel.get('Id') == rid:
target = rel.get('Target')
if target:
return 'word/' + target.replace('../', '')
return None
def extract_images_fixed(docx_path, output_dir=None):
"""提取图片主函数"""
docx_path = Path(docx_path)
if not docx_path.exists():
print(f"错误:文件不存在 - {docx_path}")
return
if output_dir is None:
output_dir = docx_path.parent / 'images'
else:
output_dir = Path(output_dir)
output_dir.mkdir(parents=True, exist_ok=True)
print(f"输出目录:{output_dir}\n")
with ZipFile(docx_path, 'r') as docx_zip:
document_xml = docx_zip.read('word/document.xml').decode('utf-8', errors='ignore')
rels_xml = docx_zip.read('word/_rels/document.xml.rels').decode('utf-8', errors='ignore')
image_files = [f for f in docx_zip.namelist() if f.startswith('word/media/')]
print(f"文档中共有 {len(image_files)} 张图片\n")
# 提取图片和标题的映射关系
image_title_map = extract_image_title_mapping(document_xml)
print(f"识别到 {len(image_title_map)} 张带标题的图片\n")
# 提取图片
extracted_count = 0
for rid, title in image_title_map.items():
try:
image_path = get_image_file_from_rid(rels_xml, rid)
if not image_path or image_path not in image_files:
print(f"✗ {title} - 未找到对应的图片文件")
continue
image_data = docx_zip.read(image_path)
# 确定文件扩展名
ext = Path(image_path).suffix
if not ext:
if image_data[:8] == b'\x89PNG\r\n\x1a\n':
ext = '.png'
elif image_data[:2] == b'\xff\xd8':
ext = '.jpg'
elif image_data[:6] in [b'GIF87a', b'GIF89a']:
ext = '.gif'
else:
ext = '.png'
# 保存图片
output_path = output_dir / f"{title}{ext}"
# 处理重名
counter = 1
while output_path.exists():
output_path = output_dir / f"{title}_{counter}{ext}"
counter += 1
with open(output_path, 'wb') as f:
f.write(image_data)
file_size = len(image_data) / 1024
print(f"✓ {output_path.name} ({file_size:.1f} KB)")
extracted_count += 1
except Exception as e:
print(f"✗ {title} - 提取失败:{e}")
print(f"\n提取完成!成功提取 {extracted_count} 张图片")
if __name__ == '__main__':
if len(sys.argv) < 2:
print("使用方法:python extract_images.py <Word文档路径> [输出目录]")
sys.exit(1)
word_path = sys.argv[1]
output_dir = sys.argv[2] if len(sys.argv) > 2 else None
extract_images_fixed(word_path, output_dir)
Windows 批处理脚本(可选)
@echo off
if "%~1"=="" (
echo 请拖拽Word文档到此脚本
pause
exit /b
)
set "word_file=%~1"
set "output_dir=%~2"
set "script_name=extract_images.py"
REM 检测Python
python --version >nul 2>&1
if %errorlevel% equ 0 (
set "PYTHON_CMD=python"
goto :RUN
)
py --version >nul 2>&1
if %errorlevel% equ 0 (
set "PYTHON_CMD=py"
goto :RUN
)
echo Python未安装
pause
exit /b
:RUN
if "%output_dir%"=="" (
set "output_dir=%~dp1images"
)
"%PYTHON_CMD%" "%script_name%" "%word_file%" "%output_dir%"
pause
使用方法
方式一:Python 命令行
# 基本用法(图片保存到文档同目录的images文件夹)
python extract_images.py "报告.docx"
# 指定输出目录
python extract_images.py "报告.docx" "D:/output/images"
方式二:Windows 拖拽(使用批处理脚本)
- 将 Word 文档拖拽到
提取图片.bat上 - 图片自动保存到文档同目录的
images文件夹
注意事项
- 仅支持
.docx格式,不支持.doc格式 - 图片标题格式:支持
图1-1:标题或图1-1 标题格式 - 标题位置:标题应在图片所在段落或后续2个段落中
- 重名处理:自动添加序号后缀(如
图1-1_标题_1.png)
总结
这个工具通过解析 Word 文档的 XML 结构,实现了图片的自动提取和智能命名。核心难点在于正确关联图片元素和标题文本,通过 relationship ID 建立映射关系即可解决。
代码使用 Python 标准库,无需安装额外依赖,开箱即用。
本文是原创文章,采用 CC BY-NC-ND 4.0 协议,完整转载请注明来自 程序员小刘
评论
匿名评论
隐私政策
你无需删除空行,直接评论以获取最佳展示效果