最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Python使用Flask實現(xiàn)將DOCX轉(zhuǎn)為Markdown

 更新時間:2026年01月29日 14:59:54   作者:weixin_46244623  
這篇文章主要為大家詳細(xì)介紹了如何基于Python的Flask框架編寫一個項目,能夠?qū)⑸蟼鞯?docx文件轉(zhuǎn)換為 Markdown,并提取圖片以供下載預(yù)覽,感興趣的小伙伴可以了解下

摘要

本文演示一個基于 Flask 的后端 + 簡單前端頁面的項目,能夠?qū)⑸蟼鞯?nbsp;.docx 文件轉(zhuǎn)換為 Markdown,并提取圖片以供下載/預(yù)覽。

包含運(yùn)行方式、關(guān)鍵代碼解析(app.py)、前端 templates/index.html 功能說明,以及常見注意事項。

項目簡介

功能:上傳 .docx → 返回 Markdown 文本 + 提取圖片(通過 API 提供圖片訪問 URL),前端支持預(yù)覽與打包下載 ZIP。

適用場景:將 Word 文檔內(nèi)容遷移到博客、技術(shù)文檔、知識庫時快速生成 Markdown。

環(huán)境與依賴

Python 3.8+(示例中也可用 Python 3.13)

依賴見 requirements.txt:

python-docx==0.8.11
Flask==2.3.0
Flask-CORS==4.0.0
Werkzeug==2.3.0

安裝命令:

python -m venv venv
source venv/bin/activate
pip install -r requirements.txt

項目結(jié)構(gòu)(簡要)

app.py — Flask 后端,負(fù)責(zé)接收上傳、解析 DOCX、提取圖片并返回 Markdown。

templates/index.html — 前端頁面,基于 Vue 3,提供上傳、展示與下載功能。

uploads/ — 存放上傳的文件與提取的圖片(運(yùn)行時生成)。

完整代碼

#!/usr/bin/env python3
"""
DOCX 轉(zhuǎn) Markdown 的 Flask API 后端
"""

from flask import Flask, request, jsonify, send_file, render_template
from flask_cors import CORS
from docx import Document
import os
import io
import json
from pathlib import Path
from werkzeug.utils import secure_filename

app = Flask(__name__, static_folder='templates', static_url_path='')

CORS(app)

# 配置上傳文件夾
UPLOAD_FOLDER = 'uploads'
ALLOWED_EXTENSIONS = {'docx', 'doc'}
MAX_FILE_SIZE = 50 * 1024 * 1024  # 50MB

if not os.path.exists(UPLOAD_FOLDER):
    os.makedirs(UPLOAD_FOLDER)

app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
app.config['MAX_CONTENT_LENGTH'] = MAX_FILE_SIZE


def allowed_file(filename):
    """檢查文件擴(kuò)展名"""
    return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS


def extract_images_from_run(run, image_dir, image_counter, doc_part=None):
    """從 run 元素中提取圖片"""
    images = []
    
    for drawing in run.element.findall('.//{http://schemas.openxmlformats.org/wordprocessingml/2006/main}drawing'):
        for blip in drawing.findall('.//{http://schemas.openxmlformats.org/drawingml/2006/main}blip'):
            embed_id = blip.get('{http://schemas.openxmlformats.org/officeDocument/2006/relationships}embed')
            if embed_id:
                try:
                    # 從關(guān)系中獲取圖片
                    image_part = run.part.rels[embed_id].target_part
                    image_data = image_part.blob
                    
                    # 確定圖片擴(kuò)展名
                    content_type = image_part.content_type
                    ext_map = {
                        'image/jpeg': 'jpg',
                        'image/png': 'png',
                        'image/gif': 'gif',
                        'image/bmp': 'bmp',
                        'image/tiff': 'tiff',
                        'image/webp': 'webp'
                    }
                    ext = ext_map.get(content_type, 'png')
                    
                    image_counter += 1
                    image_filename = f"image_{image_counter}.{ext}"
                    image_path = os.path.join(image_dir, image_filename)
                    
                    # 保存圖片
                    with open(image_path, 'wb') as f:
                        f.write(image_data)
                    
                    images.append({
                        'filename': image_filename,
                        'path': image_path,
                        'counter': image_counter
                    })
                except Exception as e:
                    pass
    
    return images, image_counter


def convert_docx_to_markdown(docx_path, image_dir=None):
    """轉(zhuǎn)換 DOCX 文件為 Markdown"""
    
    if image_dir is None:
        image_dir = os.path.join(UPLOAD_FOLDER, 'images')
    
    if not os.path.exists(image_dir):
        os.makedirs(image_dir)
    
    # 加載 DOCX 文檔
    doc = Document(docx_path)
    
    markdown_content = []
    image_counter = 0
    
    # 處理段落和圖片
    for para in doc.paragraphs:
        # 檢查段落中的圖片
        for run in para.runs:
            images, image_counter = extract_images_from_run(run, image_dir, image_counter)
            for img in images:
                rel_path = os.path.join('images', img['filename'])
                markdown_content.append(f"![image]({rel_path})")
                markdown_content.append("")
        
        text = para.text.strip()
        
        if not text:
            markdown_content.append("")
            continue
        
        # 檢查段落樣式
        style = para.style.name if para.style else ""
        
        # 處理標(biāo)題
        if "Heading 1" in style:
            markdown_content.append(f"# {text}")
        elif "Heading 2" in style:
            markdown_content.append(f"## {text}")
        elif "Heading 3" in style:
            markdown_content.append(f"### {text}")
        elif "Heading 4" in style:
            markdown_content.append(f"#### {text}")
        elif "Heading 5" in style:
            markdown_content.append(f"##### {text}")
        elif "Heading 6" in style:
            markdown_content.append(f"###### {text}")
        else:
            # 處理文本格式
            formatted_text = process_runs(para.runs)
            if formatted_text:
                markdown_content.append(formatted_text)
            else:
                markdown_content.append(text)
    
    # 處理表格
    for table in doc.tables:
        markdown_content.append("")
        markdown_table, image_counter = convert_table_to_markdown(table, image_dir, image_counter)
        markdown_content.extend(markdown_table)
        markdown_content.append("")
    
    result = "\n".join(markdown_content)
    
    return result, image_counter


def process_runs(runs):
    """處理文本 runs 以處理加粗、斜體等格式"""
    result = []
    
    for run in runs:
        text = run.text
        
        if not text:
            continue
        
        # 處理加粗
        if run.bold:
            text = f"**{text}**"
        
        # 處理斜體
        if run.italic:
            text = f"*{text}*"
        
        # 處理下劃線
        if run.underline:
            text = f"__{text}__"
        
        result.append(text)
    
    return "".join(result).strip()


def convert_table_to_markdown(table, image_dir, image_counter):
    """將 DOCX 表格轉(zhuǎn)換為 Markdown 表格格式"""
    markdown_lines = []
    
    # 處理每一行
    for i, row in enumerate(table.rows):
        cells = row.cells
        row_content = []
        
        for cell in cells:
            # 從單元格中獲取文本
            cell_parts = []
            for para in cell.paragraphs:
                # 檢查段落中的圖片
                for run in para.runs:
                    images, image_counter = extract_images_from_run(run, image_dir, image_counter)
                    for img in images:
                        rel_path = os.path.join('images', img['filename'])
                        cell_parts.append(f"![img]({rel_path})")
                
                para_text = para.text.strip()
                if para_text:
                    cell_parts.append(para_text)
            
            cell_text = " ".join(cell_parts).strip()
            row_content.append(cell_text)
        
        # 添加行到 markdown
        markdown_lines.append("| " + " | ".join(row_content) + " |")
        
        # 在表頭行(第一行)后添加分隔符
        if i == 0:
            separator = "|" + "|".join([" --- " for _ in row_content]) + "|"
            markdown_lines.append(separator)
    
    return markdown_lines, image_counter


@app.route('/', methods=['GET'])
def index():
    """返回主頁面"""
    return send_file('templates/index.html', mimetype='text/html')


@app.route('/api/health', methods=['GET'])
def health():
    """健康檢查"""
    return jsonify({'status': 'ok'})


@app.route('/api/convert', methods=['POST'])
def convert():
    """轉(zhuǎn)換 DOCX 文件為 Markdown 和圖片"""
    try:
        # 檢查是否有文件上傳
        if 'file' not in request.files:
            return jsonify({'status': 'error', 'message': '沒有上傳文件'}), 400
        
        file = request.files['file']
        
        if file.filename == '':
            return jsonify({'status': 'error', 'message': '文件名為空'}), 400
        
        if not allowed_file(file.filename):
            return jsonify({'status': 'error', 'message': '只支持 .docx 格式文件'}), 400
        
        # 保存上傳的文件
        filename = secure_filename(file.filename)
        filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename)
        file.save(filepath)
        
        # 創(chuàng)建專門的圖片目錄
        image_dir = os.path.join(app.config['UPLOAD_FOLDER'], Path(filename).stem + '_images')
        
        # 轉(zhuǎn)換 DOCX 到 Markdown
        markdown_content, image_count = convert_docx_to_markdown(filepath, image_dir)
        
        # 獲取圖片列表
        images = []
        if os.path.exists(image_dir):
            for img_file in os.listdir(image_dir):
                if os.path.isfile(os.path.join(image_dir, img_file)):
                    images.append({
                        'name': img_file,
                        'path': f"/api/image/{Path(filename).stem + '_images'}/{img_file}"
                    })
        
        # 替換 markdown 中的圖片路徑
        for img in images:
            # 將相對路徑替換為 API 路徑
            old_path = f"images/{img['name']}"
            markdown_content = markdown_content.replace(old_path, img['path'])
        
        # 清理上傳的 docx 文件(可選)
        try:
            os.remove(filepath)
        except:
            pass
        
        return jsonify({
            'status': 'success',
            'markdown': markdown_content,
            'images': images,
            'image_count': image_count
        })
    
    except Exception as e:
        return jsonify({'status': 'error', 'message': str(e)}), 500


@app.route('/api/image/<path:filepath>', methods=['GET'])
def get_image(filepath):
    """獲取提取的圖片"""
    try:
        full_path = os.path.join(app.config['UPLOAD_FOLDER'], filepath)
        
        # 安全檢查
        if not os.path.abspath(full_path).startswith(os.path.abspath(app.config['UPLOAD_FOLDER'])):
            return jsonify({'status': 'error', 'message': '非法請求'}), 403
        
        if not os.path.exists(full_path):
            return jsonify({'status': 'error', 'message': '文件不存在'}), 404
        
        return send_file(full_path)
    
    except Exception as e:
        return jsonify({'status': 'error', 'message': str(e)}), 500


if __name__ == '__main__':
    app.run(debug=False, host='0.0.0.0', port=5000)

運(yùn)行步驟

啟動服務(wù):

python app.py
# 或
python3 app.py

服務(wù)默認(rèn)監(jiān)聽 0.0.0.0:5000,打開瀏覽器訪問 http://localhost:5000/ 使用前端頁面。

關(guān)鍵代碼解析

1.應(yīng)用與配置

  • 創(chuàng)建 Flask 實例:app = Flask(__name__, static_folder='templates', static_url_path='')
  • 配置上傳目錄與最大文件大?。篣PLOAD_FOLDER = 'uploads'、MAX_FILE_SIZE = 50 * 1024 * 1024

2.允許的文件檢查

allowed_file(filename):只允許 docx/doc 擴(kuò)展名。

3.圖片提?。篹xtract_images_from_run(run, image_dir, image_counter, doc_part=None)

通過讀取 run 元素的 drawing/blip,使用關(guān)系 id(embed)找到 image_part,讀取其 blob 保存為文件;根據(jù) content_type 推斷擴(kuò)展名。

4.DOCX 轉(zhuǎn) Markdown:convert_docx_to_markdown(docx_path, image_dir=None)

  • 使用 python-docx 的 Document(docx_path) 加載文檔。
  • 遍歷 doc.paragraphs:對每個 run 提取圖片、根據(jù)段落樣式(如 Heading 1)生成對應(yīng) Markdown 標(biāo)題,非標(biāo)題段落調(diào)用 process_runs 處理加粗/斜體/下劃線等;遍歷 doc.tables 使用 convert_table_to_markdown 轉(zhuǎn)換表格。

5.文本樣式處理:process_runs(runs)

根據(jù) run.bold、run.italic、run.underline 包裹 **、*、__,然后拼接返回。

6.表格轉(zhuǎn)換:convert_table_to_markdown(table, image_dir, image_counter)

逐行逐單元格處理,單元格內(nèi)可能包含圖片(同樣提?。?,生成 Markdown 表格和表頭分隔符。

7.API 路由

  • GET /:返回前端頁面 templates/index.html。
  • GET /api/health:健康檢查,返回 {'status': 'ok'}。
  • POST /api/convert:接收上傳文件(字段 file),保存文件、創(chuàng)建圖片目錄 {stem}_images、調(diào)用轉(zhuǎn)換并返回 JSON(包含 markdown、images 列表和 image_count)。
  • GET /api/image/<path:filepath>:從 uploads/ 安全返回圖片文件(帶路徑校驗)。

前端 templates/index.html 功能概覽

基于 Vue 3(CDN)實現(xiàn),交互包括:

  • 拖拽或點(diǎn)擊上傳 .docx 文件(前端做擴(kuò)展名校驗)。
  • 顯示已選文件名和大小,調(diào)用 /api/convert 上傳文件并等待結(jié)果;期間顯示 Loading 狀態(tài)。
  • 轉(zhuǎn)換成功后彈窗顯示 Markdown 內(nèi)容、預(yù)覽(簡單解析)和圖片列表。
  • 支持將 Markdown + 圖片打包為 ZIP 下載(使用 JSZip)。

前端渲染要點(diǎn):

  • renderMarkdown:簡單將 Markdown → HTML(支持標(biāo)題、加粗、斜體、代碼塊、圖片、鏈接、列表的基礎(chǔ)轉(zhuǎn)換),并把圖片路徑替換為后端返回的 img.path。
  • 下載邏輯:先把 document.md 寫入 zip,再 fetch 每個圖片的 URL 把 Blob 寫入 zip,最后觸發(fā)瀏覽器下載。

示例:用 curl 調(diào)用 API

curl -F "file=@/path/to/test.docx" http://localhost:5000/api/convert

響應(yīng)后可直接訪問圖片:

http://localhost:5000/api/image/<yourfile_stem>_images/image_1.png

常見問題與注意事項

python-docx 解析并非 100% 保留 Word 的復(fù)雜布局和樣式,復(fù)雜的段落結(jié)構(gòu)(嵌套列表、復(fù)雜表格合并單元格)可能需要額外處理。

圖片提取通過關(guān)系表(rels)和 drawing/blip 節(jié)點(diǎn)查找,若有特殊嵌入方式(例如 OLE)可能無法提取。

并發(fā)上傳時請注意服務(wù)器磁盤與內(nèi)存限制,生產(chǎn)環(huán)境建議:

  • 使用臨時目錄并定期清理過期文件;
  • 通過反向代理(如 Nginx)設(shè)置上傳大小限制與超時;
  • 將圖片和生成文件存儲到對象存儲(OSS/S3)。

以上就是Python使用Flask實現(xiàn)將DOCX轉(zhuǎn)為Markdown的詳細(xì)內(nèi)容,更多關(guān)于Python DOCX轉(zhuǎn)為Markdown的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 使用GitHub和Python實現(xiàn)持續(xù)部署的方法

    使用GitHub和Python實現(xiàn)持續(xù)部署的方法

    這篇文章主要介紹了使用GitHub和Python實現(xiàn)持續(xù)部署的方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2019-05-05
  • pandas loc iloc ix用法詳細(xì)分析

    pandas loc iloc ix用法詳細(xì)分析

    pandas處理數(shù)據(jù)時,我們會經(jīng)??吹絛ataframe結(jié)構(gòu)使用loc, iloc, ix等方法,那么這些方法到底有啥區(qū)別,下面我們來進(jìn)行詳細(xì)分析,感興趣的朋友跟隨小編一起看看吧
    2023-01-01
  • python模仿網(wǎng)頁版微信發(fā)送消息功能

    python模仿網(wǎng)頁版微信發(fā)送消息功能

    這篇文章主要介紹了python模仿網(wǎng)頁版微信發(fā)送消息功能,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-02-02
  • 如何更改Pycharm配置文件的存放路徑

    如何更改Pycharm配置文件的存放路徑

    Pycharm配置文件默認(rèn)是放在C盤的,修改存放位置,這樣系統(tǒng)重裝的時候就不會不見了,下面這篇文章主要給大家介紹了關(guān)于如何更改Pycharm配置文件的存放路徑的相關(guān)資料,需要的朋友可以參考下
    2022-12-12
  • 基于Django?websocket實現(xiàn)視頻畫面的實時傳輸功能(最新推薦)

    基于Django?websocket實現(xiàn)視頻畫面的實時傳輸功能(最新推薦)

    Django?Channels?是一個用于在?Django框架中實現(xiàn)實時、異步通信的擴(kuò)展庫,本文給大家介紹基于Django?websocket實現(xiàn)視頻畫面的實時傳輸案例,本案例是基于B/S架構(gòu)的視頻監(jiān)控畫面的實時傳輸,使用django作為服務(wù)端的開發(fā)框架,需要的朋友可以參考下
    2023-06-06
  • Python讀取yaml文件的詳細(xì)教程

    Python讀取yaml文件的詳細(xì)教程

    這篇文章主要給大家介紹了關(guān)于Python讀取yaml文件的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-07-07
  • Python基于遞歸算法實現(xiàn)的走迷宮問題

    Python基于遞歸算法實現(xiàn)的走迷宮問題

    這篇文章主要介紹了Python基于遞歸算法實現(xiàn)的走迷宮問題,結(jié)合迷宮問題簡單分析了Python遞歸算法的定義與使用技巧,需要的朋友可以參考下
    2017-08-08
  • Python面向?qū)ο骳lass類屬性及子類用法分析

    Python面向?qū)ο骳lass類屬性及子類用法分析

    這篇文章主要介紹了Python面向?qū)ο骳lass類屬性及子類用法,結(jié)合實例形式較為詳細(xì)的分析了Python面向?qū)ο缶幊讨衏lass類屬性的訪問、修改、刪除操作及子類的相關(guān)使用技巧,需要的朋友可以參考下
    2018-02-02
  • python使用正則表達(dá)式檢測密碼強(qiáng)度源碼分享

    python使用正則表達(dá)式檢測密碼強(qiáng)度源碼分享

    客戶系統(tǒng)升級,要求用戶密碼符合一定的規(guī)則,即:包含大小寫字母、數(shù)字、符號,長度不小于8,于是先用python寫了個簡單的測試程序:
    2014-06-06
  • python算法題 鏈表反轉(zhuǎn)詳解

    python算法題 鏈表反轉(zhuǎn)詳解

    這篇文章主要介紹了python算法題 鏈表反轉(zhuǎn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2019-07-07

最新評論

铜梁县| 东乌珠穆沁旗| 南皮县| 深圳市| 漳浦县| 平昌县| 仙桃市| 宜宾市| 九龙坡区| 崇明县| 健康| 宜兰县| 宜丰县| 辽宁省| 交城县| 延边| 西盟| 余庆县| 专栏| 普安县| 喜德县| 太仆寺旗| 扎囊县| 清流县| 吉水县| 蒙山县| 长岭县| 双城市| 濮阳市| 林西县| 新营市| 闸北区| 体育| 黑龙江省| 汕尾市| 华池县| 松潘县| 会昌县| 丰镇市| 溧水县| 石嘴山市|