跳转至


课程  因子投资  机器学习  Python  Poetry  ppw  tools  programming  Numpy  Pandas  pandas  算法  hdbscan  聚类  选股  Algo  minimum  numpy  algo  FFT  模式识别  配对交易  GBDT  LightGBM  XGBoost  statistics  CDF  KS-Test  monte-carlo  VaR  回测  过拟合  algorithms  machine learning  strategy  python  sklearn  pdf  概率  数学  面试题  量化交易  策略分类  风险管理  Info  interview  career  xgboost  PCA  wavelet  时序事件归因  SHAP  Figures  Behavioral Economics  graduate  arma  garch  人物  职场  Quantopian  figure  Banz  金融行业  买方  卖方  story  量化传奇  rsi  zigzag  穹顶压力  因子  ESG  因子策略  投资  策略  pe  ORB  Xgboost  Alligator  Indicator  factor  alpha101  alpha  技术指标  wave  quant  algorithm  pearson  spearman  tushare  因子分析  Alphalens  涨停板  herd-behaviour  momentum  因子评估  review  SMC  聪明钱  trade  history  indicators  zscore  波动率  强化学习  顶背离  freshman  resources  others  AI  DeepSeek  network  量子计算  金融交易  IBM  weekly  LLT  backtest  backtrader  研报  papers  UBL  quantlib  jupyter-notebook  scikit-learn  pypinyin  qmt  xtquant  blog  static-site  duckdb  工具  colors  free resources  barra  world quant  Alpha  openbb  数据  risk-management  llm  prompt  CANSLIM  Augment  arsenal  copilot  vscode  code  量化数据存储  hdf5  h5py  cursor  augment  trae  Jupyter  jupysql  pyarrow  parquet  数据源  quantstats  实盘  clickhouse  notebook  redis  remote-agent  AI-tools  Moonshot  回测,研报,tushare 

arsenal »

2024年,免费博客赚钱方案


几年前,我就推荐过用 Markdown 写作静态博客。静态博客几乎是零托管成本,比较适合个人博客起步。Markdown 便于本地搜索,也可当作是个人知识库方案。

现在有了新的进展。我不仅构建了一个视觉上相当不错的个人网站,还美化了 github、构建了个人出版系统 -- 将文章导出为排版精美的图片和 pdf 的能力。

这一方案的核心是 Mkdocs 和 Mkdocs-material。前者是 Python 技术文档构建系统,后者是与之适配的主题。我在 《Python 能做大项目》 这本书中,深入介绍过这两种技术。

现在,基于这两种技术,我们可以走得更远:不仅可以撰写技术文档,更可以打造博客和门户网站。

下图就是截取的 大富翁量化 的网站界面:

66%


你可以在 大富翁量化 网站上看到它最新的样式。更有创意的是,虽然它只是一个静态网站,但你每次刷新它,都能看到一些新的内容 -- 至少配图会变!

这是首页。菜单栏、搜索这些是常规配置。标签云、首页的卡片式布局,是提升站点气质的地方。

所有的文档都有版本管理,我使用了 github 来托管文档和图片,所以,也顺便把 github 的个人主页美化了一下:

75%

实在说,之前我没有想过,github 主页也可以做得像个人网站。不得不说一张好图,能大大提升颜值。


此外,作为创作者,我还希望自己的文章能在多个渠道上发布,包括公众号、知乎、CSDN 和小红书,有时候还需要把文章导出为 PDF。这些渠道采用的技术大不相同,它们的排版要求也不一样,所以,要想不把时间无谓地浪费在枯燥、重复的排版上,我们就得用好各种工具。

Mkdocs + Mkdocs - Material

基础搭建我都写在 《Python 能做大项目》 这本书的第 10 章中了,这里我们只介绍如何开通博客功能,以及定制首页。

Material 自带了博客插件,我们只需要在配置中启用它(以及其它相关插件):

 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
plugins:
  - awesome-pages:
      collapse_single_pages: true
  - blog:
      post_excerpt_separator: <!--more-->
  - tags:
      tags_file: tags.md
  - rss:
      match_path: "(blog|articles)/.*"
      category:
        - categories
        - tags
      date_from_meta:
        as_creation: "date"
        as_update: true
  - rss:
      match_path: "(blog|articles)/.*"
      category:
        - categories
        - tags
      date_from_meta:
        as_creation: "date"
        as_update: true
        datetime_format: "%Y-%m-%d %H:%M"
        default_timezone: Asia/Shanghai
      use_git: false

其中 rss 插件需要安装 mkdocs-rss-plugin 插件。关于如何定制标签云,请参考 Code Inside Out 上的这篇文章

该文也提到了如何实现最新博文的功能。不过,博主最后决定自己用 Python 撸一个方案,以实现本文开头提到的效果 -- 动态和卡片式,并且能自动更新 github 的 profile。

自定义脚本生成卡片式首页和 github profile

这个方案主要使用了 python-frontmatter 库。通过一个脚本,搜索 articles 和 blog 目录下所有的 md 文件,读取它们的 front matter,再按日期排序,将最新发表的文章和博文的摘要、日期、title 和首图抽取出来,通过模板生成一个新的 README.md 文件,放到项目根目录下。


github 会读取这个文件作为我们的 profile,mkdocs-material 也会根据这个文件,生成网站首页。

这个 readme.md 实际上是一个带部分 html 标签的 md 片段。我先用脚本生成了供 mkdocs 使用的 README.md,待网站发布后,再生成供 github 的 profile 使用的 readme.md。区别主要在于,github 的 profile 中不能使用'<style>'标签来定义样式,它只允许在 README.md 中 html 标签中,使用少量的样式语法。

Tip

如果对这个方案的细节感兴趣,可以直接访问 zillionare 这个项目。mkdocs-material 的定制在 docs/overrides 目录下。构建 Readme.md 的脚本在根目录下,名字为 publish.py。

为了生成响应式的卡片布局,我使用了 bootstrap 中 card 样式。它简单到只要把父容器声明为 card-columns 类,card 元素的样式声明为 card 就可以了。此外,为了根据不同的屏幕大小,显示不同的 card 列数,可以使用媒体查询和 column-count 属性,在 docs/assets/templates/homepage.tpl 文件中有示例。

在 publish.py 脚本中,我使用了 frontmatter 来提取文章中的 meta,但它的速度有点慢。不过这是一个使用多进程加速的好场景:


1
2
3
4
5
metas = []
articles = glob.glob("./docs/articles/**/*.md", recursive=True)
with ProcessPoolExecutor() as executor:
    results = executor.map(extract_article_meta, articles)
    metas.extend([meta for meta in results if meta is not None])

这样就可以多进程处理文件了。最终结果会汇总到 metas 这个数组中。

图片自动换新技巧

图片自动换新,可以给静态网站增加动态内容。让读者每次访问,都有不一样的感觉。这是通过 unsplash 网站的 gallery 功能来实现的。unsplash 是一个免费图片资源共享网站,提供了大量高清、很高质量的免费图片。如果我们将 img 元素的 url 指向'https://source.unsplash.com/random/360x200?{word}'这样的地址,unsplash 就会返回一个 360x200,并且其类别为 word 的图片。

发布到小红书

小红书发文不能超过 1000 字,并且很难格式化。如果要分享文字、代码并且做到混排,惟一的方法就是将其转换成为图片。这个步骤有点繁琐,不过,这也成为一种壁垒,导致深度内容在小红书上比较稀缺。因此,如果能搞定格式排版,这样就可以有效地利用小红书的分发。

我的方案是使用 slidev。它是一个基于 markdown 语法来创作在线演示的方案,提供了转换为图片的功能。


R50

因此,我们基于 mkdocs+material 创作的文档,只要加上少量的标记和定制,就可以很容易地转换成为图片。

要实现这个功能,在安装 slidev 之后,先进行主题定制。最重要的是 cover layout 的定制。右图就是我之前为小红书日更设计的一个首页样式。

我们可以设计多个样式,在导出时,使用下面的命令:

1
npx slidev export --format png -t /path/to/slidev_themes/special_theme_dir --output /tmp/xhs /path/to/src.md

我们需要在 markdown 内容中的合适位置,增加"---"作为分页符,这样就能导出一页页适合在小红书上发布的图片了。

发布到微信公众号

L33

微信公众号排版一直是个问题。我甚至一度放弃了公众号创作。作为技术极客,我拒绝了几乎任何不是基于 Markdown 的排版方案 -- 都什么年代了,写个自媒体都是赔钱的,平台还好意思要求我们专门为你们排版?

直到我遇到了 mdnice。

它甚至比我自己使用 mkdocs-material 的排版还要漂亮 -- 特别是它对代码的处理部分 -- 我超爱她深色主题下的 50 道阴影!

我现在也超爱写公众号了!不过,什么时候 mdnice 能实现 markdown 的 admonition 语法就更好了。毕竟,这是个信息过载的时代,我们必须用一些留白来缓解密集信息恐惧症,同时用一些闪耀的装饰吸引读者,避免他们走掉。

当然,mdnice 也可以直接发到 CSDN 上。不过,这样会失去定时发布以及自定义标签、选择专栏、定制首图的能力,所以,我宁愿自己登录到 CSDN 的网站上去编辑,好在,它提供了 markdown 编辑器,并且能自动处理图片链接。因此,我并不需要一个个地从本地上传,这省了我不少时间。

发布到知乎

如果你使用的是 vscode 来编辑 markdown 的话,就可以使用一个名为 Zhihu On VSCode 的扩展。它支持定时发布(一天以内)、选择专栏,但不支持添加话题,另外,它不能正确处理(去掉)frontmatter。

但是它能处理 markdown 中的图片链接。这样就省去了我上传图片的时间。它对代码也能很好处理。

不过,知乎的文档格式确实太素净了。

转换为 pdf

slidev 也可以转换成为 pdf。不过,我更喜欢使用 vscode 的 markdown preview enhanced 来转换 pdf。最终提供转换的是 chrome+puppetter。通过在文档的 frontmatter 中加上适当的标记,就能生成页眉和页脚。

右图就是它导出的一例。

此外,slidev 也可以导出 pdf。要实现页眉和页脚,需要要定制 global-top 和 global-bottom,一旦定制完成,这种方案似乎更好,毕竟,它是手动分页,我们对页面的控制力更强。

赚点小钱

辛苦辛苦写了博客,想办法赚点钱吧。我们可以通过前面提到的 ovrrides 方案,让 mkdocs-material 为每个页面插入一个 js 脚本。

如果你加入了广告联盟,它们一般就会为你提供一个 js 脚本,把这个 js 插入进去就可以了。也可以自己写一个 js,把自己要发布的广告,以 html 片段的形式,放在 docs 目录下的某一个子目录中,然后通过下面的代码把这些片段插入进来:

 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
function insertAd(minParas, minWords){
    var links = document.querySelectorAll("a[href*='" + link + "']");
    if (links.length > 0){
        console.log("已添加")
        return
    }

    var paras = document.querySelectorAll("article p");
    var wordCount = 0
    var paraCount = 0
    var inserted = 0
    for (var i = 0; i < paras.length; i++){
        var p = paras[i];
        paraCount ++
        wordCount += p.innerText.length
        if (inserted >= 2){
            break
        }

        if (paraCount >= minParas && wordCount >= minWords) {
            console.log("find para", p, paraCount, wordCount)
            p.insertAdjacentHTML("afterend", ad)
            paraCount = 0
            wordCount = 0
            inserted += 1
        }
    }
    if (inserted == 0 & paras.length >= 5){
        var article = document.getElementsByTagName("article")[0]
        article.insertAdjacentHTML("beforeend", ad)
    }
}

document$.subscribe(function() {
    console.log("call in ad")
    insertAd(40, 4000)
})

这段代码实现了文中和文末插入。只有在文章内容足够长时,才会插入广告。同样地,完整的代码可以在 zillionare 这个项目下找到。具体位置是 docs/overrides/javascripts/course.js。注意不要使用 ad.js 这样好听好记的名字,它会被 adblock 这样的浏览器扩展拦截!