Python 文件解析: Excel / Word / PDF 的解析、处理、预览与下载

1. 总览:同一个入口,不同解析器

当用户上传文件时,技术链路通常是:

  1. 接收文件并保存原件。
  2. 按扩展名选择对应的 Python 解析函数。
  3. 生成可预览数据。
  4. 前端展示原件与解析结果。
  5. 用户编辑后保存。
  6. 提供原件下载与编辑结果下载。

xlsx
docx
pdf
用户上传文件
Upload API
保存原件
按扩展名路由
Excel 处理器
Word 处理器
PDF 处理器
预览数据
预览数据
预览数据
前端预览与编辑
保存编辑结果
下载原件 / 下载编辑结果


2. 插件与依赖清单

先统一约定:本文里"插件"指第三方依赖包(Python 包 / 前端 npm 包)。

2.1 Python 端插件(后端)

插件 版本(示例) 用途 是否必须
fastapi 0.111.0 提供上传、预览、保存、下载 API 必须
uvicorn 0.29.0 启动 FastAPI 服务 必须
python-multipart 0.0.9 支持 multipart/form-data 文件上传 必须
openpyxl 3.1.2 解析/回写 Excel(xlsx) Excel 必须
pandas 2.2.2 Excel/表格辅助清洗(可选但常用) 推荐
python-docx 1.1.0 解析/生成 Word(docx) Word 必须
pymupdf (fitz) 1.24.9 PDF 文本、表格、分页信息提取 PDF 必须

对应 import 写法(便于直接复制代码):

  1. from fastapi import FastAPI, UploadFile, File
  2. from openpyxl import load_workbook
  3. from docx import Document
  4. import fitz(来自 pymupdf

2.2 前端插件(预览与编辑)

插件 版本(示例) 用途 是否必须
xlsx ^0.18.5 浏览器端 Excel 预览(按 sheet 渲染) Excel 预览必须
mammoth ^1.11.0 浏览器端 Word 转 HTML 预览 Word 预览必须
react-data-grid 7.0.0-beta.59 表格编辑组件(Excel/Word 表格/PDF 表格修订) 需要编辑时必须

2.3 可选插件(PDF OCR 兜底)

插件 用途 是否必须
pytesseract + Pillow PDF/图片 OCR 兜底 可选
paddleocr 中文 OCR 兜底(效果通常更好) 可选

2.4 可选插件(兼容旧格式)

插件/工具 用途 是否必须
xlrd 仅当你要直接读取 .xls 时使用 可选
libreoffice (soffice) .doc 转成 .docx,再交给 python-docx 解析 可选

2.5 安装命令示例

bash 复制代码
# Backend
pip install fastapi uvicorn python-multipart openpyxl pandas python-docx pymupdf

# Frontend
npm install xlsx mammoth react-data-grid

# Optional OCR fallback
pip install pytesseract pillow
# or
pip install paddleocr

# Optional legacy-format support
pip install xlrd

3. 通用基础:上传、存储、路由

3.1 上传接口示例

python 复制代码
from pathlib import Path
from fastapi import APIRouter, UploadFile, File

router = APIRouter()

@router.post("/api/files/upload")
async def upload(files: list[UploadFile] = File(...)):
    items = []
    for f in files:
        suffix = Path(f.filename).suffix.lower()
        raw_path = save_raw_file(f)  # 保存原件

        if suffix == ".xlsx":
            file_id = process_excel(raw_path)
            file_type = "excel"
        elif suffix == ".docx":
            file_id = process_word(raw_path)
            file_type = "word"
        elif suffix == ".pdf":
            file_id = process_pdf(raw_path)
            file_type = "pdf"
        else:
            items.append({"filename": f.filename, "error": "unsupported file type"})
            continue

        items.append({"file_id": file_id, "type": file_type, "filename": f.filename})

    return {"items": items}

说明:openpyxl 主流场景是 .xlsxpython-docx 主流场景是 .docx

如果必须支持 .xls / .doc,建议先做"格式转换"再进入本文解析流水线。

3.2 原件下载接口(通用)

python 复制代码
from fastapi.responses import FileResponse

@router.get("/api/files/{file_id}/download/raw")
def download_raw(file_id: str):
    path = locate_raw_file(file_id)
    return FileResponse(path, filename=path.name)

4. Excel:解析、处理、预览、编辑保存、下载

本章用到的插件:openpyxlpandas(可选)、xlsxreact-data-grid

Excel 的特点是天然二维网格,所以处理策略是"保留 sheet + 保留行列"。

4.1 解析(Python)

python 复制代码
from openpyxl import load_workbook


def parse_excel(path: str) -> dict:
    wb = load_workbook(path, data_only=True)
    sheets: dict[str, list[list[str]]] = {}

    for sheet_name in wb.sheetnames:
        ws = wb[sheet_name]
        rows: list[list[str]] = []
        for row in ws.iter_rows(values_only=True):
            rows.append(["" if c is None else str(c) for c in row])
        sheets[sheet_name] = rows

    return {
        "sheet_names": wb.sheetnames,
        "sheets": sheets,
    }

4.2 处理(可选)

常见的通用处理:

  1. 去掉尾部空行。
  2. 统一行长度(短行补空字符串)。
  3. 把非字符串安全转成字符串,避免前端渲染异常。
python 复制代码
def normalize_excel_rows(rows: list[list[str]]) -> list[list[str]]:
    if not rows:
        return [[""]]
    width = max(len(r) for r in rows)
    normalized = []
    for r in rows:
        row = ["" if c is None else str(c) for c in r]
        if len(row) < width:
            row += [""] * (width - len(row))
        normalized.append(row[:width])
    return normalized

4.3 前端预览

前端展示方式:

  1. Sheet 名作为 tab。
  2. 当前 sheet 用 table 或 DataGrid 渲染。
  3. 支持切换 sheet。

4.4 编辑保存

http 复制代码
PUT /api/excel/{file_id}/edit
json 复制代码
{
  "sheet_name": "Sheet1",
  "rows": [["A1", "B1"], ["A2", "B2"]]
}

后端可把编辑结果保存成:

  1. excel_edits.json(便于二次编辑)。
  2. edited.xlsx(便于下载)。

4.5 下载

  1. 原件:GET /api/excel/{file_id}/download/raw
  2. 编辑版:GET /api/excel/{file_id}/download/edited
python 复制代码
@router.get("/api/excel/{file_id}/download/edited")
def download_excel_edited(file_id: str):
    edited_path = build_edited_excel(file_id)  # 根据保存的 rows 重建 xlsx
    return FileResponse(edited_path, filename=edited_path.name)

存储 后端 前端 用户 存储 后端 前端 用户 上传 Excel POST /upload 保存 raw.xlsx parse_excel sheet_names + sheets 编辑单元格 PUT /excel/{file_id}/edit 保存 excel_edits.json / edited.xlsx 下载编辑版 GET /excel/{file_id}/download/edited edited.xlsx


5. Word:解析、处理、预览、编辑保存、下载

本章用到的插件:python-docxmammothreact-data-grid

Word 的天然结构是"段落 + 表格",不建议简单压平为纯二维表。

5.1 解析(Python)

python 复制代码
from docx import Document


def parse_word(path: str) -> dict:
    doc = Document(path)
    blocks: list[dict] = []

    # 段落块
    for p in doc.paragraphs:
        text = p.text.strip()
        if text:
            blocks.append({"type": "paragraph", "text": text})

    # 表格块
    for table_index, table in enumerate(doc.tables):
        rows = []
        for row in table.rows:
            rows.append([cell.text.strip() for cell in row.cells])
        blocks.append({"type": "table", "table_index": table_index, "rows": rows})

    return {"blocks": blocks}

5.2 处理(可选)

  1. 清理连续空段落。
  2. 表格行列补齐。
  3. 对超长文本做安全截断(仅显示时,不改原文)。

5.3 前端预览

Word 常见做法是双视图:

  1. 原件预览:mammoth 转 HTML,阅读体验更接近原文档。
  2. 编辑视图:
    1. 段落块用文本编辑器。
    2. 表格块用 DataGrid。

5.4 编辑保存

http 复制代码
PUT /api/word/{file_id}/edit
json 复制代码
{
  "blocks": [
    {"type": "paragraph", "text": "Updated paragraph"},
    {"type": "table", "table_index": 0, "rows": [["Header1", "Header2"], ["V1", "V2"]]}
  ]
}

后端可以:

  1. 保存 word_edits.json
  2. python-docx 生成 edited.docx

5.5 下载

  1. 原件:GET /api/word/{file_id}/download/raw
  2. 编辑版:GET /api/word/{file_id}/download/edited
python 复制代码
@router.get("/api/word/{file_id}/download/edited")
def download_word_edited(file_id: str):
    edited_docx = build_edited_word_docx(file_id)
    return FileResponse(edited_docx, filename=edited_docx.name)

6. PDF:解析、处理、预览、编辑保存、下载

本章用到的插件:pymupdf(可选 OCR:pytesseract/paddleocr)、react-data-grid

PDF 的关键是"按页处理",因为页面是它的天然单位。

6.1 解析(Python)

python 复制代码
import fitz


def parse_pdf(path: str) -> dict:
    pages = []
    with fitz.open(path) as doc:
        for i in range(doc.page_count):
            page = doc.load_page(i)
            text = page.get_text("text") or ""

            tables = []
            finder = page.find_tables()
            if finder and finder.tables:
                for t in finder.tables:
                    tables.append([[str(c or "").strip() for c in row] for row in t.extract()])

            pages.append({
                "page_no": i + 1,
                "text": text,
                "tables": tables,
                "width": float(page.rect.width),
                "height": float(page.rect.height),
            })

    return {"page_count": len(pages), "pages": pages}

6.2 处理(可选)

  1. find_tables() 抽不到表时,回退到 page.get_text("words") 做词块聚合。
  2. 对文本进行页级摘要(便于快速预览)。
  3. 对识别结果增加 warnings 字段(纯技术提示)。

6.3 前端预览

建议双层:

  1. 原件:iframe/objectURL 直接预览 PDF。
  2. 解析结果:按页展示 text + tables

6.4 编辑保存

http 复制代码
PUT /api/pdf/{file_id}/edit
json 复制代码
{
  "page_no": 1,
  "table_index": 0,
  "rows": [["Col1", "Col2"], ["A", "B"]],
  "notes": "manual correction"
}

后端可保存:

  1. pdf_edits.json
  2. edited.pdf(可选实现:加批注页、嵌入修订信息)

6.5 下载

  1. 原件:GET /api/pdf/{file_id}/download/raw
  2. 编辑版 PDF:GET /api/pdf/{file_id}/download/edited
  3. 编辑记录 JSON(可选):GET /api/pdf/{file_id}/download/edits-json

存储 后端 前端 用户 存储 后端 前端 用户 上传 PDF POST /upload 保存 raw.pdf parse_pdf(分页提取) pages/meta 修订表格 PUT /pdf/{file_id}/edit 保存 pdf_edits.json / edited.pdf 下载编辑版 GET /pdf/{file_id}/download/edited edited.pdf


7. API 清单(示例)

类型 预览接口 保存接口 下载接口
Excel GET /api/excel/{file_id}/preview PUT /api/excel/{file_id}/edit download/raw / download/edited
Word GET /api/word/{file_id}/preview PUT /api/word/{file_id}/edit download/raw / download/edited
PDF GET /api/pdf/{file_id}/preview PUT /api/pdf/{file_id}/edit download/raw / download/edited

说明:这三组接口可以由同一个服务实现。本文拆开写,仅用于按文件格式独立说明技术实现细节。


8. 工程落地建议(纯技术)

  1. 原件一定要保存,不要只存解析结果。
  2. 编辑结果建议落 JSON,再按需导出编辑版文件。
  3. 下载接口统一加 Content-Disposition: attachment
  4. 所有预览数据都做空值与类型兜底,前端会省很多判断。
  5. 文件名建议带时间戳,例如:
    1. report_edited_20260224_1530.xlsx
    2. doc_edited_20260224_1530.docx
    3. scan_edited_20260224_1530.pdf

9. 效果

excel预览效果

word预览效果

pdf 预览效果

相关推荐
hhb_61818 小时前
Groovy语法进阶与工程实践指南
开发语言·python
hmywillstronger18 小时前
Rhino 中文字方向问题的解析与解决方案
python
AI技术增长19 小时前
Pytorch图像去噪实战(四):Attention UNet图像去噪实战,让模型重点恢复边缘和纹理区域
人工智能·pytorch·python
2401_8330336219 小时前
如何修复固定定位头部容器中悬浮下拉菜单的错位问题
jvm·数据库·python
z44247532619 小时前
CSS Grid布局如何实现网格项目的自动增长_设置grid-auto-flow- row
jvm·数据库·python
GeLx19 小时前
从反爬角度:Playwright CDP 模式、Playwright 传统模式与 DrissionPage 的比较
python·程序人生·playwright·drissionpage·pyppeteer·浏览器自动化控制
m0_7403524219 小时前
如何在 SvelteKit 中为动态加载的图片实现响应式悬停覆盖层
jvm·数据库·python
TechWayfarer19 小时前
IP归属地运营商能解决什么问题?风控/增长/数据平台落地实践(附API代码)
开发语言·网络·python·网络协议·tcp/ip
雷帝木木19 小时前
Python 并发编程的高级技巧与性能优化
人工智能·python·深度学习·机器学习
Flittly20 小时前
【LangGraph新手村系列】(1)LangGraph 入门:StateGraph 与带记忆的 ReAct 循环
python·langchain