设计思路
传统的博客程序,一般都有后台数据库。当新发布一篇博客时,基本步骤就需要:1.登陆->2.新建博客-3.填写标题-4.复制内容-5.填写关键字-6.选择分类-7.点击发布等,过程十分繁琐。
前段时间正好看到轻量级博客Letterpress中提到的想法:直接上传markdown格式的博客原文,让程序自动去处理和渲染。这样一来发布博客确实可省去很多体力活;另外,作为 Python 新手,也想动手实践下,至于实现好坏暂且不表。
主要思路:
-
页面模板:
-
去年10月起折腾过 Movable Type 5,虽博客写得少,但还是看过官方的文档;以及近来流行的 Bootstrap 项目,也动手进行过一些定制化,Movable Type 后台的模板设计很值得学习和借鉴,所以照搬过来。
-
博客主要采用典型的三栏设计,据此对模板进行抽象和分类,即可分为主模板、layout、modules、widgets 和 misc 等,模板之间可以相互引用。这些模板根据 URL 请求获取处理后的参数,完成最终渲染并响应给用户。
-
-
核心逻辑:
-
初始化。EntryService 读取指定路径下的 markdown 文件,进行初始化,以
2013-7-3_github_tips.md
为例:- 1.生成URL,即根据文件名生成URL。如
2013-7-3_github_tips.md
->/blog/2013/07/03/github_tips.html
,对应的 raw 文件 URL 则为/raw/2013/07/03/github_tips.md
。 - 2.获取时间,优先从文件名中提取,否则获取文件的创建或者更新时间,这里暂未处理好。
- 3.获取内容,包括 markdown 原文内容,以及经过 markdown 到 html 转换后的内容。
- 4.其他初始化,如 tag, category, calendar, archive 等。
- 5.存储博客: url(key) -> entry(value) 为一一对一个关系,这里使用 dict 将 entry 保存在内存中。
- 1.生成URL,即根据文件名生成URL。如
-
响应请求。当浏览器客户端进行访问时,Web.py 应用根据 URL 路由到指定的 Controller,并由 EntryService 处理逻辑(包括搜索、查看博客、查博客的 Raw 内容、博客和 Raw 归档等),返回响应参数(针对模板,该参数也创建了数据模型,可以简化参数传递);再由控制器根据响应参数判断渲染的模板文件,响应给用户。若请求博客的 Raw 内容,则直接返回文本内容不用模板渲染。
-
动态监听。使用 pyinotify 监听文件系统中的指定博客路径,当该路径下有新增、修改、删除博客的操作时,触发监听事件,并通知 EntryService 进行相应的新增、修改、删除操作,以更新内存中的博客内容。在本项目中,仅搭建了一个可用的模型,pyinotify 使用了一个单独的线程,EntryService 在整个程序运行过程,仅有一个实例。
-
项目结构
blog/
├── __init__.py #初始化文件,主要加载 pyinotify,监听 blog/raw/entry 路径下 md 的新增、修改、删除
├── blog.py #博客程序入口,运行: $ cd blog; python blog.py > blog.log 2>&1 &
├── config.py #博客配置,主要是站点、文件系统路径、博客URL路由以及全局环境的配置
├── controller.py #控制器模块,分发URL请求,并响应响应的视图
├── service.py #服务模块,初始化博客系统,处理URL请求逻辑
├── model.py #模型模块,针对博客系统的 Entry,Page,Tag,Category 等设置的数据模型,以及模板中的参数模型
├── tool.py #工具类,如自动提取关键字,将字典转换为对象等,待完善
├── README.md
├── raw # raw 格式文件路径,主要是 markdown 文件
│ ├── entry #博客文件
│ ├── page #页面文件,相对于博客而言,页面处理简单很多
│ └── tweet
├── static #静态文件,web.py 框架的要求,此路径下的文件可以有 web.py 当做静态文件处理
│ ├── css
│ ├── favicon.ico
│ ├── img
│ └── js
└─── template #模板库,主要参考 Movable Type 5 的设计。layout, misc, modules, widgets 为子模板。
├── index.html #首页模板
├── search.html #搜索模板
├── entry.html # Entry/Page 详情模板
├── archive.html #归档模板
├── atom.xml # RSS 订阅模板
├── error.html #错误页面模板
├── layout #布局模板,这里采用 `AA|B|C` 的三栏布局,AA 放博客主要内容,B、C作为右侧边栏显示 widgets
│ ├── footer.html
│ ├── header.html
│ ├── navbar.html
│ ├── three
│ │ ├── primary.html
│ │ └── secondary.html
│ └── two
├── misc #杂项资源
│ ├── ads
│ │ └── ad.html
│ └── analytics.html
├── modules #模板模板,在AA布局中使用
│ ├── archive.html
│ ├── category.html
│ ├── comment.html
│ ├── entry.html
│ ├── error.html
│ ├── excerpt.html
│ ├── info.html
│ ├── pager.html
│ ├── related.html
│ ├── search.html
│ └── tag.html
└── widgets #小工具模板,在 B、C 侧边栏中使用
├── about.html
├── archive.html
├── calendar.html
├── category.html
├── link.html
├── powered.html
├── recently.html
└── tag.html
要求
-
仅限于 Linux 平台。因使用 pyinotify 有平台限制。pyinotify 监听指定路径,即可自动处理新增、更新以及删除的博客。详见pyinotify。
-
Markdown。将 markdown格 式文件渲染成 HTML,详见 markdown。
-
Web.py。使用的Web框架,由已故的 Aaron Swartz开发,详见 Web.py。
-
Python 2.7.5。本博客程序仅在 Python 2.7.5 下测试通过。
运行
克隆博客代码
$ cd ~
$ git clone https://github.com/dylanninin/blog.git
切换路径
$ cd blog
启动博客
$ python blog.py > blog.log 2>&1 & # Listen on 0.0.0.0:8080 default
博客
环境准备
$ ln -s ~/blog/raw/entry entry
$ cd entry
发布博客
$ rz # use ZMODEM (Batch) file receive tool to send your local markdown file to blog server. md format is usually yyyy-mm-dd_file_name.md
删除博客
$ rm 2013-07-02_github_tips.md
效果
- Online Demo: Pastime Paradise
Movable Type 5:http://dylanninin.com/
计划
-
自动提取关键字
-
自动摘要
-
自动查找相关博客
-
功能完善和 bug 排除
-
代码重构
更新
2017-06-18
- 部署到 Heroku
2016-03-06
2015-09-17
- 格式化代码
2013-11-23
本计划使用 TF-IDF 以及 Jieba,自动提取博客的关键字、摘要以及相似文章,但效果并不理想。
这里模仿 Github Pages 中的做法,在博客的开始声明一些属性:
---
title: the title, default None if it's empty
category: category, default Uncategorised if it's empty.
tags: [tag1, tag2], default [Untagged] if it's empty.
---
另外,此博客的在线 demo。