Hexo博客集成抖音视频实战指南

前言

作为一名钓鱼爱好者,我在抖音分享了许多钓鱼视频。为了让博客访问者能够直观地浏览我的抖音作品,决定在Hexo博客中集成抖音视频相册功能。

本文将详细介绍整个实现过程,从数据配置到页面展示,再到进阶优化,帮助你快速在博客中实现类似功能。

一、需求分析与方案设计

1.1 功能需求

在博客中展示抖音视频作品,需要实现以下核心功能:

功能 描述
视频列表展示 以网格形式展示视频封面卡片
标题与日期 在封面上显示视频标题和发布日期
视频播放 点击卡片弹出播放器,支持上一首/下一首切换
数据管理 通过JSON文件管理视频数据,支持增删改查
年份分类 按视频发布年份进行分组展示

1.2 技术选型

  • 静态博客框架:Hexo v6+
  • 主题:Next v8+
  • 数据格式:JSON(轻量、易维护)
  • 页面渲染:Nunjucks(Swig模板引擎)
  • 样式方案:Stylus(主题内置支持)

1.3 整体架构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
┌─────────────────────────────────────────────────────┐
│ 数据层 │
│ source/douyin/videos.json │
│ (标题、日期、视频ID、描述、封面图URL) │
└─────────────────────┬───────────────────────────────┘
↓ Hexo 生成前
┌─────────────────────────────────────────────────────┐
│ 处理层 │
│ themes/next/scripts/filters/douyin-videos.js │
│ (读取JSON → 按年份分组 → 注入主题配置) │
└─────────────────────┬───────────────────────────────┘
↓ 模板渲染
┌─────────────────────────────────────────────────────┐
│ 展示层 │
│ themes/next/layout/douyin.swig │
│ source/_data/styles.styl │
│ (视频卡片网格、弹窗播放器、封面遮罩层) │
└─────────────────────────────────────────────────────┘

数据流向:

  1. videos.json 存储原始视频数据
  2. douyin-videos.js 在Hexo生成前读取数据,按年份分组
  3. 注入 theme.douyinVideos 全局配置
  4. douyin.swig 模板循环读取配置,渲染页面
  5. 浏览器端JavaScript处理弹窗播放逻辑

二、核心实现步骤

2.1 创建视频数据文件

source/douyin/ 目录下创建 videos.json 文件,这是整个功能的数据源。

文件结构:

1
2
3
4
5
6
7
8
9
10
11
{
"videos": [
{
"title": "天气转凉",
"date": "2025-11-02",
"short_id": "RkEfPk9Pzj4",
"video_id": "7568078861650152723",
"desc": "天气转凉,钓鱼爱好者们,你们都空军了吗?"
}
]
}

字段说明:

字段 类型 必填 说明
title 字符串 视频标题,用于展示和提取
date 字符串 发布日期,格式:YYYY-MM-DD,用于排序和年份分组
short_id 字符串 抖音短链ID(如 v.douyin.com/xxx 中的 xxx)
video_id 字符串 视频完整ID,19位数字,用于播放和封面获取
desc 字符串 视频描述文字
cover 字符串 封面图URL,不填则使用随机图

2.2 创建数据处理脚本

themes/next/scripts/filters/ 目录下创建 douyin-videos.js。这是一个Hexo过滤器,在生成页面之前执行。

核心逻辑:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
hexo.extend.filter.register('before_generate', function() {
// 1. 读取videos.json文件
const config = JSON.parse(fs.readFileSync(configFile, 'utf8'));

// 2. 按年份分组
const grouped = {};
videos.forEach(video => {
const year = video.date.split('-')[0]; // 提取年份
if (!grouped[year]) grouped[year] = [];
grouped[year].push({ /* 视频数据 */ });
});

// 3. 注入主题配置
hexo.theme.config.douyinVideos = {
years: Object.keys(grouped).sort((a, b) => b - a), // 年份降序
grouped,
allVideos: videos.flatMap(y => grouped[y]), // 扁平数组
videoCount: videos.length
};
});

输出数据:

变量 类型 说明
theme.douyinVideos.years 数组 所有年份,如 ['2025', '2024', '2023']
theme.douyinVideos.grouped 对象 按年份分组,{ '2025': [...], '2024': [...] }
theme.douyinVideos.allVideos 数组 所有视频扁平数组,按年份降序
theme.douyinVideos.videoCount 数字 视频总数

2.3 创建页面模板

themes/next/layout/ 目录下创建 douyin.swig

关键实现点:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{% for video in theme.douyinVideos.allVideos %}
<div class="videos-item video-card" data-id="{{ video.video_id }}">
<!-- 封面图:有cover用cover,没有用随机图 -->
<div class="video-cover" style="background-image: url('{{ video.cover || ('https://picsum.photos/180/220?random=' + video.video_id) }}');">
<!-- 播放图标 -->
<div class="video-play-icon"><i class="fa fa-play"></i></div>
<!-- 标题和日期遮罩层 -->
<div class="video-overlay">
<!-- truncate(10, true) 限制标题为10字符 -->
<div class="video-title">{{ video.title | truncate(10, true) }}</div>
<div class="video-date">{{ video.date }}</div>
</div>
</div>
<!-- 描述文字 -->
<div class="video-desc">{{ video.desc }}</div>
</div>
{% endfor %}

模板亮点:

  • truncate(10, true) Nunjucks过滤器限制标题为10个字符
  • 封面图使用视频ID作为随机种子,保证每次访问图片一致
  • 支持三元运算符处理可选封面

2.4 配置样式

source/_data/styles.styl 中添加样式。

核心样式说明:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
// 视频网格布局
.videos-grid {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 20px;
}

// 视频卡片
.video-cover {
width: 180px;
height: 220px;
border: 2px solid #e74c3c; // 主题红边框
border-radius: 6px;
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
transition: all 0.3s ease;

&:hover {
transform: scale(1.03); // 悬浮放大
border-color: #c0392b;
}
}

// 标题和日期遮罩层
.video-overlay {
position: absolute;
bottom: 0;
left: 0;
right: 0;
padding: 8px 10px 6px;
// 渐变遮罩:底部黑 → 顶部透明
background: linear-gradient(to top, rgba(0,0,0,0.85) 0%, transparent 100%);

.video-title {
color: #fff;
font-size: 14px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}

.video-date {
color: rgba(255,255,255,0.85);
font-size: 11px;
margin-top: 2px;
}
}

2.5 创建页面入口

source/douyin/index.md 创建页面文件:

1
2
3
4
5
6
---
title: 抖音作品
date: 2026-04-01 12:00:00
type: "douyin"
layout: "douyin"
---

只需配置 layout: "douyin",Hexo会自动使用 douyin.swig 模板渲染。

2.6 配置菜单

themes/next/languages/zh-CN.yml 中添加:

1
2
menu:
douyin: 抖音作品

三、视频播放功能实现

3.1 弹窗HTML结构

douyin.swig 模板末尾添加弹窗代码:

1
2
3
4
5
6
7
8
<div id="douyin-modal" class="modal">
<div class="modal-content">
<span class="close-btn">&times;</span>
<span class="nav-btn prev-btn">&#10094;</span>
<span class="nav-btn next-btn">&#10095;</span>
<iframe id="douyin-player" frameborder="0" allowfullscreen></iframe>
</div>
</div>

3.2 弹窗样式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
.modal {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0,0,0,0.9);
z-index: 9999;

&.active { display: block; }
}

.modal-content {
width: 80%;
max-width: 900px;
margin: 5% auto;
background: #000;
border-radius: 8px;

iframe {
width: 100%;
height: 500px;
}
}

.close-btn {
position: absolute;
top: -40px;
right: 0;
color: #fff;
font-size: 32px;
cursor: pointer;
}

3.3 播放逻辑(JavaScript)

视频播放使用抖音开放平台的嵌入播放器:

1
https://open.douyin.com/player/video?vid={video_id}

JavaScript实现的功能:

功能 实现方式
点击打开弹窗 .video-card 绑定点击事件
加载视频 设置iframe.src为播放地址
关闭弹窗 点击关闭按钮或背景区域
上一首/下一首 数组索引加减,支持循环
禁止背景滚动 弹窗打开时设置 body.overflow: hidden

四、辅助工具详解

为了更方便地管理视频数据,我开发了两个Python工具。

4.1 抖音视频链接转换工具

工具位置: tools/抖音视频链接转换工具/

功能:

  • 从抖音分享文本中提取视频信息
  • 自动获取视频完整ID(通过短链请求)
  • 生成可直接使用的JSON格式

4.1.1 输入文件格式

douyin_input.txt 中按以下格式放置抖音分享文本(每行一条):

1
2
3
4
5
6
# 格式1:从抖音APP复制的完整文本(推荐)
6.46 l@C.uf ZZm:/ 05/18 # 视频标题 # 标签
https://v.douyin.com/u5QvD6hzoZU/ 复制此链接,打开Dou音搜索,直接观看视频!

# 格式2:仅包含链接
https://v.douyin.com/xkqUZFxICQM/

文本特征说明:

位置 内容示例 说明
开头 6.46 l@C.uf ZZm:/ 抖音APP复制的标识符(可忽略)
日期 05/18 月/日格式,会自动补充年份
标题区 视频标题文字 提取第一个分隔符前的内容
链接 https://v.douyin.com/xxx/ 短链ID提取

4.1.2 运行步骤

1
2
3
4
5
6
7
8
9
10
11
# 1. 进入工具目录
cd tools/抖音视频链接转换工具

# 2. 运行工具(无输入文件会自动创建示例)
python 抖音视频链接转换工具.py

# 3. 查看输出
# 生成 douyin_output.json 文件

# 4. 复制内容到数据文件
# 将 douyin_output.json 的内容复制到 source/douyin/videos.json

工具输出示例:

1
2
3
4
5
6
7
8
9
10
11
==================================================
抖音视频链接转换工具
==================================================

正在获取 video_id: u5QvD6hzoZU ...成功 -> 7286633511249825079
✓ 提取: # 我要上热门 # 神奇动物在抖音 (ID: 7286633511249825079)

处理完成! 共转换 1 个视频
输出文件: douyin_output.json

将 douyin_output.json 内容复制到 source/douyin/videos.json 即可

4.1.3 核心处理逻辑

工具内部处理流程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def process(text):
# 1. 提取日期
# 从 "05/18" 格式提取月日,自动判断年份
# 如果提取的月份大于当前月份,说明是去年发布

# 2. 提取短链ID
# 正则匹配: v.douyin.com/xxx

# 3. 请求短链获取完整ID
# 访问 https://v.douyin.com/xxx
# 从响应URL中提取 /video/数字

# 4. 提取标题
# 取描述文字的第一个分隔符前内容
# 分隔符:,。, ——

# 5. 清理描述
# 去除#标签、链接、提示语

4.2 抖音视频ID提取工具

工具位置: tools/抖音视频ID提取工具/

功能:

  • 为已有视频数据补充缺失的video_id
  • 自动获取视频封面图URL
  • 验证video_id格式是否正确(19位数字)

使用场景:

场景 说明
补充ID 分享链接已过期,只能通过已有短链重新获取ID
获取封面 需要为视频添加封面图时自动抓取
数据校验 验证video_id是否为19位有效数字

运行命令:

1
2
cd tools/抖音视频ID提取工具
python douyin-id-extractor.py

输出示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
==================================================
抖音视频ID提取工具
==================================================

共找到 38 个视频

[1/38] 寒冬冰水中的连竿喜悦...
-> 已有完整ID: 7314629156933864742
-> 正在获取封面图...
-> 封面图: https://p3-sign.douyinpic.com/...

完成! 更新了 15 个视频封面图
配置文件已保存: source/douyin/videos.json

4.3 完整使用流程

1
2
3
4
5
6
7
8
9
10
11
步骤1:获取抖音分享链接

步骤2:复制到douyin_input.txt(每行一条)

步骤3:运行"抖音视频链接转换工具"

步骤4:复制douyin_output.json内容到videos.json

步骤5:运行hexo g验证效果

(如需封面)运行"抖音视频ID提取工具"

五、进阶功能与优化

5.1 视频排序算法

默认按日期降序排列(最新的在前)。如需调整排序,可使用以下Python脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import json

with open('source/douyin/videos.json', 'r', encoding='utf-8') as f:
data = json.load(f)

videos = data.get('videos', [])

# 按日期降序排列
videos.sort(key=lambda x: x.get('date', ''), reverse=True)

# 按列优先重排(每行4个,左到右日期递减)
row_size = 4
cols = [[] for _ in range(row_size)]
for i, video in enumerate(videos):
cols[i % row_size].append(video)

new_videos = []
for i in range(len(videos)):
new_videos.append(cols[i % row_size][i // row_size])

data['videos'] = new_videos

with open('source/douyin/videos.json', 'w', encoding='utf-8') as f:
json.dump(data, f, ensure_ascii=False, indent=2)

排序效果:

位置 排序前 排序后
第1行第1列 最早 最新
第1行最后 最晚 较新
最后1行第1列 最新 最旧

5.2 响应式布局

样式中已包含响应式断点:

屏幕宽度 列宽 间距
> 768px 180px 20px
480-768px 160px 16px
< 480px 140px 12px

六、常见问题

6.1 日期提取错误

问题: 从分享文本中自动提取的日期可能是错误的,特别是未来日期。

原因: 日期格式 MM/DD 缺少年份,工具通过当前年份推断,可能出错。

解决方案:

  1. 手动核对 videos.json 中的日期字段
  2. 修正错误日期后重新生成:hexo clean && hexo g

6.2 视频播放地址失效

问题: 弹窗无法播放视频。

原因: 抖音播放器地址可能发生变化。

解决方案:

  1. 使用 https://www.douyin.com/video/{video_id} 直接访问
  2. 使用抖音开放平台的 embed 接口
  3. 考虑直接跳转抖音链接打开

6.3 修改后页面未更新

问题: 修改了配置但页面没有变化。

解决方案:

1
2
# 清理缓存后重新生成
hexo clean && hexo g

七、文件结构汇总

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
BlogCode/
├── source/
│ ├── douyin/
│ │ ├── videos.json # 视频数据文件
│ │ └── index.md # 页面入口
│ └── _posts/ # 博客文章
│ └── Hexo博客集成抖音视频相册实战指南.md

├── themes/next/
│ ├── layout/
│ │ └── douyin.swig # 页面模板
│ ├── scripts/filters/
│ │ └── douyin-videos.js # 数据处理脚本
│ └── languages/
│ └── zh-CN.yml # 菜单配置

├── source/_data/
│ └── styles.styl # 样式文件

└── tools/
├── 抖音视频链接转换工具/ # 工具1
└── 抖音视频ID提取工具/ # 工具2

八、总结

通过本文的详细讲解,你应该能够:

  1. ✅ 理解三层架构设计(数据层→处理层→展示层)
  2. ✅ 创建和管理视频数据(videos.json)
  3. ✅ 实现视频列表的网格展示
  4. ✅ 添加标题和日期的封面遮罩展示
  5. ✅ 实现视频弹窗播放功能(支持上一首/下一首)
  6. ✅ 使用辅助工具自动化获取视频信息
  7. ✅ 处理常见问题(日期错误、播放失效)

这个抖音视频相册功能为博客增添了一个生动的展示窗口,让访问者能够更直观地了解我的钓鱼生活。

如果你在实现过程中遇到任何问题,欢迎在评论区留言交流!


查看效果

想查看抖音视频相册的实际效果吗?点击下方链接:

👉 抖音作品 - 我的抖音视频列表


本文对应的代码版本:v1.0