乐于分享
好东西不私藏

扫描版PDF原格式转Word

扫描版PDF原格式转Word

目标:将扫描版PDF(即图片格式,无法直接复制文字)文件,原格式转换成可编辑的Word (.docx) 文件。

实现思路:

PDF转Word通常做法是,将PDF转成文本,然后根据要求的Word格式,对文本进行修改,但有的时候PDF格式是各种各样的,可能字体、颜色、大小不一样,没有针对每个PDF生成原格式的Word。

下面我介绍一下我实现的思路:

  1. 拆PDF:将PDF的每一页,都变成一张清晰的图片;

  2. 转HTML:让AI识别图片上面所有内容,包含文字、字体、表格、标题,将内容以HTML格式输出;

  3. 转Word:用电脑上的Word软件打开HTML文件,然后“另存为”  .docx  格式。

(温馨提示,Word也可以打开HTML文件)

还是老规矩,只展示最核心的、能跑通的代码,方便理解每一步在干什么。如果不想看过程,可以直接跳至文末,获取完整代码。

准备工作:

使用工具:Python

用到的库:os , requests ,  base64 , fitz  (注意:安装时是  pip install pymupdf ),win32com  (用于操作Word,安装命令: pip install pywin32 )

这里用到了两个大模型,都有免费token:

  1. qwen-vl-max,来自阿里云百炼平台,负责看图识字并生成HTML。你需要去百炼平台申请一个API密钥。没有用OCR大模型,主要因为OCR大模型在生成HTML的时候效果不行

  2. Qwen3-8B,用的是硅基流动的平台,负责检查和修正HTML格式。你也需要去相应平台申请API密钥。

如果嫌麻烦也可以用同一个平台的,只不过可能要花钱。

代码实现

import os#负责打开或者删除文件import requests#调用大模型import base64#将图片转成字符串,方便传输import fitz#将PDF分割成图片import win32com.clientdef pdf_to_image(pdf_path, total_pages):    """用PyMuPDF将PDF转为图片"""    doc = fitz.open(pdf_path)    for page_num in range(total_pages):        page = doc[page_num]                # 直接指定输出为 A4 尺寸 + 300DPI        pix = page.get_pixmap(            dpi=300,            clip=fitz.Rect(00595842)  # 595×842 = A4 标准点数        )        temp_img = f"temp_{page_num}.png"        pix.save(temp_img)    doc.close()def image_to_html(image_path):      #将图片转成字符串    with open(image_path, "rb"as f:        b64 = base64.b64encode(f.read()).decode("utf-8")    """调用qwen-vl识别图片,输出HTML格式"""    resp = requests.post(        "https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions",        headers={            "Authorization"f"Bearer {API_KEY}",            "Content-Type""application/json"        },        json={            "model": MODEL,            "messages": [{                "role""user",                "content": [                    {"type""text""text""请识别这张图片中的所有内容,保持所有原格式和结构,以标准HTML格式输出。注意:保留标题层级、段落、列表、表格列宽和行高、不同段落的字体和颜色等格式。只输出HTML代码,不要有其他说明。"},                    {"type""image_url""image_url": {"url"f"data:image/png;base64,{b64}"}}                ]            }],            "temperature"0.1,            "max_tokens"4096        },        timeout=120    )    #提取转化内容    raw_content=resp.json()["choices"][0]["message"]["content"]    #print(raw_content)    clean_text = raw_content.replace("```html""").replace("```""").strip()    return clean_textdef html_bz(html_txt):    API_KEY_Q = "硅基流动的KEY"    MODEL_Q = "Qwen/Qwen3-8B"    TSC= html_txt +'/n'+'你自己不要做额外修改,不要改变文本顺序,不要改变表格颜色和行高,如果html格式不符合word要求,只修改html格式即可,直接输出html代码,其他不要输出'    resp = requests.post(        "https://api.siliconflow.cn/v1/chat/completions",        headers={"Authorization"f"Bearer {API_KEY_Q}""Content-Type""application/json"},        json={            "model": MODEL_Q,            "messages": [{                "role""user",                "content": TSC            }],            "temperature"0.0,            "max_tokens"4092,            "stream"False       # 显式关闭流式输出        },        timeout=180    )    #提取转化内容    html_f=resp.json()["choices"][0]["message"]["content"]    return html_fdef html2word(OUTPUT_HTML,OUTPUT_WORD):    # 获取当前文件夹路径(你的 .py 文件所在目录)    current_dir = os.path.dirname(os.path.abspath(__file__))    # HTML 路径    html_path = os.path.join(current_dir, OUTPUT_HTML)    # 最终要保存的 docx 路径(明确指定)    docx_path = os.path.join(current_dir, OUTPUT_WORD)    # 启动 Word 应用    word = win32com.client.Dispatch("Word.Application")    word.Visible = False  # 隐藏 Word 窗口(True 则显示)    # 新建文档    doc = word.Documents.Add()    try:        # 打开 HTML 文件        doc = word.Documents.Open(html_path)        # 另存为 .docx 格式        doc.SaveAs2(docx_path,16)  # 16 = wdFormatXMLDocument (.docx)        doc.Close()        print(f"通过 Word 客户端另存为成功:{OUTPUT_WORD}")    finally:        # 关闭 Word 应用        word.Quit()# 核心流程:PDF转图片→ 逐页解析 → 合并HTMLif __name__ == "__main__":    # 核心配置(替换为你的信息)    API_KEY = "阿里云百炼的KEY"  # 在百炼控制台获取    MODEL = "qwen-vl-max"    PDF_PATH = r"D:\测试.pdf"  # 待解析PDF    OUTPUT_HTML = "output.html"  # 输出html文件    OUTPUT_WORD = "output.docx"#另存为docx文件    #获取PDF总页数    doc = fitz.open(PDF_PATH)    total_pages = len(doc)    doc.close()    pdf_to_image(PDF_PATH, total_pages)    print("完成PDF转图片")    # 2. 逐页解析    html_content = ''    for page_idx in range(total_pages):        # 转图片        img_path = f"temp_{page_idx}.png"        #输出html        page_html = image_to_html(img_path)        html_content +='\n' + page_html        print(f"解析完第{page_idx+1}页")        # 删除临时图片        os.remove(img_path)    # 3. 将非标准HTML转成标准HTML,并保存成html文件    html_content = html_bz(html_content)    with open(OUTPUT_HTML, "w", encoding="utf-8"as f:        f.write(html_content)    #4.HTML文件转WORD文件    html2word(OUTPUT_HTML,OUTPUT_WORD)    print(f"解析完成!结果已保存至 {html_content}")

效果展示

左边是原PDF,右边是输出的word

格式能还原个90%,在表格行高方面还是不能还原,但是字体、段落、表格表头颜色都能还原。
写在最后
主体架构早就写好了,但是一直卡在莫名其妙的点,PDF最后一页是表格,然后这个表格的HTML内容不能被Word识别,最后无奈只能让小参数的大语言模型重新修改vl大模型生成HTML内容。
如果你只是识别一个扫描版PDF文档:
1)直接丢给现有主流大模型APP,让他们识别并以HTML格式输出;
2)创建一个空文本文件,将HTML复制进去,文本文件存储为HTML格式,记得是utf-8编码,类似下图
3)然后用Word打开这个文件,另存为.docx格式就完成由扫描版PDF原格式转Word了。
如果你有什么其他的好方法,欢迎在评论区交流。