Flask Web开发实战:从入门到API集成
1. 最简单的Flask应用
python
"""
Flask基础应用示例
功能:创建一个最简单的Hello World Flask应用
"""
from flask import Flask
# 创建Flask应用实例
app = Flask(__name__)
# 定义路由和处理函数
@app.route("/", methods=["GET"])
def hello():
return "hello CS 41"
# 启动开发服务器
if __name__ == "__main__":
app.run(debug=True, port=5000)
运行方式:
bash
python app.py
2. 动态路由与API响应
python
"""
猜数字游戏API
功能:实现一个猜数字的RESTful API
"""
from flask import Flask
app = Flask(__name__)
# 游戏配置
SECRET_NUMBER = 41
@app.route("/", methods=["GET"])
def hello():
return "hello CS 41"
@app.route("/<guess>", methods=["GET"])
def guess_check(guess):
"""
猜数字API接口
参数: guess - 用户猜测的数字
返回: JSON格式的响应
"""
try:
# 类型转换和安全检查
guess = int(guess)
except ValueError:
return {"error": "Hey! Thats not a number"}
# 构建响应数据
output = {'correct': guess == SECRET_NUMBER}
if guess < SECRET_NUMBER:
output["message"] = "too low!"
elif guess > SECRET_NUMBER:
output["message"] = "too high"
else:
output["message"] = "congrats!!"
return output
if __name__ == "__main__":
app.run(debug=True, port=5000)
API使用示例:
bash
# 访问正确的数字
curl http://localhost:5000/41
# 访问错误的数字
curl http://localhost:5000/30
# 访问非数字
curl http://localhost:5000/abc
3. 处理POST请求与模板渲染
3.1 服务器端代码(app.py)
python
"""
待办事项列表应用
功能:支持GET和POST请求,使用模板渲染HTML
"""
from flask import Flask, request, render_template
app = Flask(__name__)
# 全局存储(实际项目中应使用数据库)
items = []
@app.route("/", methods=["GET", "POST"])
def hello():
"""
处理待办事项列表
GET: 显示所有待办项
POST: 添加新的待办项
"""
if request.method == "POST":
# 获取POST请求中的JSON数据
data = request.get_json()
# 验证数据格式
if "to_add" in data:
items.append(data["to_add"])
return {"status": "success", "message": "Item added"}
else:
return {"error": "Missing 'to_add' field"}, 400
# GET请求:渲染模板
return render_template("list.html", items=items)
if __name__ == "__main__":
app.run(debug=True, port=4000)
3.2 HTML模板(templates/list.html)
html
<!--
待办事项列表页面
功能:显示所有待办项,支持动态更新
-->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>待办事项列表</title>
<style>
body {
font-family: 'Arial', sans-serif;
max-width: 600px;
margin: 50px auto;
padding: 20px;
background-color: #f5f5f5;
}
.container {
background-color: white;
border-radius: 10px;
padding: 30px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
h1 {
color: #333;
border-bottom: 2px solid #4CAF50;
padding-bottom: 10px;
}
ul {
list-style-type: none;
padding: 0;
}
li {
background-color: #f8f9fa;
margin: 10px 0;
padding: 15px;
border-radius: 5px;
border-left: 5px solid #4CAF50;
display: flex;
justify-content: space-between;
align-items: center;
}
.empty-state {
text-align: center;
color: #666;
padding: 40px 20px;
}
.empty-state i {
font-size: 48px;
color: #ccc;
margin-bottom: 20px;
}
.item-count {
background-color: #4CAF50;
color: white;
padding: 5px 10px;
border-radius: 20px;
font-size: 0.9em;
}
.footer {
margin-top: 30px;
text-align: center;
color: #888;
font-size: 0.9em;
}
</style>
</head>
<body>
<div class="container">
<h1>待办事项列表
<span class="item-count">{{ items|length }} 项</span>
</h1>
{% if items %}
<ul>
{% for item in items %}
<li>
<span>{{ loop.index }}. {{ item }}</span>
<span>🕒 {{ now().strftime('%H:%M') }}</span>
</li>
{% endfor %}
</ul>
{% else %}
<div class="empty-state">
<div>📝</div>
<h3>暂无待办事项</h3>
<p>使用客户端脚本添加你的第一个待办项</p>
</div>
{% endif %}
<div class="footer">
<p>使用Python客户端或Postman添加新项目</p>
<p>POST请求格式: {"to_add": "任务内容"}</p>
</div>
</div>
<script>
// 自动刷新页面(每30秒)
setTimeout(function() {
location.reload();
}, 30000);
</script>
</body>
</html>
4. 客户端交互脚本
python
"""
Flask应用客户端
功能:通过requests库与Flask服务器交互
"""
import requests
import json
class FlaskClient:
def __init__(self, base_url="http://127.0.0.1:4000"):
"""
初始化Flask客户端
:param base_url: Flask服务器地址
"""
self.base_url = base_url
def add_item(self, item):
"""
添加待办项到服务器
:param item: 待办项内容
:return: 服务器响应
"""
try:
response = requests.post(
self.base_url,
json={"to_add": item},
headers={"Content-Type": "application/json"}
)
if response.status_code == 200:
print(f"✅ 成功添加: {item}")
return response.json()
else:
print(f"❌ 添加失败: {response.status_code}")
print(f"响应: {response.text}")
return None
except requests.exceptions.ConnectionError:
print("❌ 连接失败,请确保Flask服务器正在运行")
return None
def get_items(self):
"""
获取所有待办项
:return: HTML页面内容
"""
try:
response = requests.get(self.base_url)
if response.status_code == 200:
return response.text
else:
print(f"获取失败: {response.status_code}")
return None
except requests.exceptions.ConnectionError:
print("❌ 连接失败")
return None
def interactive_mode(self):
"""交互式模式:持续添加待办项"""
print("=" * 50)
print("Flask待办事项客户端")
print("=" * 50)
print("输入待办事项内容(输入'quit'退出,输入'list'查看)")
print("-" * 50)
while True:
user_input = input("📝 输入待办项: ").strip()
if user_input.lower() == 'quit':
print("👋 退出客户端")
break
elif user_input.lower() == 'list':
html = self.get_items()
if html:
print("📄 服务器返回的HTML(前500字符):")
print(html[:500])
elif user_input:
self.add_item(user_input)
else:
print("⚠️ 输入不能为空")
def main():
"""主函数:运行客户端"""
# 创建客户端实例
client = FlaskClient()
# 测试单个添加
print("测试单个添加...")
client.add_item("学习Flask框架")
# 进入交互模式
client.interactive_mode()
if __name__ == "__main__":
main()
5. 集成外部API:维基百科词云生成器
5.1 服务器端代码
python
"""
维基百科词云生成器
功能:调用维基百科API获取内容,生成词云图片
"""
from flask import Flask, render_template
import requests
from wordcloud import WordCloud
import io
import base64
app = Flask(__name__)
# 配置词云生成器
wc = WordCloud(
background_color="white",
max_words=100,
colormap='plasma',
width=1000,
height=800,
font_path="simhei.ttf" # 中文字体
)
def get_wikipedia_content(topic):
"""
获取维基百科页面内容
:param topic: 主题关键词
:return: 页面内容或错误信息
"""
try:
# 构建API请求URL
api_url = "https://en.wikipedia.org/w/api.php"
params = {
"action": "query",
"format": "json",
"titles": topic,
"prop": "extracts",
"explaintext": True,
"exintro": True # 只获取引言部分
}
# 发送API请求
response = requests.get(api_url, params=params, timeout=10)
response.raise_for_status()
# 解析响应数据
data = response.json()
pages = data.get("query", {}).get("pages", {})
if not pages:
return None, "未找到相关页面"
# 获取第一个页面
page_id, page_data = list(pages.items())[0]
if page_id == -1:
return None, "页面不存在"
extract = page_data.get("extract", "")
if not extract:
return None, "页面内容为空"
return extract, None
except requests.exceptions.RequestException as e:
return None, f"API请求失败: {str(e)}"
except Exception as e:
return None, f"处理失败: {str(e)}"
def generate_wordcloud_svg(text):
"""
生成词云SVG图像
:param text: 文本内容
:return: SVG字符串
"""
try:
# 生成词云
wordcloud = wc.generate(text)
# 转换为SVG格式
svg_bytes = wordcloud.to_svg()
# 编码为base64(可选,更安全)
# svg_base64 = base64.b64encode(svg_bytes).decode('utf-8')
# return f'<img src="data:image/svg+xml;base64,{svg_base64}" alt="WordCloud">'
return svg_bytes.decode('utf-8')
except Exception as e:
return f'<div style="color: red; padding: 20px;">生成词云失败: {str(e)}</div>'
@app.route("/")
def index():
"""首页:显示使用说明"""
return """
<!DOCTYPE html>
<html>
<head>
<title>维基百科词云生成器</title>
<style>
body { font-family: Arial; max-width: 800px; margin: 50px auto; padding: 20px; }
.container { background: #f8f9fa; padding: 30px; border-radius: 10px; }
input { padding: 10px; width: 70%; margin-right: 10px; }
button { padding: 10px 20px; background: #007bff; color: white; border: none; cursor: pointer; }
.example { margin-top: 20px; color: #666; }
</style>
</head>
<body>
<div class="container">
<h1>🌍 维基百科词云生成器</h1>
<p>输入任何维基百科词条名称,生成对应的词云</p>
<form action="/generate" method="GET">
<input type="text" name="topic" placeholder="例如:Python, Machine Learning, Artificial Intelligence..." required>
<button type="submit">生成词云</button>
</form>
<div class="example">
<p>示例:</p>
<a href="/Python">Python</a> |
<a href="/Artificial_intelligence">人工智能</a> |
<a href="/Machine_learning">机器学习</a>
</div>
</div>
</body>
</html>
"""
@app.route("/generate", methods=["GET"])
def generate_from_form():
"""从表单接收参数的词云生成"""
topic = request.args.get("topic", "").strip()
if not topic:
return redirect("/")
return redirect(f"/{topic}")
@app.route("/<wiki_slug>", methods=["GET"])
def generate_wordcloud(wiki_slug):
"""
生成词云的主函数
:param wiki_slug: 维基百科词条名称
"""
# 获取维基百科内容
content, error = get_wikipedia_content(wiki_slug)
if error:
return f"""
<div style="padding: 50px; text-align: center;">
<h2>😕 生成失败</h2>
<p>{error}</p>
<p><a href="/">返回首页</a></p>
</div>
"""
# 生成词云SVG
svg_content = generate_wordcloud_svg(content)
# 渲染模板
return render_template("wordcloud.html",
title=wiki_slug,
svg_content=svg_content,
content_preview=content[:500] + "...")
if __name__ == "__main__":
app.run(debug=True, port=5000)
5.2 词云展示模板
html
<!-- templates/wordcloud.html -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>词云生成结果 - {{ title }}</title>
<style>
body {
font-family: 'Arial', sans-serif;
margin: 0;
padding: 20px;
background-color: #f0f2f5;
}
.container {
max-width: 1200px;
margin: 0 auto;
background-color: white;
border-radius: 10px;
padding: 30px;
box-shadow: 0 2px 20px rgba(0,0,0,0.1);
}
.header {
text-align: center;
margin-bottom: 30px;
padding-bottom: 20px;
border-bottom: 2px solid #eaeaea;
}
.header h1 {
color: #333;
margin-bottom: 10px;
}
.header .topic {
font-size: 1.2em;
color: #007bff;
background-color: #e7f3ff;
padding: 5px 15px;
border-radius: 20px;
display: inline-block;
}
.wordcloud-container {
text-align: center;
margin: 30px 0;
padding: 20px;
background-color: #f8f9fa;
border-radius: 8px;
}
.content-preview {
margin-top: 30px;
padding: 20px;
background-color: #f8f9fa;
border-radius: 8px;
line-height: 1.6;
}
.content-preview h3 {
color: #555;
margin-bottom: 15px;
}
.actions {
text-align: center;
margin-top: 30px;
padding-top: 20px;
border-top: 1px solid #eee;
}
.btn {
display: inline-block;
padding: 10px 25px;
margin: 0 10px;
background-color: #007bff;
color: white;
text-decoration: none;
border-radius: 5px;
transition: background-color 0.3s;
}
.btn:hover {
background-color: #0056b3;
}
.btn-secondary {
background-color: #6c757d;
}
.btn-secondary:hover {
background-color: #545b62;
}
.stats {
display: flex;
justify-content: space-around;
margin: 20px 0;
padding: 15px;
background-color: #f8f9fa;
border-radius: 8px;
}
.stat-item {
text-align: center;
}
.stat-value {
font-size: 1.5em;
font-weight: bold;
color: #007bff;
}
.stat-label {
font-size: 0.9em;
color: #666;
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>📊 词云生成结果</h1>
<div class="topic">{{ title }}</div>
</div>
<div class="stats">
<div class="stat-item">
<div class="stat-value">{{ content_preview|length }}</div>
<div class="stat-label">文本长度</div>
</div>
<div class="stat-item">
<div class="stat-value">{{ content_preview.split()|length }}</div>
<div class="stat-label">单词数量</div>
</div>
<div class="stat-item">
<div class="stat-value">100</div>
<div class="stat-label">显示词数</div>
</div>
</div>
<div class="wordcloud-container">
<h2>词云可视化</h2>
<div style="display: inline-block; margin: 20px;">
{{ svg_content|safe }}
</div>
<p style="color: #666; margin-top: 15px;">
词云中词语的大小表示其在文本中出现的频率
</p>
</div>
<div class="content-preview">
<h3>📝 内容预览</h3>
<p>{{ content_preview }}</p>
<p style="color: #888; font-style: italic;">
(以上内容来自维基百科,已截断显示前500字符)
</p>
</div>
<div class="actions">
<a href="/" class="btn">🏠 返回首页</a>
<a href="https://en.wikipedia.org/wiki/{{ title }}"
target="_blank"
class="btn btn-secondary">
🌐 查看完整维基百科页面
</a>
<button onclick="location.reload()" class="btn">🔄 重新生成</button>
</div>
</div>
</body>
</html>
6. 完整项目结构
flask-demo-project/
├── app.py # 主Flask应用
├── client.py # 客户端脚本
├── requirements.txt # 依赖包列表
├── README.md # 项目说明
├── templates/ # HTML模板目录
│ ├── list.html # 待办事项列表页
│ ├── wordcloud.html # 词云展示页
│ └── base.html # 基础模板
├── static/ # 静态文件
│ ├── css/
│ ├── js/
│ └── images/
└── docs/ # 文档
└── api.md # API文档
requirements.txt
txt
Flask==2.3.3
requests==2.31.0
wordcloud==1.9.2
matplotlib==3.7.2
numpy==1.24.3
README.md
markdown
# Flask Web开发实战项目
## 项目简介
本示例项目展示了Flask Web开发的完整流程,涵盖以下功能:
1. 基础Flask应用
2. RESTful API设计
3. 模板渲染
4. 外部API集成
5. 客户端交互
## 快速开始
### 安装依赖
```bash
pip install -r requirements.txt
运行服务器
bash
python app.py
运行客户端
bash
python client.py
API文档
猜数字游戏
GET /<guess>- 猜测数字
待办事项
GET /- 查看所有待办项POST /- 添加待办项
词云生成器
-
GET /<topic>- 生成维基百科词云
7. 部署到生产环境
python""" 生产环境配置示例 """ from flask import Flask import os from dotenv import load_dotenv # 加载环境变量 load_dotenv() app = Flask(__name__) # 生产环境配置 class Config: SECRET_KEY = os.environ.get('SECRET_KEY', 'dev-key-here') DEBUG = False HOST = '0.0.0.0' PORT = int(os.environ.get('PORT', 5000)) app.config.from_object(Config) @app.route("/health") def health_check(): """健康检查端点(用于生产环境监控)""" return {"status": "healthy", "service": "Flask Web App"} if __name__ == "__main__": # 生产环境使用Waitress或Gunicorn # pip install waitress from waitress import serve serve(app, host=app.config['HOST'], port=app.config['PORT'])