【翻译】编写一个小型静态网站生成器
大概有一百多种用 Python 编写的静态站点生成器(甚至还有其他语言编写的静态站点生成器)。
所以我决定写我自己的。为什么?好吧,我只是想。我希望将自己的博客从Ghost移开,并且希望保持真正的简约性。我决定使用 GitHub Pages 托管,因为他们最近宣布支持 自定义域的SSL。
渲染内容
每个静态网站生成器都需要采用某种源格式(例如 Markdown 或 ReStructuredText)并将其转换为 HTML。自从我离开 Ghost 以来,我决定坚持 Markdown。
自从我最近将 Github风格的Markdown渲染 集成到 Warehouse 中以来,我决定使用 cmarkgfm 。使用以下方式将 Markdown 渲染为 HTML:
import cmarkgfm
def render_markdown(content: str) -> str:
content = cmarkgfm.markdown_to_html_with_extensions(
content,
extensions=['table', 'autolink', 'strikethrough'])
return content
cmarkgfm 确实有一个名为 github_flavored_markdown_to_html 的便捷方法,但是它使用 GitHub 的 tagfilter 扩展名,当我要将脚本和内容嵌入到帖子中时,这是不希望的。因此,我只是选择了我想使用的扩展。
收集资源(所有文档)
好的,我们有一种渲染 Markdown 的方法,但是我们还需要一种收集所有源文件的方法。我决定将所有来源存储在 ./src。我们可以pathlib用来收集它们:
import pathlib
from typing import Iterator
def get_sources() -> Iterator[pathlib.Path]:
return pathlib.Path('.').glob('srcs/*.md')
头部元数据
许多静态网站生成器都有 “前题” 的概念-一种为每个源文件设置元数据等的方法。我想支持 frontmatter,让我为每个帖子设置日期和标题。看起来像这样:
---
title: Post time
date: 2018-05-11
---
# Markdown content here.
对于 frontmatter 有一个非常好的和简单的现有库,称为 python-frontmatter。我可以用它来提取前题和原始内容:
import frontmatter
def parse_source(source: pathlib.Path) -> frontmatter.Post:
post = frontmatter.load(str(source))
return post
返回的 post 对象的 .content 属性具有实际内容,而 .keys 属性则具有前题的值,可以通过 post['title'] 等方式调用。
渲染帖子
现在我们有了帖子的内容和要点,我们可以渲染它们。我决定使用 jinja2 将 cmarkgfm 渲染后的 Markdown 和 frontmatter 放置到一个简单的HTML模板中。
这里是模板:
<!doctype html>
<html>
<head><title>{{post.title}}</title></head>
<body>
<h1>{{post.title}}</h1>
<em>Posted on {{post.date.strftime('%B %d, %Y')}}</em>
<article>
{{content}}
</article>
</body>
</html>
这里是 python 的渲染代码:
import jinja2
jinja_env = jinja2.Environment(
loader=jinja2.FileSystemLoader('templates'),
)
def write_post(post: frontmatter.Post, content: str):
path = pathlib.Path("./docs/{}.html".format(post['stem']))
template = jinja_env.get_template('post.html')
rendered = template.render(post=post, content=content)
path.write_text(rendered)
请注意,我将渲染后的 HTML 文件存储在 ./docs 中。这是因为我将 GitHub Pages 配置为发布 doc 目录 中的内容。
现在我们可以渲染单个帖子,我们可以使用一开始创建的 get_sources 函数遍历所有帖子:
from typing import Sequence
def write_posts() -> Sequence[frontmatter.Post]:
posts = []
sources = get_sources()
for source in sources:
# Get the Markdown and frontmatter.
post = parse_source(source)
# Render the markdown to HTML.
content = render_markdown(post.content)
# Write the post content and metadata to the final HTML file.
post['stem'] = source.stem
write_post(post, content)
posts.append(post)
return posts
编写目录
现在,我们可以渲染帖子,但是我们也应该渲染 index.html 列出所有帖子。我们可以使用另一个 jinja2 模板以及所有帖子的列表来执行此操作write_posts。
这里是渲染模板:
<!doctype html>
<html>
<body>
<h1>My blog posts</h1>
<ol>
{% for post in posts %}
<li>
<a href="/{{post.stem}}">{{post.title}}</a>
<li>
{% endfor %}
</ol>
</body>
</html>
这里是 python 渲染代码:
def write_index(posts: Sequence[frontmatter.Post]):
# Sort the posts from newest to oldest.
posts = sorted(posts, key=lambda post: post['date'], reverse=True)
path = pathlib.Path("./docs/index.html")
template = jinja_env.get_template('index.html')
rendered = template.render(posts=posts)
path.write_text(rendered)
整理起来
现在剩下的就是使用一个 main 函数将其连接起来。
def main():
posts = write_posts()
write_index(posts)
if __name__ == '__main__':
main()
在GitHub上查看
因此,您正在阅读的页面已使用此代码呈现!您可以在 theacodes / blog.thea.codes 上查看完整的源代码,包括语法高亮显示支持。
转载信息
- author: Thea Flowers
- post url:Writing a small static site generator