python:Flask 3, mistune 2, 实现在线编辑 Markdown 文档的 Web 服务程序

承上一篇: nodejs: 能在线编辑 Markdown 文档的 Web 服务程序,更多扩展功能

模仿 app2.js, 请用 python编程 : flask 3, mistune 2, 或者其它模块 实现同样功能的Web服务

以下是使用 Flask 3 + Mistune 2 实现与 app2.js 相同功能的 Python Web 服务,包含 Markdown 解析(支持代码高亮、Mermaid、表格、公式)、文档保存/加载/列表查询等核心功能:

1. 先安装依赖

pip install flask==3.0.3 mistune==2.0.5 pip install pygments python-multipart python-dotenv 

2. 完整代码(app.py)

# -*- coding: utf-8 -*-""" Markdown 在线编辑器 (支持表格/公式/Mermaid) """import os import json from pathlib import Path import mistune from mistune.renderers import HTMLRenderer from mistune.plugins import table, task_lists, footnotes from pygments import highlight from pygments.lexers import get_lexer_by_name, guess_lexer from pygments.formatters import HtmlFormatter from flask import Flask, render_template, request, jsonify, send_from_directory # 初始化 Flask 应用 app = Flask(__name__, static_url_path='/', static_folder='public',# 静态文件目录(对应前端资源) template_folder='views')# 模板目录(对应editor.html)# 配置 app.config['JSON_AS_ASCII']=False# 支持中文 DOCS_DIR = Path(__file__).parent /'docs'# 文档保存目录 DOCS_DIR.mkdir(exist_ok=True)# 确保目录存在# ---- Markdown 解析配置 ----# 自定义代码高亮渲染器(支持 Mermaid/代码高亮)classCustomRenderer(HTMLRenderer):defblock_code(self, code, info=None):# 处理 Mermaid 代码块if info and info.strip()=='mermaid':returnf'<div>{mistune.escape(code)}</div>'# 处理普通代码块高亮try:# 尝试获取指定语言的 lexer lexer = get_lexer_by_name(info.strip())if info else guess_lexer(code)except:# 自动检测语言 lexer = guess_lexer(code)# 使用 Pygments 高亮代码 formatter = HtmlFormatter( noclasses=False,# 生成带类名的 HTML(配合 highlight.js 样式) cssclass='hljs',# 兼容 highlight.js 样式类 linenos=False# 不显示行号(可根据需求开启)) highlighted = highlight(code, lexer, formatter)returnf'<pre>{highlighted}</pre>'# 初始化 Mistune 解析器(启用所有扩展) markdown_parser = mistune.create_markdown( renderer=CustomRenderer(), plugins=['table',# 表格支持'task_lists',# 任务列表支持'footnotes'# 脚注支持], escape=False# 关键配置:禁用字符转义)# ---- 路由配置 ----# 首页 - 编辑器界面@app.route('/')defindex():return render_template('editor.html', title='Markdown 在线编辑器 (支持表格/公式/Mermaid)')# 解析 Markdown 为 HTML (API)@app.route('/api/parse', methods=['POST'])defparse_markdown():try: data = request.get_json() markdown = data.get('markdown','')ifnot markdown:return jsonify({'error':'Markdown 内容不能为空'}),400# 解析 Markdown 为 HTML html = markdown_parser(markdown)return jsonify({'html': html})except Exception as e:return jsonify({'error':f'解析 Markdown 失败: {str(e)}'}),500# 保存文档 (API)@app.route('/api/save', methods=['POST'])defsave_document():try: data = request.get_json() filename = data.get('filename','').strip() content = data.get('content','').strip()ifnot filename ornot content:return jsonify({'error':'文件名和内容不能为空'}),400# 拼接文件路径 file_path = DOCS_DIR /f'{filename}.md'# 写入文件(UTF-8 编码)withopen(file_path,'w', encoding='utf-8')as f: f.write(content)return jsonify({'success':True,'message':'文件保存成功','filePath':str(file_path)})except Exception as e:return jsonify({'error':f'保存文件失败: {str(e)}'}),500# 加载文档 (API)@app.route('/api/load/<filename>', methods=['GET'])defload_document(filename):try: file_path = DOCS_DIR /f'{filename}.md'# 检查文件是否存在ifnot file_path.exists():return jsonify({'error':'文件不存在'}),404# 读取文件内容withopen(file_path,'r', encoding='utf-8')as f: content = f.read()return jsonify({'success':True,'content': content})except Exception as e:return jsonify({'error':f'加载文件失败: {str(e)}'}),500# 获取文档列表 (API)@app.route('/api/docs', methods=['GET'])deflist_docs():try:# 读取目录下所有 .md 文件 docs =[]forfilein DOCS_DIR.glob('*.md'): docs.append({'name':file.stem,# 不带扩展名的文件名'path':str(file)})return jsonify({'success':True,'docs': docs})except Exception as e:return jsonify({'error':f'获取文档列表失败: {str(e)}'}),500# 静态文件服务(兼容前端资源加载)@app.route('/public/<path:path>')defserve_static(path):return send_from_directory('public', path)# ---- 启动服务器 ----if __name__ =='__main__':# 启动 Flask 开发服务器(生产环境建议用 Gunicorn) app.run( host='127.0.0.1',# 不允许外部访问 port=8000,# 与原 Node.js 端口保持一致 debug=True# 开发模式(生产环境关闭))

3. 目录结构说明

需要和原 Node.js 版本保持一致的目录结构,确保前端页面能正常加载:

TREE /F md-editor md-editor ├── app.py # Python 服务端代码 ├── docs/ # 文档保存目录(自动创建) │ └── demo.md ├── public/ # 静态资源目录(前端 JS/CSS) │ ├── js/ │ │ ├── mermaid.min.js │ │ └── highlight.min.js │ └── css/ │ └── github-dark.min.css └── views/ # 模板目录 └── editor.html # 原 Node.js 版本的 editor.ejs(无需修改) 

4. 核心功能说明

  1. Markdown 解析
    • 使用 mistune 2 作为核心解析器,兼容 GFM 规范
    • 自定义渲染器支持 Mermaid 代码块(输出 <div>
    • 使用 Pygments 实现代码高亮(兼容 highlight.js 样式)
    • 启用表格、任务列表、脚注等扩展
  2. API 接口
    • /api/parse:POST 请求,解析 Markdown 为 HTML
    • /api/save:POST 请求,保存 Markdown 文档
    • /api/load/<filename>:GET 请求,加载指定文档
    • /api/docs:GET 请求,获取所有文档列表
  3. 前端兼容
    • 静态资源路径、接口地址与原 Node.js 版本完全一致
    • 复用原 editor.html 前端页面,只需修改一行
      cd public\css
      rename editor.ejs editor.html
      修改第6行:<title>{{ title }}</title>

5. 运行方式

直接运行

python app.py

生产环境建议用 Gunicorn

gunicorn -w 4 -b 0.0.0.0:8000 app:app

6. 关键注意事项

  1. 静态资源:需要将原 Node.js 版本中 public 目录下的 js/mermaid.min.jsjs/highlight.min.jscss/github-dark.min.css 复制到 Python 项目的 public 目录中。
  2. 公式渲染:数学公式渲染仍由前端 KaTeX 处理,服务端仅负责 Markdown 解析,与原逻辑一致。
  3. 编码:所有文件读写均使用 UTF-8 编码,确保中文正常显示。
  4. 兼容性:接口返回格式与原 Node.js 版本完全一致,前端无需任何修改即可对接。

该实现完整复刻了原 Node.js 版本的所有核心功能,同时符合 Python/Flask 的开发规范,可直接替换原服务端使用。

Read more

贪心算法篇——万千抉择中的唯一考量,最优解追寻的跬步累积(1)

贪心算法篇——万千抉择中的唯一考量,最优解追寻的跬步累积(1)

文章目录 * 引言:在选择的海洋中 * 贪心算法的哲学:局部最优,全球最优 * 贪心算法的经典应用 * 贪心算法的局限与挑战 * 结语:智者的选择,最优的未来 引言:在选择的海洋中 在人生的旅途上,每个人都要面临无数的选择。每一个选择,都是一次抉择;每一次抉择,都是命运的交汇点。数学与计算机科学的世界里,贪心算法正是对这种“选择”的一种深刻体现。在一系列的选择面前,贪心算法如同一位睿智的旅行者,始终秉持着最优的哲学:每一次决策都应基于局部最优,以期在最后抵达全局最优的境地。 贪心算法(Greedy Algorithm),正如其名所示,是一种每次都选择当前看起来最优解的算法。这种算法策略简单却充满智慧,常常能够解决很多看似复杂的问题。它通过一种局部的、贪婪的方式,一步步走向最终解。然而,正如智慧的旅行者需要对道路有所预见一样,贪心算法也有其适用的范围,只有在满足某些条件时,它才能发挥出最优解的魅力。 在这篇报告中,我们将深入探讨贪心算法的基本理念、适用范围、经典应用,并通过具体的代码示例,揭开这一算法的神秘面纱。 贪心算法的哲学:

By Ne0inhk
动态规划 线性 DP 经典四题一遍吃透

动态规划 线性 DP 经典四题一遍吃透

文章目录 * 台阶问题 * 最大子段和 * 传球游戏 * 乌龟棋 线性dp 是动态规划问题中最基础、最常⻅的⼀类问题。它的特点是状态转移只依赖于前⼀个或前⼏个状态,状态之间的关系是线性的,通常可以⽤⼀维或者⼆维数组来存储状态。 我们在⼊⻔阶段解决的《下楼梯》以及《数字三⻆形》其实都是线性dp,⼀个是⼀维的,另⼀个是⼆ 维的。 台阶问题 题目描述 题目解析 本题就是上一节下楼梯的问题的加强版,总体思路不变,下面我们还是按照动规5板斧来分析一下这道题。 1、状态表示 dp[i]表示走到第i个台阶的所有方案数 2、状态转移方程 第i个台阶的方案数等于从i-1阶到i-k阶的所有方案数之和,因为本题数据比较大,用long long都无法保证数据不越界,所以题目规定方案数还需要模100003,第i个台阶的方案数等于从i-1阶到i-k阶的所有方案数之和再模上100003,所以但是注意是可能越界访问的,比如i为3,

By Ne0inhk
【数据结构与算法】环与相遇:链表带环问题的底层逻辑与工程实现

【数据结构与算法】环与相遇:链表带环问题的底层逻辑与工程实现

🔥小龙报:个人主页 🎬作者简介:C++研发,嵌入式,机器人等方向学习者 ❄️个人专栏:《C语言》《【初阶】数据结构与算法》 ✨ 永远相信美好的事情即将发生 文章目录 * 前言 * 一、带环链表 * 1.1题目 * 1.2 算法原理 * 1.3 代码 * 1.4 数学证明 * 1.4.1 为什么带环slow与fast必定能相遇? * 1.4.2 fast一定只能走2步吗?可以是2步甚至更多吗? * 1.4.2.1 以3步为例 * 1.4.3结论 * 二、环形链表(寻找相遇点) * 2.1 题目

By Ne0inhk
Python 调用 Ollama 本地大模型 API 完全指南

Python 调用 Ollama 本地大模型 API 完全指南

Python 调用 Ollama 本地大模型 API 完全指南 Ollama 是一个开源工具,允许开发者在本地轻松运行 Llama、Mistral、Gemma 等主流大语言模型(LLM)。它不仅提供命令行交互,还内置了 HTTP API 服务,使得我们可以通过 Python 等编程语言远程调用本地模型,实现私有化、低延迟、无网络依赖的 AI 应用开发。 本文将手把手教你如何在 Python 中通过 HTTP 请求调用 Ollama 的 API,完成文本生成、对话交互等任务。 一、前提准备 1. 安装并启动 Ollama * 官网下载安装:https://ollama.com/ * 首次运行会自动下载模型(需联网),之后即可离线使用。 安装后,

By Ne0inhk