Pelican 静态博客网站详细部署指南

Pelican 静态博客网站详细部署指南

本文档提供从零开始在云服务器上部署 Pelican 静态博客的完整步骤,涵盖本地blog编写和预览环境、Gitee 仓库配置、服务器部署和自动化发布。

1. 环境概述

1.1 目标环境

项目 配置
服务器 CentOS 9 Stream
Web 服务器 Nginx 1.20.1
Python 版本 Python 3.11
虚拟环境管理 uv
Git 版本 2.43
域名 chenlip.top

1.2 服务器目录结构

/home/app/my_blog/                    # 博客根目录(blog + webhook 统一管理)
├── .venv/                    # uv 虚拟环境(Python 3.11)
│   ├── ...
├── content/                  # 内容目录(从 Gitee 拉取)
│   ├── articles/             #   - 文章
│   ├── pages/                #   - 页面
│   ├── images/               #   - 图片
│   ├── downloads/            #   - 下载附件(.zip/.rar/.7z)
│   └── media/                #   - 音视频(<10MB)
├── output/                   # Pelican 生成的文件
├── pelicanconf.py            # 开发配置
├── publishconf.py            # 生产配置
├── webhook_server.py         # Webhook 服务(fastapi)
├── rebuild.sh                # 构建脚本
├── requirements.txt          # Python 依赖
└── .env                      # 环境变量

1.3 本地blog编写和预览环境(Windows 10或linux)

以下的本地日志编写和管理环境, 如果是linux环境, 请自行使用对应的命令即可

对于window环境, 个人习惯是使用 CMD 终端,所以下面的命令基本都是基于。

你的本地环境用于编写和预览博客内容,服务器负责构建和发布。

项目 你的环境(请先准备好以下工具) 说明
操作系统 Windows 10 本地开发机
Python Python 3.11 已安装 ✅
包管理器 uv 0.11 已安装 ✅
Git 已安装 代码版本控制 ✅
本地项目目录 D:\pythonapp\my_blog 博客源码存放位置

2. 本地日志编写和管理环境搭建

⚠️ 以下的本地日志编写和管理环境, 如果是linux环境, 请自行使用对应的命令即可

⚠️ 对于window环境, 个人习惯是使用 CMD 终端,所以下面的命令基本都是基于。

2.1 创建本地项目目录

:: 在 D:\pythonapp(可以是你想要的任何目录) 目录下创建博客项目
cd D:\pythonapp

git clone https://gitee.com/hoeking/my_blog.git

2.2 使用 uv 创建虚拟环境

:: 进入项目目录
cd D:\pythonapp\my_blog

:: 使用 uv 创建 Python 3.11 虚拟环境
uv venv .venv --python 3.11.14

:: 激活虚拟环境 (CMD)
.venv\Scripts\activate

:: 验证
where python 
python --version

2.3 安装 Pelican 及依赖

:: 激活虚拟环境后执行
uv pip install "pelican[markdown]"

:: 安装 Invoke(Windows 上替代 Makefile 的官方推荐工具)
uv pip install invoke

:: 安装 livereload(开发时浏览器自动刷新,强烈推荐)
uv pip install livereload

:: 安装完成后验证
pelican --version
invoke --version

为什么需要 invoke 和 livereload?

  • Windows 没有 make 命令,无法使用 Makefile,Invoke 是官方推荐的 Windows 替代方案
  • invoke livereload 可以实现修改文件后浏览器自动刷新,是最高效的本地开发方式
  • 如果暂时不想安装,也可以用纯 pelican 命令开发(见 2.7 节方法一、二)

2.4 初始化 Pelican 项目(如果是直接git clone, 则无需初始化)

:: 在 my_blog 目录下执行
pelican-quickstart

# 回答交互式问题:
# > Where do you want to create your new website? [.]
# > What will be the title of this web site? Chen's Blog
# > Who will be the author of this web site? Chen Li
# > What will be the default language of this website? [zh]
# > What is the URL of your website? https://chenlip.top
# > Do you want to specify a URL prefix? [Y/n] n
# > Do you want to enable article pagination? [Y/n] n
# > Do you want to generate a tasks.txt file? [y/N] n
# > Do you want to upload website to GitHub? [y/N] n

2.5 本地目录结构

初始化后,D:\pythonapp\my_blog 目录结构如下:

D:\pythonapp\my_blog\
├── .venv/                    # uv 虚拟环境
│   └── Scripts/              #   - Windows 激活脚本
├── content/                  # 博客文章目录 ⭐ 你在这里写文章
│   ├── articles/             #   - 文章 (md 文件)
│   ├── pages/                #   - 页面
│   ├── images/               #   - 图片
│   ├── downloads/            #   - 下载附件
│   └── media/                #   - 音视频 (<10MB)
├── output/                   # 生成的静态文件
├── pelicanconf.py            # 开发配置
├── publishconf.py            # 生产配置
├── tasks.py                  # Invoke 构建任务(Windows 开发环境推荐使用)
├── Makefile                  # 构建脚本(仅 Linux/macOS 可用,Windows 无法使用 ❌)
└── develop_server.sh        # 开发服务器脚本(仅 Linux/macOS 可用,Windows 无法使用 ❌)

⚠️ Windows 重要说明

  • Makefiledevelop_server.sh 是Linux下的 pelican-quickstart 自动生成的,但它们仅适用于 Linux/macOS(POSIX 系统)
  • Windows 环境下应使用 Invoketasks.py)或直接使用 pelican 命令来替代 make
  • 这两个文件可以保留在项目中(供服务器端使用),但本地开发时不要尝试用 make 命令

2.6 创建示例文章(如果是克隆的项目, 里面已经有示例了)

# 创建

2.7 使用 dev_win.bat启动(推荐)

⚠️ 重要:Windows 本地开发必须使用 .bat 启动,否则会遇到编码问题!

2.7.1 创建 dev_win.bat

D:\pythonapp\my_blog 目录下创建 dev_win.bat 文件:

@echo off
chcp 65001 >nul
set PYTHONUTF8=1

cd /d %~dp0
call .venv\Scripts\activate.bat

echo Starting Pelican livereload server...
start http://localhost:11326
invoke livereload

pause

2.7.2 使用方法

:: 进入博客目录
cd D:\pythonapp\my_blog

:: 双击 dev_win.bat或在 CMD 中运行
dev_win.bat

2.7.3 常见问题

问题 原因 解决
运行后报错 UnicodeDecodeError 未使用 dev_win.bat启动 双击 dev_win.bat运行
提示 "不是内部或外部命令" 虚拟环境未激活 确保 .venv\Scripts\activate.bat 可执行

Invoke 常用开发命令速查

命令 作用
invoke build 生成静态文件
invoke regenerate 自动检测变化并重新生成
invoke serve 启动本地预览服务器
invoke livereload 自动重生成 + 浏览器自动刷新 ⭐推荐

2.8 本地开发常用命令

:: ============ 激活虚拟环境 ============
.venv\Scripts\activate

:: ============ 生成静态文件 ============
pelican content/ -s pelicanconf.py

:: ============ 启动预览服务器 ============
pelican --listen --port 8000

:: ============ 自动监听修改并重新生成(单独运行) ============
pelican --autoreload content/ -s pelicanconf.py

:: ============ Windows 上的开发工作流 ============
:: 终端1:启动自动重载(监听文件变化)
pelican -r content/ -s pelicanconf.py
:: 终端2:启动预览服务器(另开一个 CMD 窗口)
pelican --listen --port 8000

:: ============ 使用 Invoke(推荐) ============
invoke build        :: 生成静态文件
invoke regenerate   :: 自动检测变化并重新生成
invoke serve        :: 启动预览服务器
invoke livereload   :: 自动重生成 + 浏览器自动刷新 ⭐

:: ============ 清理生成的文件 ============
pelican --clean content/ -s pelicanconf.py

⚠️ Windows 重要提醒pelican -r -l(即 --autoreload --listen 组合)在 Windows 上不能同时使用,必须分两个终端窗口分别运行。这是 Pelican 官方文档明确指出的限制。


3. Gitee 仓库配置

3.1 为什么需要 Gitee 仓库?

┌─────────────────┐      git push       ┌─────────────────┐
│  本地 D:\pythonapp │ ────────────────→ │  Gitee 仓库      │
│  \my_blog        │                    │  hoeking/my_note │
└─────────────────┘                    └────────┬────────┘
                                                │
                                        webhook 触发
                                                │
                                                ▼
                                       ┌─────────────────┐
                                       │  云服务器        │
                                       │  /home/app/my_blog  │
                                       │  (自动构建发布)  │
                                       └────────┬────────┘
                                                │
                                                ▼
                                       ┌─────────────────┐
                                       │  https://        │
                                       │  chenlip.top     │
                                       └─────────────────┘

流程说明

  1. 你在本地 D:\pythonapp\my_blog 编写博客
  2. 使用 git push 推送到 Gitee
  3. Gitee 发送 Webhook 通知到服务器
  4. 服务器自动拉取代码、构建、发布

3.2 创建 Gitee 仓库

  1. 登录 Gitee
  2. 点击右上角 "+""新建仓库"
  3. 填写信息:
配置项
仓库名称 my_notemy_blog
路径 hoeking/my_note
仓库公开性 公开(Webhook 需要)
勾选 ✅ 使用 README 初始化仓库(可选)
  1. 点击 "创建"

3.3 初始化本地 Git 并推送到 Gitee

# 进入本地项目目录
cd D:\pythonapp\my_blog

# 初始化 Git(如果还没有初始化)
git init

# 添加远程仓库
git remote add origin https://gitee.com/hoeking/my_note.git

# 添加所有文件
git add .

# 提交
git commit -m "初始化 Pelican 博客项目"

# 推送到 Gitee(首次推送)
git push -u origin master

3.4 验证推送成功

在 Gitee 仓库页面刷新,确认文件已上传。


4. 服务器环境准备

2.1 配置防火墙

# 开放 HTTP(80)和 HTTPS(443)端口
firewall-cmd --permanent --add-service=http
firewall-cmd --permanent --add-service=https

# 重载防火墙
firewall-cmd --reload

# 查看已开放的端口
firewall-cmd --list-all

2.2 配置 SELinux(我的服务器上这个是禁用状态的, 所以这步可以跳过)

# 检查 SELinux 状态
getenforce

# 如果是 Enforcing 模式,需要配置
setsebool -P httpd_can_network_connect on
setsebool -P httpd_read_user_content on

5. 创建博客目录并初始化虚拟环境

3.1 创建目录

# 创建博客目录
mkdir -p /home/app/my_blog

3.2 使用 uv 创建 Python 3.11 虚拟环境

cd /home/app/my_blog

# 使用 uv 创建 Python 3.11 虚拟环境
uv venv .venv --python 3.11.14

# 激活虚拟环境
source .venv/bin/activate

# 验证
python --version  # 应显示 Python 3.11.x

3.3 在虚拟环境中安装依赖

# 激活虚拟环境(后续操作都需要先激活)
source /home/app/my_blog/.venv/bin/activate

# 安装 Pelican 和 Markdown 支持
pip install "pelican[markdown]"

# 安装 Webhook 服务依赖
pip install fastapi uvicorn python-dotenv httpx

# 验证安装
pelican --version

6. 初始化博客配置

4.1 在本地初始化项目

在本地 Windows 机器上操作

:: 创建博客项目
cd D:\pythonapp
mkdir my_blog
cd my_blog

:: 初始化 Pelican(交互式)
pelican-quickstart

:: 回答问题:
:: > Where do you want to create your new website? [.]
:: > What will be the title of this web site? Chen's Blog
:: > Who will be the author of this web site? Chen Li
:: > What will be the default language of this website? [zh]
:: > What is the URL of your website? https://chenlip.top
:: > Do you want to specify a URL prefix? e.g., https://example.com [Y/n] n
:: > Do you want to enable article pagination? [Y/n] n
:: > Do you want to generate a tasks.txt Accumulo file? [y/N] n
:: > Do you want to upload website to GitHub? [y/N] n

4.2 配置文件说明

pelicanconf.py(开发配置)

#!/usr/bin/env python
# -*- coding: utf-8 -*- #
from __future__ import annotations

# ===========================================
# 基础配置
# ===========================================
SITENAME = "Chen's Blog"
SITEURL = "http://localhost:8000"
AUTHOR = "Chen Li"
SITESUBTITLE = "技术分享与生活记录"
DESCRIPTION = "分享技术心得与生活感悟"

# 路径配置
PATH = "content"
OUTPUT_PATH = "output"
THEME = "themes/notmyidea"

# 文章配置
ARTICLE_PATHS = ["articles"]
PAGE_PATHS = ["pages"]
STATIC_PATHS = ["images", "downloads", "media", "extra"]

# URL 配置
ARTICLE_URL = "articles/{slug}.html"
ARTICLE_SAVE_AS = "articles/{slug}.html"
PAGE_URL = "pages/{slug}.html"
PAGE_SAVE_AS = "pages/{slug}.html"

# ===========================================
# 分类和标签
# ===========================================
USE_FOLDER_AS_CATEGORY = True
DEFAULT_CATEGORY = "杂项"
DISPLAY_CATEGORIES_ON_MENU = True
DISPLAY_TAGS_ON_MENU = True

# 标签云
TAG_CLOUD_MAX_ITEMS = 50
TAG_CLOUD_STEPS = 4

# ===========================================
# 时间配置
# ===========================================
WITH_FUTURE_DATES = True
DEFAULT_DATE_FORMAT = "%Y-%m-%d"

# ===========================================
# 开发选项(本地预览配置)
# ===========================================
# 本地预览时显示所有文章(包括草稿、隐藏等)
DEBUG = True

# 显示草稿文章(draft 状态)
INCLUDE_DRAFTS = True

# 显示隐藏文章(hidden 状态)
HIDDEN_INCLUDE = True

# 保留历史文件,不删除输出目录
DELETE_OUTPUT_DIRECTORY = False

# 使用缓存加速构建
LOAD_CONTENT_CACHE = True

# 静态文件
STATIC_CREATE_LINKS = True
STATIC_EXCLUDE_SOURCES = False

# ===========================================
# 忽略文件配置
# ===========================================
# Pelican 构建时会忽略以下匹配模式的文件
# 这对 webhook 自动构建也有效:即使 git pull 了这些文件,构建时也会被跳过
IGNORE_FILES = [
    '.*',           # 所有隐藏文件 (.gitignore, .DS_Store 等)
    '_*',           # 以下划线开头的文件和目录
    '*.bak',        # 备份文件
    '*.tmp',        # 临时文件
    '*.swp',        # Vim 交换文件
    '*~',           # Emacs 备份文件
    'draft-*',      # 以 draft- 开头的文件
    'private-*',    # 以 private- 开头的文件
]

# 排除特定目录下的内容(仅处理 articles 和 pages 目录)
ARTICLE_EXCLUDES = ['temp', 'archive', '_drafts', '_private', 'drafts', 'private']
PAGE_EXCLUDES = ['temp', 'archive', '_drafts', '_private']

# 不纳入版本控制的临时目录
EXCLUDES = ['.git', '.venv', '__pycache__', 'node_modules', 'temp']

# ===========================================
# Markdown 支持
# ===========================================
MARKDOWN = {
    "extensions": [
        "meta",
        "extra",
        "codehilite",
        "toc",
        "tables",
        "fenced_code",
    ],
    "extension_configs": {
        "markdown.extensions.toc": {"title": "目录"},
    },
}

# 分页(禁用)
DEFAULT_PAGINATION = False

# ===========================================
# 站点地图 (Sitemap)
# ===========================================
SITEMAP = {
    "format": "xml",
    "priorities": {
        "articles": 0.7,
        "pages": 0.5,
        "indexes": 0.5,
    },
    "changefreqs": {
        "articles": "monthly",
        "pages": "weekly",
        "indexes": "daily",
    },
}

# ===========================================
# 评论系统 (Valine - 基于 LeanCloud)
# ===========================================
# 使用 Valine 无需后端,直接集成 LeanCloud
# 配置方法见文档后续章节
# Valine 国内访问速度快,适合国内用户

# ===========================================
# 搜索功能 (Tipue Search)
# ===========================================
# 使用 Tipue Search 实现静态站点搜索,无需后端
SEARCH_MODE = "static"
SEARCH_EXTERNAL = ""
DIRECT_TEMPLATES = ["index", "tags", "categories", "archives", "sitemap", "search"]
TEMPLATE_PAGES = {"search": "search.html"}

# ===========================================
# 社交链接
# ===========================================
SOCIAL = (
    ("GitHub", "https://github.com/yourusername"),
    ("Email", "mailto:your@email.com"),
)

# ===========================================
# 版权信息
# ===========================================
COPYRIGHT_YEAR = 2024

3.1.3 附件与音视频目录配置

Pelican 的 STATIC_PATHS 配置用于指定哪些目录作为静态资源目录,这些目录下的文件会被复制到输出目录:

# 静态文件路径配置
STATIC_PATHS = [
    "images",    # 图片目录
    "downloads", # 下载附件(.zip/.rar/.7z)
    "media",     # 音视频目录(<10MB)
    "extra",     # 额外静态文件
]

目录结构说明

content/
├── images/                # 图片文件
├── downloads/             # 下载附件
│   ├── source-code/      #   - 源码包
│   ├── templates/        #   - 模板文件
│   └── documents/        #   - PDF文档
├── media/                 # 音视频文件
│   ├── videos/           #   - 视频 (<10MB)
│   └── audios/           #   - 音频 (<10MB)
└── extra/                 # 额外静态文件

⚠️ 重要:本地开发配置和服务器配置必须保持 STATIC_PATHS 一致!

publishconf.py(生产配置)

publishconf.py(生产配置)

#!/usr/bin/env python
# -*- coding: utf-8 -*- #
from __future__ import annotations

# 从 pelicanconf.py 导入基础配置
from pelicanconf import *

# ===========================================
# 生产环境配置
# ===========================================
SITEURL = "https://chenlip.top"
RELATIVE_URLS = False

# ⚠️ 重要:生产环境必须关闭草稿预览,确保草稿不会被发布
INCLUDE_DRAFTS = False
HIDDEN_INCLUDE = False

# 忽略文件配置(继承自 pelicanconf.py,保持不变)
# 注意:即使 git pull 拉取了这些文件,构建时也会被过滤,不会发布

# 生产环境需要删除输出目录以确保干净
DELETE_OUTPUT_DIRECTORY = False  # 保持增量更新

# 启用缓存以加快构建速度
LOAD_CONTENT_CACHE = True

# ===========================================
# RSS/Atom 订阅
# ===========================================
FEED_ALL_ATOM = "feeds/all.atom.xml"
CATEGORY_FEED_ATOM = "feeds/{slug}.atom.xml"
TAG_FEED_ATOM = "feeds/{slug}.atom.xml"
FEED_RSS = "feeds/all.rss.xml"

# ===========================================
# Google Analytics(可选)
# ===========================================
# GOOGLE_ANALYTICS = "G-XXXXXXXXXX"

# ===========================================
# 结束时启用
# ===========================================
import sys
if len(sys.argv) > 1 and sys.argv[1] == "publish":
    pass

4.3 创建内容目录结构

:: 创建内容目录
mkdir content\articles\tech
mkdir content\articles\life
mkdir content\pages
mkdir content\images
mkdir content\downloads\source-code
mkdir content\downloads\templates
mkdir content\media\videos
mkdir content\media\audios
mkdir content\drafts

创建示例文章 content\articles\hello-world.md(用编辑器创建,内容如下):

Title: 欢迎来到我的博客
Date: 2024-01-01 10:00
Category: 杂项
Tags: 欢迎, 博客
Slug: welcome

这是我的第一篇博客文章!

## 关于我

我是一名软件开发者,喜欢分享技术文章。

提示:Windows CMD 不支持 cat > file << 'EOF'mkdir -p,请直接用编辑器创建文件,目录需逐级创建。


7. 高级功能配置

5.1 安装插件(站点地图、搜索等)

# 激活虚拟环境
source /home/app/my_blog/.venv/bin/activate

# 安装 pelican 插件(如果主题需要)
pip install pelican-plugin-render-math pelican-tiptap

5.2 配置站点地图 (Sitemap)

Pelican 内置 sitemap 插件,无需额外安装。

启用方式:在 pelicanconf.py 中已配置 SITEMAP 字典。

生成的文件

  • sitemap.xml - 站点地图

验证:构建后在 output/ 目录检查 sitemap.xml 文件。

5.3 配置标签 (Tags)

Pelican 原生支持标签功能。

使用方法:在文章 metadata 中添加 Tags:

Title: 我的第一篇文章
Date: 2024-01-01 10:00
Category: 技术
Tags: Python, Pelican, 博客
Slug: my-first-post

文章内容...

标签云页面:访问 /tags/ 查看所有标签。

配置标签云样式(在 THEME/templates 目录的 CSS 中):

.tag-cloud {
    font-family: Arial, sans-serif;
}
.tag-1 { font-size: 0.8em; }
.tag-2 { font-size: 1.0em; }
.tag-3 { font-size: 1.2em; }
.tag-4 { font-size: 1.5em; }

5.4 配置评论系统 (Valine)

Valine 是一款基于 LeanCloud 的轻量级评论系统,无需后端,直接通过 LeanCloud API 存储评论数据。国内访问速度快,配置简单。

5.4.1 注册 LeanCloud 并创建应用

  1. 访问 LeanCloud 官网,注册账号并登录
  2. 进入控制台,点击 "创建应用"
  3. 输入应用名称(如 my-blog-comments),选择 "开发版"
  4. 创建完成后,进入 "应用设置 → 应用Key"
  5. 记录以下两个关键信息:
  6. AppID
  7. AppKey

5.4.2 配置安全域名

⚠️ 重要:必须配置安全域名,否则评论功能无法正常使用。

在 LeanCloud 控制台中:

  1. 进入 "应用设置 → 安全中心"
  2. "安全域名" 中添加你的博客域名,例如:
  3. https://chenlip.top
  4. https://www.chenlip.top
  5. 保存设置

5.4.3 安装 Valine

方式一:CDN 引入(推荐)

在博客主题的 HTML 模板 <head> 中添加:

<!-- Valine 依赖的 LeanCloud SDK -->
<script src="//cdn1.lncld.net/static/js/3.0.4/av-min.js"></script>
<!-- Valine 核心脚本 -->
<script src='//unpkg.com/valine/dist/Valine.min.js'></script>

方式二:NPM 安装(适合前端开发者)

npm install valine --save
import Valine from 'valine';

5.4.4 在模板中添加评论框

编辑博客主题的 article.html 模板文件,在文章底部添加评论区域:

<!-- Valine 评论系统 -->
{% if article and article.status != 'draft' %}
<div id="vcomments" class="comments-area"></div>
<script>
    new Valine({
        el: '#vcomments',
        appId: '你的LeanCloud AppID',
        appKey: '你的LeanCloud AppKey',
        placeholder: '欢迎留言交流!',
        avatar: 'mm',
        meta: ['nick', 'mail', 'link'],
        requiredFields: ['nick', 'mail'],
        pageSize: 10,
        lang: 'zh-CN',
        visitor: true,
        highlight: true,
        recordIP: false,
        enableQQ: false
    });
</script>
{% endif %}

5.4.5 基础配置参数说明

参数 说明 默认值
el 评论框容器元素选择器 #vcomments
appId LeanCloud AppID 必填
appKey LeanCloud AppKey 必填
placeholder 评论框占位文字 '说点什么吧...'
avatar 头像样式:mm/identicon/monsterid/wavatar/retro 'mm'
meta 评论者信息字段 ['nick', 'mail', 'link']
requiredFields 必填字段 ['nick']
pageSize 每页评论数 10
lang 语言设置 'zh-CN'
visitor 开启文章访问量统计 false
highlight 代码高亮 true

5.4.6 进阶配置

邮件通知功能

Valine 支持评论通知,但需要额外配置(可选)。

评论样式自定义

在主题的 CSS 文件中添加自定义样式:

/* Valine 评论框样式 */
#vcomments {
    padding: 20px 0;
}

.v[data-class=v] .vcard {
    padding: 10px 0;
}

.v[data-class=v] .vcontent {
    font-size: 15px;
    line-height: 1.8;
}

/* 评论输入框 */
.veditor {
    min-height: 120px;
    resize: vertical;
}

/* 提交按钮 */
.vsubmit {
    background: #3498db;
    color: #fff;
    border: none;
    padding: 8px 20px;
    border-radius: 4px;
    cursor: pointer;
}

5.4.7 评论数据管理

在 LeanCloud 控制台中:

  1. 选择你的应用
  2. 进入 "存储 → Comment"
  3. 可以查看、编辑、删除所有评论数据

5.4.8 常见问题排查

问题 解决方案
评论框不显示 检查 AppID/AppKey 是否正确,确认安全域名已配置
评论提交失败 检查浏览器控制台错误信息,确认网络连接正常
加载缓慢 尝试使用 NPM 方式本地加载,或更换 CDN 源
头像不显示 Valine 使用 Gravatar 头像,确保评论者邮箱已注册 Gravatar

5.4.9 Valine vs Giscus 对比

特性 Valine Giscus
数据存储 LeanCloud GitHub Discussions
国内访问 ✅ 快速 ❌ 可能不稳定
无需数据库
配置复杂度
依赖外部服务 LeanCloud GitHub
适合场景 国内用户为主 面向全球/技术社区

5.5 配置访问统计功能

静态博客无法在后端直接记录访问量,需要借助第三方服务。以下介绍两种方案。

5.5.1 方案一:不蒜子(Busuanzi)- 最简单

不蒜子是一个轻量级的网站统计服务,只需一行代码即可集成,国内访问速度快。

1. 在模板中添加统计脚本

编辑博客主题的 base.htmlfooter.html,在 </body> 前添加:

<!-- 不蒜子统计 -->
<script async src="//busuanzi.icodeq.com/busuanzi/2.3/busuanzi.pure.mini.js"></script>

2. 显示页面阅读量

在文章页面 article.html 中添加阅读量显示位置:

<!-- 文章阅读量 -->
<span id="busuanzi_value_page_pv" class="page-view-count">0</span> 次阅读

3. 显示全站访客量(可选)

在页脚 footer.html 或侧边栏添加:

<!-- 全站访客统计 -->
<span id="busuanzi_value_site_pv">0</span> 次访问
<span id="busuanzi_value_site_uv">0</span> 位访客

4. CSS 样式自定义

/* 阅读量样式 */
.page-view-count {
    color: #666;
    font-size: 14px;
    margin-left: 5px;
}

/* 悬停提示 */
.page-view-count:hover {
    color: #3498db;
}

5. 不蒜子常用标签说明

标签 ID 说明 示例
busuanzi_value_site_pv 全站总访问量 1000 次访问
busuanzi_value_site_uv 全站总访客数 500 位访客
busuanzi_value_page_pv 当前页面访问量 100 次阅读

5.5.2 方案二:Valine Visitor(与评论系统共用)

如果你已集成 Valine 评论系统,可以直接开启 visitor 功能来统计文章阅读量,无需额外注册其他服务。

1. 修改 Valine 初始化配置

编辑 article.html 中的 Valine 配置,确保开启 visitor 选项:

<script>
    new Valine({
        el: '#vcomments',
        appId: '你的LeanCloud AppID',
        appKey: '你的LeanCloud AppKey',
        placeholder: '欢迎留言交流!',
        avatar: 'mm',
        visitor: true,        <!-- 开启文章阅读量统计 -->
        // ...其他配置
    });
</script>

2. 显示阅读量

Valine 会自动在评论区域上方显示阅读量。如需自定义显示位置,可在文章模板中添加:

<!-- Valine 阅读量(可选自定义显示) -->
<span class="article-visitor" id="<文章ID>-visitors">
    阅读: <span class="leancloud-visitors-count">0</span>
</span>

其中 <文章ID> 需要与 Valine 生成的 ID 一致,通常格式为 url_key-文章slug

3. 在文章元信息中显示阅读量

编辑 article.html 模板,在文章标题下方添加阅读量:

<article class="post">
    <header class="post-header">
        <h1 class="post-title">{{ article.title }}</h1>
        <div class="post-meta">
            <span class="post-date">{{ article.date.strftime('%Y-%m-%d') }}</span>
            <span class="post-category">{{ article.category }}</span>
            <!-- 阅读量显示 -->
            <span class="post-views">
                <i class="icon-view"></i>
                <span class="leancloud-visitors-count" id="{{ article.url }}-visitors">0</span> 阅读
            </span>
        </div>
    </header>
    <!-- 文章内容 ... -->
</article>

4. CSS 样式

/* 阅读量样式 */
.post-views {
    color: #888;
    font-size: 14px;
    margin-left: 15px;
}

.post-views .leancloud-visitors-count {
    color: #e74c3c;
    font-weight: bold;
}

5.5.3 两种方案对比

特性 不蒜子 Valine Visitor
集成复杂度 ⭐ 最简单(一行代码) ⭐⭐ 需开启配置
数据存储 第三方服务器 LeanCloud
国内访问 ✅ 快速 ✅ 快速
是否需要注册 ❌ 无需注册 ✅ 需 LeanCloud
功能 简单统计 评论 + 统计一体化
可自定义程度 中等

5.5.4 同时使用两种方案(互为备份)

如果希望同时使用两种统计方式,可以都集成,显示阅读量时取其中一个来源的数据:

<!-- 双统计(实际使用时可只显示一个) -->
<span class="view-count">
    阅读: <span id="busuanzi_value_page_pv">0</span> 次
    <!-- 或使用 Valine: -->
    <!-- <span class="leancloud-visitors-count">0</span> 次 -->
</span>

5.7 配置搜索功能 (Tipue Search)

Tipue Search 是一个纯静态的 JavaScript 搜索方案。

1. 安装 Tipue Search 插件

pip install pelican-tipue-search

2. 在 pelicanconf.py 中启用

PLUGINS = ['tipue_search']
DIRECT_TEMPLATES = ['index', 'tags', 'categories', 'archives', 'sitemap', 'search']
TEMPLATE_PAGES = {'search': 'search.html'}
TIPUE_SEARCH = True
TIPUE_SEARCH_MODE = 'static'

3. 创建搜索页面模板THEME/templates/search.html):

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="utf-8">
    <title>搜索 - {{ SITENAME }}</title>
    <link rel="stylesheet" href="{{ SITEURL }}/theme/css/normalize.css">
    <link rel="stylesheet" href="{{ SITEURL }}/theme/css/tipuesearch.css">
</head>
<body>
    <h1>搜索</h1>
    <form action="{{ SITEURL }}/search.html">
        <input type="text" name="q" id="tipue_search_input" placeholder="输入关键词搜索...">
        <button type="submit">搜索</button>
    </form>
    <div id="tipue_search_content"></div>
    <script src="{{ SITEURL }}/theme/js/tipuesearch_set.js"></script>
    <script src="{{ SITEURL }}/theme/js/tipuesearch.js"></script>
    <script>
        tipuesearch({"show": 10, "newWindow": false});
    </script>
</body>
</html>

4. 下载 Tipue Search 文件

# 创建目录
mkdir -p themes/your-theme/static/js
mkdir -p themes/your-theme/static/css

# 下载 Tipue Search 文件(从官网下载)
# https://tipue.com/search/
# 需要:tipuesearch.js, tipuesearch_set.js, tipuesearch.css, tipuesearch.img/

5.8 标签和分类的区别

特性 标签 (Tags) 分类 (Categories)
数量限制 文章可有多个标签 文章只能属于一个分类
层级 扁平结构 可嵌套层级
用途 关键词/主题 主要主题分组
页面 /tags/ /category/

示例

Title: 学习 Python 列表
Category: 编程        # 只能一个
Tags: Python, 列表, 入门  # 可以多个

8. 配置 Nginx

5.1 创建 Nginx 配置文件

nano /etc/nginx/conf.d/blog.conf

配置文件内容

server {
    listen 80;
    server_name chenlip.top www.chenlip.top;

    # 重定向到 HTTPS(可选,如已配置 SSL)
    # return 301 https://$server_name$request_uri;

    # 博客根目录
    root /home/app/my_blog/output;

    # 日志
    access_log /var/log/nginx/blog_access.log;
    error_log /var/log/nginx/blog_error.log;

    # 默认首页
    index index.html;

    # 字符集
    charset utf-8;

    # Gzip 压缩
    gzip on;
    gzip_vary on;
    gzip_min_length 1024;
    gzip_types text/plain text/css text/xml text/javascript 
               application/x-javascript application/xml application/rss+xml
               application/atom+xml image/svg+xml;

    # 静态文件缓存
    location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|woff|woff2|ttf|eot)$ {
        expires 30d;
        add_header Cache-Control "public, immutable";
    }

    # 安全 headers
    location ~ /\. {
        deny all;
    }

    # 主路由
    location / {
        try_files $uri $uri/ $uri.html =404;
    }

    # 禁止访问隐藏文件
    location ~ /\. {
        deny all;
        access_log off;
        log_not_found off;
    }
}

# HTTPS 配置(可选,如需要)
# server {
#     listen 443 ssl http2;
#     server_name chenlip.top www.chenlip.top;
#     
#     ssl_certificate /etc/letsencrypt/live/chenlip.top/fullchain.pem;
#     ssl_certificate_key /etc/letsencrypt/live/chenlip.top/privkey.pem;
#     
#     root /var/www/blog/output;
#     index index.html;
#     
#     # ... 其他配置同上
# }

5.2 测试并重载 Nginx

# 测试配置语法
nginx -t

# 重载 Nginx
systemctl reload nginx

# 启用开机自启
systemctl enable nginx

9. 配置 Git 仓库

6.1 在服务器上克隆仓库

# 切换到博客目录
cd /home/app/my_blog

# 克隆 Gitee 仓库(使用 HTTPS)
git clone https://gitee.com/hoeking/my_note.git .

# 或者使用 SSH(如果已配置密钥)
# git clone git@gitee.com:hoeking/my_note.git .

# 查看克隆的内容
ls -la

6.2 配置 Git 钩子(可选)

# 创建 post-merge 钩子(每次 git pull 后自动构建)
cat > .git/hooks/post-merge << 'EOF'
#!/bin/bash
# 自动构建博客(可选,按需启用)
# /home/app/my_blog/rebuild.sh
EOF

chmod +x .git/hooks/post-merge

10. 配置 Webhook 服务

7.1 创建 Python 依赖文件

# 激活虚拟环境
source /home/app/my_blog/.venv/bin/activate

# 创建 requirements.txt
cat > /home/app/my_blog/requirements.txt << 'EOF'
pelican[markdown]
fastapi
uvicorn[standard]
python-dotenv
httpx
EOF

7.2 创建 Webhook 服务脚本

# 创建 /home/app/my_blog/webhook_server.py, 请参考项目中的 webhook_server.py 文件


chmod +x /home/app/my_blog/webhook_server.py

7.3 创建构建脚本

# 创建 /home/app/my_blog/rebuild.sh, 请参考项目中的 rebuild.sh 文件

chmod +x /home/app/my_blog/rebuild.sh

# 创建日志文件
touch /var/log/pelican-rebuild.log

7.4 创建环境变量文件

# 创建 .env 文件, 请参考项目中的 .env 文件
cat > /home/app/my_blog/.env << 'EOF'
WEBHOOK_SECRET=xiongxiong-is-cool
EOF

7.5 配置 Systemd 服务

# 创建 systemd 服务文件, 请参考项目中的 blog-webhook.service 文件
cat > /etc/systemd/system/blog-webhook.service << 'EOF'
....
EOF
# 重新加载 systemd
systemctl daemon-reload

# 启动服务
systemctl start blog-webhook

# 检查状态
systemctl status blog-webhook

# 启用开机自启
systemctl enable blog-webhook

7.6 配置反向代理(可选)

如果您希望使用域名访问 Webhook 管理界面:

# /etc/nginx/conf.d/webhook.conf
server {
    listen 80;
    server_name webhook.chenlip.top;

    location / {
        proxy_pass http://127.0.0.1:5000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

7.7 忽略文件与 Webhook 构建流程

Webhook 自动构建时,pelicanconf.py 中的 IGNORE_FILES 配置会自动生效:

┌─────────────────────────────────────────────────────────────┐
│  1. Git Push 触发 Webhook                                   │
└─────────────────────┬───────────────────────────────────────┘
                      ▼
┌─────────────────────────────────────────────────────────────┐
│  2. 服务器执行 rebuild.sh                                    │
│     - git pull 拉取所有文件(包括 _drafts/、*.bak 等)         │
└─────────────────────┬───────────────────────────────────────┘
                      ▼
┌─────────────────────────────────────────────────────────────┐
│  3. Pelican 构建时自动过滤                                   │
│     - 根据 pelicanconf.py 的 IGNORE_FILES 配置               │
│     - 跳过以下划线开头的文件和目录                             │
│     - 跳过 draft-*、private-* 等模式                         │
│     - 只生成正常文章到 output/ 目录                           │
└─────────────────────┬───────────────────────────────────────┘
                      ▼
┌─────────────────────────────────────────────────────────────┐
│  4. Nginx 发布 output/ 目录                                  │
│     - 草稿、临时文件不会出现在网站上                            │
└─────────────────────────────────────────────────────────────┘

结论:无需额外配置,Pelican 会自动根据 IGNORE_FILES 在构建时过滤文件。


11. 配置 Gitee Webhook

8.1 在 Gitee 仓库设置 Webhook

  1. 登录 Gitee,进入 hoeking/my_note 仓库
  2. 点击 管理WebHooks添加 WebHook
  3. 填写配置:
配置项
WebHook 地址 http://chenlip.top:5000/webhookhttp://<服务器IP>:5000/webhook
密钥 设置一个安全的密钥(如 MySecretKey123!
触发条件 选择 Push 事件
  1. 点击 添加

8.2 在服务器上设置密钥

# 编辑 .env 文件, 或者将 example.env 复制为 .env
cat > /home/app/my_blog/.env << 'EOF'
WEBHOOK_SECRET=MySecretKey123!
EOF

# 重启服务
systemctl restart blog-webhook

8.3 测试 Webhook

# 手动测试 Webhook
curl -X POST http://127.0.0.1:5000/webhook \
  -H "Content-Type: application/json" \
  -H "X-Gitee-Event: Push Hook" \
  -d '{"ref": "refs/heads/master", "commits": []}'

# 检查服务日志
journalctl -u blog-webhook -f

12. 初始化博客内容

9.1 本地测试构建

在本地 Windows 机器上

请参考上面 第 2 章 中的说明。开始编写博客文章。

9.2 推送到 Gitee

:: 初始化 Git(如果尚未初始化)
git init
git remote add origin https://gitee.com/hoeking/my_note.git

:: 添加所有文件
git add .

:: 提交
git commit -m "初始化博客项目"

:: 推送
git push -u origin master

9.3 触发首次构建

推送后,Gitee 会发送 Webhook 请求,服务器会自动构建博客(前提是服务端已经部署好环境并启动了 Webhook 服务)。

或者手动触发:

# 在服务器上
cd /home/app/my_blog
git pull origin master
/home/app/my_blog/rebuild.sh

13. 验证部署

10.1 检查服务状态

# 检查 Nginx
systemctl status nginx

# 检查 Webhook 服务
systemctl status blog-webhook

# 检查进程
ps aux | grep -E "nginx|pelican|uvicorn"

10.2 检查日志

# Nginx 访问日志
tail -f /var/log/nginx/blog_access.log

# Nginx 错误日志
tail -f /var/log/nginx/blog_error.log

# Webhook 服务日志
journalctl -u blog-webhook -f

# 构建日志
tail -f /var/log/pelican-rebuild.log

10.3 测试访问

# 测试 HTTP 访问
curl -I http://chenlip.top

# 测试 HTTPS(如果配置了)
curl -I https://chenlip.top

# 检查页面内容
curl -s http://chenlip.top | head -20

14. SSL/HTTPS 配置(可选但推荐)

11.1 安装 Certbot

# 安装 Certbot
dnf install -y epel-release
dnf install -y certbot python3-certbot-nginx

# 申请证书
certbot --nginx -d chenlip.top -d www.chenlip.top

11.2 自动续期

# 测试自动续期
certbot renew --dry-run

# 添加定时任务
crontab -e
# 添加:0 0 * * * certbot renew

15. 故障排查

12.1 常见问题

问题 可能原因 解决方案
Webhook 无法访问 防火墙未开放 5000 端口 开放端口或使用反向代理
构建失败 权限问题 检查目录权限
页面样式丢失 STATIC_PATH 配置错误 检查主题静态文件路径
中文乱码 文件编码问题 确保使用 UTF-8 编码
UnicodeDecodeError Windows 编码与文件编码不匹配 使用 dev_win.bat 启动(见下方说明)
Docutils 中文警告 Docutils 无中文本地化 可忽略,不影响功能

12.2 Windows 本地开发编码问题详解

12.2.1 问题原因

Windows 中文版的默认编码是 GBK (代码页 936),而 Pelican 的内容文件(Markdown)是 UTF-8 编码。当 Python 用 GBK 去读 UTF-8 文件时,就会出现 UnicodeDecodeError

┌─────────────────────────────────────────────────────────────┐
│                     编码不匹配问题                            │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│   你的 .md 文件保存格式      Python 默认读取编码               │
│        ↓                              ↓                     │
│   ┌──────────┐                  ┌──────────┐                │
│   │ UTF-8    │    ≠ ≠ ≠ ≠ ≠    │ GBK      │                │
│   │ 中文"你好"│                  │ 乱码"浣犲ソ"│                │
│   └──────────┘                  └──────────┘                │
│                                                             │
│   → Python 用 GBK 解码 UTF-8 文件 = 崩溃                     │
└─────────────────────────────────────────────────────────────┘

12.2.2 解决方案:使用 dev_win.bat启动

完整的 dev_win.bat内容见第 2.9.1 节,此处仅说明编码相关的关键配置。

D:\pythonapp\my_blog 目录下创建 dev_win.bat 文件,关键编码设置如下:

@echo off
chcp 65001 >nul
set PYTHONIOENCODING=utf-8
set PYTHONLEGACYWINDOWSSTDIO=utf-8
title Pelican Blog Manager

cd /d "%~dp0"
:: ... 完整内容见 2.9.1 节

⚠️ 重要:dev_win.bat已更新,新增了"自动重载"和"Invoke livereload"选项,详见第 2.9.1 节。

12.2.3 dev_win.bat配置说明

命令 作用
chcp 65001 让 CMD 窗口显示 UTF-8
PYTHONIOENCODING=utf-8 让 Python 输出/读取 UTF-8
PYTHONLEGACYWINDOWSSTDIO=utf-8 让 Windows 标准输入输出使用 UTF-8

注意chcp 65001 和环境变量设置的是不同层面的编码,两者缺一不可!

12.2.4 CentOS 上的编码问题

在 CentOS/Linux 上基本不会遇到编码问题!

系统 默认编码 说明
Windows GBK (cp936) 混乱,容易出问题
CentOS/Linux UTF-8 统一,兼容性好

CentOS 上可以直接运行:

pelican content/

不需要任何额外的编码设置。

12.2.5 Docutils 中文警告说明

运行 Pelican 时可能会看到以下警告:

WARNING  Docutils has no localization for 'zh'. Using 'en' instead.

含义:Docutils(文档处理库)没有中文语言包,所以用英文替代。

组成部分 含义
Docutils Python 文档工具库,Pelican 用它处理 reStructuredText 格式
no localization for 'zh' 没有中文本地化文件
Using 'en' instead 退回到英文

这只是一个警告,不影响功能! 可以安全忽略。

12.3 调试命令

# 查看所有运行的服务
systemctl list-units --type=service

# 查看端口占用
netstat -tlnp | grep -E "80|443|5000"

# 测试 Python 环境
source /home/app/my_blog/.venv/bin/activate
python -c "import fastapi; print(fastapi.__version__)"

# 手动运行构建
cd /home/app/my_blog
source /home/app/my_blog/.venv/bin/activate
pelican content/ -s publishconf.py -v

16. 维护命令

13.1 常用操作

# 激活虚拟环境
source /home/app/my_blog/.venv/bin/activate

# 手动触发构建
/home/app/my_blog/rebuild.sh

# 重启 Webhook 服务
systemctl restart blog-webhook

# 重载 Nginx
systemctl reload nginx

# 查看构建历史
tail -20 /var/log/pelican-rebuild.log

# 更新依赖
pip install --upgrade pelican fastapi uvicorn

13.2 备份

# 备份博客内容
tar -czf blog-backup-$(date +%Y%m%d).tar.gz /home/app/my_blog

# 备份配置
cp /home/app/my_blog/.env /root/blog-env-backup

17. 完整部署检查清单

  • [x] 服务器环境已准备(Python、Git、Nginx、uv 已安装)
  • [x] 防火墙已配置(80、443 端口)
  • [ ] /home/app/my_blog 目录已创建
  • [ ] uv 虚拟环境已创建(Python 3.11)
  • [ ] Pelican 和依赖已安装到虚拟环境
  • [ ] 配置文件已创建
  • [ ] Nginx 已配置并运行
  • [ ] Git 仓库已克隆到 /home/app/my_blog
  • [ ] Webhook 服务已安装并运行
  • [ ] Systemd 服务已配置
  • [ ] Gitee Webhook 已设置
  • [ ] SSL 证书已配置(如需要)
  • [ ] 首次构建测试成功

文档信息

  • 部署日期:2024年
  • 服务器:CentOS 9 Stream
  • 适用版本:Pelican 最新版