在當(dāng)今的數(shù)字時(shí)代,擁有一個(gè)屬于自己的網(wǎng)站已經(jīng)成為展示個(gè)人風(fēng)采和技術(shù)能力的重要方式。而通過(guò)Python搭建個(gè)人網(wǎng)站不僅可以快速實(shí)現(xiàn)這一目標(biāo),還能讓你更深入地了解Web開(kāi)發(fā)的過(guò)程和原理。本文將介紹如何使用Python來(lái)搭建一個(gè)簡(jiǎn)單的個(gè)人網(wǎng)站,并給出一些進(jìn)階建議。
準(zhǔn)備工作
在開(kāi)始搭建個(gè)人網(wǎng)站之前,你需要確保已經(jīng)安裝了以下工具和庫(kù):
- Python
- Flask:一個(gè)輕量級(jí)的Web框架
- Jinja2:用于模板渲染
- Markdown:用于編寫(xiě)內(nèi)容
- Pygments:為代碼高亮提供支持
你可以通過(guò)以下命令安裝這些庫(kù):
pip install flask jinja2 pygments
基本結(jié)構(gòu)
我們的目標(biāo)是創(chuàng)建一個(gè)能夠顯示博客文章的個(gè)人網(wǎng)站,因此需要以下幾個(gè)文件:
app.py
:主程序文件templates/
:存放HTML模板的目錄static/
:存放靜態(tài)資源的目錄posts/
:存放博客文章的目錄
創(chuàng)建項(xiàng)目目錄
創(chuàng)建一個(gè)項(xiàng)目目錄并在其中初始化文件結(jié)構(gòu):
mkdir mywebsite
cd mywebsite
touch app.py
mkdir templates static posts
編寫(xiě)主程序
在app.py
文件中,編寫(xiě)Flask應(yīng)用的基本結(jié)構(gòu):
from flask import Flask, render_template, request, send_from_directory, abort
import os
import markdown
from pygments import highlight, lexers, formatters
# 初始化Flask應(yīng)用
app = Flask(__name__)
# 配置
app.config['TEMPLATES_AUTO_RELOAD'] = True
app.config['DEBUG'] = True
# 配置靜態(tài)文件路徑
app.config['UPLOAD_FOLDER'] = 'static'
# 配置文件上傳類(lèi)型限制
app.config['ALLOWED_EXTENSIONS'] = set(['png', 'jpg', 'jpeg', 'gif'])
# 路由與視圖函數(shù)
@app.route('/')
def index():
posts = load_posts()
return render_template('index.html', posts=posts)
@app.route('/post/<slug>')
def post(slug):
post = load_post_by_slug(slug)
if not post:
abort(404)
post['content'] = markdown.markdown(post['content'])
return render_template('post.html', post=post)
@app.route('/upload', methods=['POST'])
def upload_file():
file = request.files['file']
if file and allowed_file(file.filename):
filename = secure_filename(file.filename)
filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename)
file.save(filepath)
return 'File uploaded successfully'
else:
return 'Invalid file or no file provided'
@app.route('/static/<path:filename>')
def get_static_file(filename):
try:
return send_from_directory(app.config['UPLOAD_FOLDER'], filename)
except FileNotFoundError:
abort(404)
if __name__ == '__main__':
app.run()
加載文章數(shù)據(jù)
我們需要從posts
文件夾中加載文章數(shù)據(jù)。新建一個(gè)util.py
文件來(lái)處理這一邏輯:
import os
from datetime import datetime
import yaml
from markdown import markdown as md_renderer # 引入markdown模塊的markdown方法
from pygments import highlight, lexers, formatters # 引入Pygments模塊的highlight方法、lexers(詞法分析器)和formatters(格式化器)
from jinja2 import TemplateNotFound
def load_posts(folder='posts'):
"""加載所有文章"""
posts = []
for filename in sorted(os.listdir(folder), reverse=True):
if filename.endswith('.md'):
with open(os.path.join(folder, filename), encoding='utf-8') as f:
content = f.read()
rendered = md_renderer(content, extensions=['markdown.extensions.codehilite'])
date = datetime.now()
slug = filename[:-3] # 去掉后綴".md"
title = filename # 默認(rèn)標(biāo)題為文件名,不含后綴
template = None
try:
template = renderer(title) # 嘗試獲取自定義模板,如果不存在則使用默認(rèn)模板
except TemplateNotFound:
template = renderer('post.html') # 使用默認(rèn)模板
posts.append({'slug': slug, 'title': title, 'content': rendered, 'date': date, 'template': template})
return posts
def load_post_by_slug(slug):
"""根據(jù)文章的slug加載文章"""
for post in load_posts():
if post['slug'] == slug:
return post
return None
在上述代碼中,我們定義了兩個(gè)函數(shù):load_posts()
用于加載所有文章,load_post_by_slug(slug)
根據(jù)給定的slug加載特定文章。我們還使用了pygments
來(lái)實(shí)現(xiàn)代碼高亮。
編寫(xiě)模板文件
在templates
文件夾中創(chuàng)建兩個(gè)模板文件:index.html
和post.html
。
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My Blog</title>
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
</head>
<body>
<header>
<h1>My Blog</h1>
</header>
<main>
{% for post in posts %}
<article>
<h2><a href="{{ url_for('post', slug=post.slug) }}">{{ post.title }}</a></h2>
<p>{{ post.content|truncate(200) }}</p>
<a href="{{ url_for('post', slug=post.slug) }}">Read More...</a>
</article>
{% endfor %}
</main>
</body>
</html>
post.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ post.title }} - My Blog</title>
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
</head>
<body>
<header>
<h1>{{ post.title }}</h1>
</header>
<div class="post">{{ post.content | safe }}</div>
</body>
</html>
這兩個(gè)模板分別用于主頁(yè)的文章列表展示和單篇文章的詳細(xì)展示。注意我們?cè)?code>post.html中使用了safe
過(guò)濾器來(lái)確保Markdown解析后的內(nèi)容被正確顯示。
樣式文件與腳本文件
我們可以添加一些簡(jiǎn)單的樣式來(lái)美化我們的網(wǎng)站。在static
目錄下創(chuàng)建一個(gè)style.css
文件:
“`css
body {
font-family: Arial, sans-serif;
line-height: 1.6;
margin: 0;
padding: 0;
background: #f4f4f4;
}
header {
background: #333;
color: white;
padding: 10px 0;
text-align: center;
}
main {
padding: 20px;
}
article {
margin-bottom: 20px;
background: white;
padding: 20px;
border-radius: 5px;
box-shadow: 0 0 10px rgba(0,0,0,0.1);
}
a {
color: #333;
text-decoration: none;} a:hover {color:#56a7e8; text-decoration : underline;} .post {word-wrap: break-word;} header{background:linear-gradient(to right,#4facfe 0%,#06466b 100