有时候我们写了几篇 Markdown 笔记或文档,想快速生成一个带导航的静态网站,又不想用 Hugo、Hexo 这些重型框架。今天我们就用 OpenCode 命令行 AI 助手,4 分钟内写一个 Markdown 静态站点生成器——把 .md 文件批量转换成 HTML 页面,自动生成首页目录,一行命令搞定。
markdown 库python ssg.py src/ dist/ → 得到一套精美的 HTML 站点安装 OpenCode 后,确保有 Python 3 环境。依赖只需要一个 markdown 包:
pip install markdown
然后打开终端,进入项目目录,开始对 OpenCode 下达指令。
我直接对 OpenCode 说:
> 帮我创建一个 Python 脚本 ssg.py,先搭好基本框架:读取命令行参数 src 和 dist 目录,扫描 src 下所有 .md 文件。
OpenCode 立刻生成了入口代码:
import sys
from pathlib import Path
def main():
src_dir = Path(sys.argv[1]) if len(sys.argv) > 1 else Path('src')
dst_dir = Path(sys.argv[2]) if len(sys.argv) > 2 else Path('dist')
if not src_dir.exists():
print(f'错误: 源目录 "{src_dir}" 不存在')
sys.exit(1)
md_files = list(src_dir.glob('**/*.md'))
print(f'找到 {len(md_files)} 个 Markdown 文件')
if __name__ == '__main__':
main()
要点:pathlib.Path.glob('**/*.md') 比 os.walk 更简洁,递归扫描所有子目录。OpenCode 自动选择了现代 Python 写法,不需要我额外指定。
我接着对它说:
> 加入 HTML 模板和 markdown 转换逻辑,用 markdown 库把每个 .md 转为 HTML 页面。
OpenCode 补充了核心转换函数和 HTML 模板:
import markdown
HTML_TEMPLATE = """<!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: system-ui; max-width: 800px; margin: 0 auto; padding: 20px; }
nav { border-bottom: 1px solid #eee; padding-bottom: 10px; margin-bottom: 30px; }
nav a { margin-right: 15px; color: #0366d6; text-decoration: none; }
pre { background: #f6f8fa; padding: 16px; border-radius: 6px; overflow-x: auto; }
code { background: #f6f8fa; padding: 2px 6px; border-radius: 3px; }
table { border-collapse: collapse; width: 100%; }
th, td { border: 1px solid #ddd; padding: 8px 12px; }
</style>
</head>
<body>
<nav><a href="/index.html">首页</a></nav>
<main>{{content}}</main>
</body>
</html>"""
def convert_file(src: Path, dst: Path):
content = src.read_text(encoding='utf-8')
html_body = markdown.markdown(content, extensions=['fenced_code', 'tables'])
title = src.stem
html = HTML_TEMPLATE.replace('{{title}}', title).replace('{{content}}', html_body)
dst.parent.mkdir(parents=True, exist_ok=True)
dst.write_text(html, encoding='utf-8')
return title, dst
要点:markdown.markdown() 的 extensions 参数很关键——fenced_code 支持围栏代码块,tables 支持 GFM 表格。OpenCode 自动加上了这两个常用扩展,比手写正则靠谱得多。
我对 OpenCode 说:
> 再生成一个 index.html 首页,列出所有文章的链接。
OpenCode 给出了索引页的生成逻辑:
INDEX_TEMPLATE = """<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>站点目录</title>
<style>
body { font-family: system-ui; max-width: 800px; margin: 0 auto; padding: 20px; }
h1 { border-bottom: 2px solid #0366d6; padding-bottom: 10px; }
ul { list-style: none; padding: 0; }
li { margin: 12px 0; }
a { color: #0366d6; text-decoration: none; font-size: 1.1em; }
a:hover { text-decoration: underline; }
</style>
</head>
<body>
<h1>文章列表</h1>
<ul>{{links}}</ul>
</body>
</html>"""
def generate_index(files, output_dir: Path):
links = []
for title, path in files:
rel_path = path.relative_to(output_dir).as_posix()
links.append(f'<li><a href="/{rel_path}">{title}</a></li>')
html = INDEX_TEMPLATE.replace('{{links}}', '\n'.join(links))
(output_dir / 'index.html').write_text(html, encoding='utf-8')
要点:首页目录用 path.relative_to().as_posix() 生成跨平台的相对路径,保证 Windows 和 Linux 下都能正常访问。
最后我对 OpenCode 说:
> 把上面的函数串起来,在 main 里调用 convert_file 和 generate_index。
OpenCode 完成了主函数,我去终端测试:
# 创建测试目录和一篇示例文章 mkdir src echo '# Hello World 这是一篇测试文章。
print("Hello, OpenCode!")
# 运行生成器 python ssg.py src dist
输出:
✓ src\hello.md → dist\hello.html 完成! 共转换 1 个文件,输出目录: dist
用浏览器打开 dist/index.html,首页上出现了文章链接,点击进入后代码高亮、表格样式都正常渲染。
#!/usr/bin/env python3
import sys
from pathlib import Path
import markdown
HTML_TEMPLATE = """<!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: system-ui; max-width: 800px; margin: 0 auto; padding: 20px; }
nav { border-bottom: 1px solid #eee; padding-bottom: 10px; margin-bottom: 30px; }
nav a { margin-right: 15px; color: #0366d6; text-decoration: none; }
pre { background: #f6f8fa; padding: 16px; border-radius: 6px; overflow-x: auto; }
code { background: #f6f8fa; padding: 2px 6px; border-radius: 3px; }
table { border-collapse: collapse; width: 100%; margin: 15px 0; }
th, td { border: 1px solid #ddd; padding: 8px 12px; }
th { background: #f6f8fa; }
blockquote { border-left: 4px solid #0366d6; padding-left: 15px; color: #666; }
</style>
</head>
<body>
<nav><a href="/index.html">首页</a></nav>
<main>{{content}}</main>
</body>
</html>"""
INDEX_TEMPLATE = """<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>站点目录</title>
<style>
body { font-family: system-ui; max-width: 800px; margin: 0 auto; padding: 20px; }
h1 { border-bottom: 2px solid #0366d6; padding-bottom: 10px; }
ul { list-style: none; padding: 0; }
li { margin: 12px 0; }
a { color: #0366d6; text-decoration: none; font-size: 1.1em; }
a:hover { text-decoration: underline; }
</style>
</head>
<body>
<h1>文章列表</h1>
<ul>{{links}}</ul>
</body>
</html>"""
def convert_file(src: Path, dst: Path):
content = src.read_text(encoding='utf-8')
html_body = markdown.markdown(content, extensions=['fenced_code', 'tables'])
title = src.stem
html = HTML_TEMPLATE.replace('{{title}}', title).replace('{{content}}', html_body)
dst.parent.mkdir(parents=True, exist_ok=True)
dst.write_text(html, encoding='utf-8')
return title, dst
def generate_index(files, output_dir: Path):
links = []
for title, path in files:
rel_path = path.relative_to(output_dir).as_posix()
links.append(f'<li><a href="/{rel_path}">{title}</a></li>')
html = INDEX_TEMPLATE.replace('{{links}}', '\n'.join(links))
(output_dir / 'index.html').write_text(html, encoding='utf-8')
def main():
src_dir = Path(sys.argv[1]) if len(sys.argv) > 1 else Path('src')
dst_dir = Path(sys.argv[2]) if len(sys.argv) > 2 else Path('dist')
if not src_dir.exists():
print(f'错误: 源目录 "{src_dir}" 不存在')
sys.exit(1)
md_files = list(src_dir.glob('**/*.md'))
if not md_files:
print(f'警告: 在 "{src_dir}" 中没有找到 .md 文件')
sys.exit(0)
dst_dir.mkdir(parents=True, exist_ok=True)
results = []
for md_file in md_files:
dst = dst_dir / md_file.relative_to(src_dir).with_suffix('.html')
title, path = convert_file(md_file, dst)
results.append((title, path))
print(f'✓ {md_file} → {dst}')
generate_index(results, dst_dir)
print(f'\n完成! 共转换 {len(results)} 个文件,输出目录: {dst_dir}')
if __name__ == '__main__':
main()
全程 4 分钟,我只用了 4 句自然语言指令,OpenCode 自动完成了:
项目骨架搭建
Markdown 到 HTML 的转换(含代码高亮、表格支持)
首页目录生成
完整的错误处理和控制台反馈
相比手动编写,省去了查文档、写样板代码和调试 CSS 的时间。最让我惊喜的是,OpenCode 自动选了 pathlib 而非 os.path、自动加了 fenced_code 和 tables 扩展——这些恰恰是新手容易漏掉的细节。
用 AI 编程助手开发工具类的项目,效率提升非常明显。你的角色从"码农"变成了"产品经理"——想清楚要什么,剩下的交给 AI。