pdf转md的一种思路
自己写的一套可行的流程
核心思路
将PDF转换为Markdown的过程分为四个关键步骤:
- PDF到Word转换
- Word到Markdown转换
- 水印去除
- 图片处理
下面我们将详细介绍每个步骤,并附上相关的代码实现。
具体方案及代码实现
1. PDF到Word转换
使用Microsoft Office将PDF转换为Word格式。这一步利用了Office强大的布局识别能力,能更好地处理复杂的PDF结构。
def convert_pdf_to_word(pdf_path, word_path):
word = win32com.client.Dispatch("Word.Application")
try:
word.Visible = False
doc = word.Documents.Open(pdf_path)
doc.SaveAs(word_path, FileFormat=16) # 16 表示 .docx 格式
doc.Close()
except Exception as e:
print(f"PDF 转 Word 过程中发生错误: {str(e)}")
raise
finally:
word.Quit()
2. Word到Markdown转换
采用Aspose.Words库将Word文档转换为Markdown。Aspose.Words在保持文档结构和格式方面表现出色。
def convert_word_to_markdown_and_clean(input_file, output_folder, original_filename):
file_name = original_filename or os.path.splitext(os.path.basename(input_file))[0]
images_folder = os.path.join(output_folder, "images")
os.makedirs(images_folder, exist_ok=True)
doc = aw.Document(input_file)
save_options = aw.saving.MarkdownSaveOptions()
save_options.images_folder = images_folder
save_options.images_folder_alias = "./images"
markdown_file = os.path.join(output_folder, f"{file_name}.md")
doc.save(markdown_file, save_options)
3. 水印去除
使用正则表达式清理Aspose生成的水印和其他不必要的标记,确保输出的Markdown文件整洁。(当然,用aspose付费版可以忽略)
def post_process_markdown(text):
patterns = [
r'\n\*\*Evaluation Only\. Created with Aspose\.Words\. Copyright 2003-2024 Aspose Pty Ltd\.\*\*\n',
r'!\[ref\d+\]',
r'\n\*\*Created with an evaluation copy of Aspose\.Words\. To remove all limitations, you can use Free Temporary License \[https://products\.aspose\.com/words/temporary-license/\*\*\]\(https://products\.aspose\.com/words/temporary-license/\)\*\*\n',
r'\*\*This document was truncated here because it was created in the Evaluation Mode\.\*\*'
]
for pattern in patterns:
text = re.sub(pattern, '', text)
return text
4. 图片处理
- 创建单独的images文件夹存储文档中的图片
- 设置相对路径确保Markdown中的图片链接正确
- 删除可能的水印图片(第一张图片必定是水印)
def convert_word_to_markdown_and_clean(input_file, output_folder, original_filename):
# ... [前面的代码]
# 删除images文件夹中的第一张图片(水印)
image_files = sorted(glob.glob(os.path.join(images_folder, "*")))
if image_files:
watermark_image = image_files[0]
os.remove(watermark_image)
print(f"已删除水印图片: {watermark_image}")
局限
- 依赖Microsoft Office,在某些环境(如服务器)中不便使用
- 毕竟是正则去除水印,效果不稳定,但是免费
使用方法
- 完整代码在文章末尾,复制到本地
- 安装所需的Python库:
pip install pywin32 aspose-words
- 运行脚本:
python pdf_word_to_markdown.py
- 按照提示输入包含PDF和Word文件的输入文件夹路径和输出文件夹路径
- 确认开始转换
- 确保您的系统上安装了Microsoft Office
- 转换大文件可能需要一些时间,请耐心等待
- 转换结果可能因原始文档的复杂程度而异
完整代码
以下是实现这个PDF到Markdown转换工具的完整Python代码:
import os
import win32com.client
import aspose.words as aw
import re
import time
import glob
def convert_pdf_to_word(pdf_path, word_path):
"""
使用 Microsoft Word 将 PDF 文件转换为 Word 文档
:param pdf_path: PDF 文件的路径
:param word_path: 输出 Word 文件的路径
"""
word = win32com.client.Dispatch("Word.Application")
try:
word.Visible = False
print(f"正在打开 PDF 文件: {pdf_path}")
doc = word.Documents.Open(pdf_path)
print("PDF 文件已成功打开")
print(f"正在保存为 Word 文件: {word_path}")
doc.SaveAs(word_path, FileFormat=16) # 16 表示 .docx 格式
print("Word 文件已保存")
doc.Close()
print("Word 文档已关闭")
# 等待文件创建完成
for _ in range(10): # 尝试10次,每次等待1秒
if os.path.exists(word_path):
print(f"Word 文件已成功创建: {word_path}")
return
time.sleep(1)
if not os.path.exists(word_path):
raise FileNotFoundError(f"Word 文件未能成功创建: {word_path}")
except Exception as e:
print(f"PDF 转 Word 过程中发生错误: {str(e)}")
raise
finally:
word.Quit()
def post_process_markdown(text):
"""
对转换后的 Markdown 文本进行后处理
:param text: 原始 Markdown 文本
:return: 处理后的 Markdown 文本
"""
# 移除水印
pattern = r'\n\*\*Evaluation Only\. Created with Aspose\.Words\. Copyright 2003-2024 Aspose Pty Ltd\.\*\*\n'
cleaned_text = re.sub(pattern, '', text)
ref_pattern = r'!\[ref\d+\]'
cleaned_text = re.sub(ref_pattern, '', cleaned_text)
new_watermark_pattern = r'\n\*\*Created with an evaluation copy of Aspose\.Words\. To remove all limitations, you can use Free Temporary License \[https://products\.aspose\.com/words/temporary-license/\*\*\]\(https://products\.aspose\.com/words/temporary-license/\)\*\*\n'
cleaned_text = re.sub(new_watermark_pattern, '', cleaned_text)
final_pattern = r'\*\*This document was truncated here because it was created in the Evaluation Mode\.\*\*'
cleaned_text = re.sub(final_pattern, '', cleaned_text)
return cleaned_text
def convert_word_to_markdown_and_clean(input_file, output_folder, original_filename):
try:
# 使用原始文件名(不包括扩展名)
file_name = original_filename or os.path.splitext(os.path.basename(input_file))[0]
# 创建images子文件夹
images_folder = os.path.join(output_folder, "images")
os.makedirs(images_folder, exist_ok=True)
# 加载Word文档
doc = aw.Document(input_file)
# 设置保存选项
save_options = aw.saving.MarkdownSaveOptions()
save_options.images_folder = images_folder
save_options.images_folder_alias = "./images" # 设置图片文件夹别名为相对路径
# 将文档转换为Markdown
markdown_file = os.path.join(output_folder, f"{file_name}.md")
doc.save(markdown_file, save_options)
# 读取生成的Markdown文件
with open(markdown_file, "r", encoding="utf-8") as file:
markdown_content = file.read()
# 应用后处理
processed_content = post_process_markdown(markdown_content)
# 保存处理后的内容到新文件
with open(markdown_file, "w", encoding="utf-8") as file:
file.write(processed_content)
print(f"转换完成!结果保存在 {markdown_file}")
# 删除images文件夹中的第一张图片(水印)
image_files = sorted(glob.glob(os.path.join(images_folder, "*")))
if image_files:
watermark_image = image_files[0]
os.remove(watermark_image)
print(f"已删除水印图片: {watermark_image}")
except Exception as e:
print(f"发生错误: {str(e)}")
raise
def convert_to_markdown(input_path, output_folder):
# 获取输入文件的名称(不包括扩展名)
file_name = os.path.splitext(os.path.basename(input_path))[0]
# 创建以文件名命名的输出文件夹
file_output_folder = os.path.join(output_folder, file_name)
os.makedirs(file_output_folder, exist_ok=True)
if input_path.lower().endswith('.pdf'):
# 临时 Word 文件路径
temp_word_path = os.path.join(file_output_folder, "temp_word_file.docx")
try:
# 步骤 1:将 PDF 转换为 Word
convert_pdf_to_word(input_path, temp_word_path)
print("PDF 已成功转换为 Word")
# 步骤 2:将 Word 转换为 Markdown 并清理
convert_word_to_markdown_and_clean(temp_word_path, file_output_folder, file_name)
finally:
# 删除临时 Word 文件
if os.path.exists(temp_word_path):
os.remove(temp_word_path)
print(f"临时 Word 文件已删除: {temp_word_path}")
elif input_path.lower().endswith(('.doc', '.docx')):
# 直接将 Word 转换为 Markdown
convert_word_to_markdown_and_clean(input_path, file_output_folder, None)
else:
print(f"不支持的文件格式: {input_path}")
raise ValueError(f"不支持的文件格式: {input_path}")
def count_files(folder):
pdf_count = len(glob.glob(os.path.join(folder, "*.pdf")))
word_count = len(glob.glob(os.path.join(folder, "*.docx"))) + len(glob.glob(os.path.join(folder, "*.doc")))
return pdf_count + word_count
def batch_convert(input_folder, output_folder):
"""
批量转换文件夹中的所有PDF和Word文件为Markdown
:param input_folder: 包含PDF和Word文件的输入文件夹路径
:param output_folder: 保存Markdown文件的输出文件夹路径
"""
# 确保输出文件夹存在
os.makedirs(output_folder, exist_ok=True)
# 获取输入文件夹中所有的PDF和Word文件
input_files = glob.glob(os.path.join(input_folder, "*.pdf"))
input_files.extend(glob.glob(os.path.join(input_folder, "*.docx")))
input_files.extend(glob.glob(os.path.join(input_folder, "*.doc")))
if not input_files:
print(f"在 {input_folder} 中没有找到PDF或Word文件。")
return
total_files = len(input_files)
successful_files = 0
failed_files = []
for index, input_file in enumerate(input_files, start=1):
print(f"正在处理: {input_file} ({index}/{total_files}, {index / total_files * 100:.2f}%)")
try:
convert_to_markdown(input_file, output_folder)
successful_files += 1
except Exception as e:
print(f"处理文件 {input_file} 时出错: {str(e)}")
failed_files.append(input_file)
print(f"完成处理: {input_file}")
print("-" * 50)
time.sleep(2)
print(f"所有文件处理完成!成功转换 {successful_files} 个文件。")
if failed_files:
print(f"以下 {len(failed_files)} 个文件处理失败:")
for file in failed_files:
print(f"- {file}")
def main():
print("欢迎使用PDF和Word到Markdown批量转换工具!")
while True:
input_folder = input("请输入包含PDF和Word文件的文件夹路径: ").strip()
if os.path.isdir(input_folder):
break
else:
print("输入的路径不是有效的文件夹,请重新输入。")
while True:
output_folder = input("请输入保存Markdown文件的输出文件夹路径: ").strip()
if os.path.isdir(output_folder) or not os.path.exists(output_folder):
break
else:
print("输入的路径不是有效的文件夹,请重新输入。")
file_count = count_files(input_folder)
print(f"\n输入文件夹: {input_folder}")
print(f"输出文件夹: {output_folder}")
print(f"待处理文件数量: {file_count}\n")
confirm = input("确认开始转换吗?(y/n): ").strip().lower()
if confirm == 'y':
batch_convert(input_folder, output_folder)
else:
print("操作已取消。")
if __name__ == "__main__":
main()
评论区