hugoの形式で書いた記事のMetadataをプログラムから扱いたい。
一つ前の記事で、ブログのカテゴリーやタグの方針について書いた。 しかし、どう考えても手作業でそれを管理していくのは無理である。 なので、ここでは自動でカテゴリーやタグを設定する方法を考える。
自動でタグやカテゴリーを設定する方法を考える前に、プログラムから簡単にタグやカテゴリーを設定する方法を確立しておきたい。
前提知識
hugoでは、記事のメタデータは各記事ファイルに埋め込むことができる。 そのメタデータは front-matterと呼ばれる。
https://gohugo.io/content-management/front-matter/
front-matterは、yaml、toml、またはjsonで記述することができる。このブログの場合はtoml形式を利用している。
例えば以下のようになる。
draft = true
date = 2023-07-15T22:46:12+09:00
title = "hugoの形式で書いた記事のMetadataをプログラムから扱う"
description = ""
slug = ""
authors = []
tags = []
categories = []
externalLink = ""
series = []
Pythonでfront-matterを読み込み、編集して再書き込みする
以下のプログラムを動かしたところ、動くことが確認できた。 といってもほとんどChatGPTに書いてもらっている。
from pathlib import Path
import toml
BASE_DIR = Path(__file__).resolve().parent.parent
def read_front_matter(file_path):
"""
front_matterを読み込む
"""
with open(file_path, "r") as file:
lines = file.readlines()
# ファイルの先頭から'+++'で囲まれた部分を抽出
front_matter_lines = []
content_start_index = 0
for i, line in enumerate(lines):
if line.strip() == "+++":
if not front_matter_lines: # front matterの始まりを検出
front_matter_lines.append(line)
else: # front matterの終わりを検出
content_start_index = i + 1
break
elif front_matter_lines: # front matterの内部を読み込む
front_matter_lines.append(line)
front_matter_str = "".join(front_matter_lines[1:]) # 最初の'+++'を除去
front_matter = toml.loads(front_matter_str) # TOMLをPythonの辞書に変換
# コンテンツの部分も抽出
content = "".join(lines[content_start_index:])
return front_matter, content
def write_front_matter(file_path, front_matter, content):
"""
指定されたファイルにfront_matter, contentを書き込む
"""
front_matter_str = toml.dumps(front_matter) # Pythonの辞書をTOMLに変換
front_matter_lines = ["+++\n", front_matter_str, "+++\n"] # '+++'で囲む
lines = front_matter_lines + [content]
with open(file_path, "w") as file:
file.writelines(lines)
target_file = (
BASE_DIR / ".....md", # TODO: 実際に存在するファイルに
)
# Hugoのブログ記事ファイルのメタデータを読み込む
front_matter, content = read_front_matter(target_file)
print(front_matter)
# メタデータを編集
front_matter["draft"] = False
# 編集したメタデータをファイルに書き戻す
write_front_matter(target_file, front_matter, content)
クラスにして使いやすくする
このままの状態では少々使いにくいので、クラスにした。
class Post:
"""
投稿を表すクラス
"""
def __str__(self):
return self.front_matter["title"]
@property
def title(self):
return self.front_matter["title"]
@title.setter
def title(self, value):
self.front_matter["title"] = value
@property
def tags(self):
return self.front_matter["tags"]
@tags.setter
def tags(self, value):
self.front_matter["tags"] = value
@property
def categories(self):
return self.front_matter["categories"]
@categories.setter
def categories(self, value):
self.front_matter["categories"] = value
def __init__(self, path):
self.path = path
self._load()
def _load(self):
"""
ファイルから投稿を読み込む
"""
front_matter, content = self._read_front_matter()
self.front_matter = front_matter
self.content = content
def _read_front_matter(self):
"""
front_matterを読み込む
つまり、メタデータを読み込む
"""
with open(self.path, "r") as file:
lines = file.readlines()
# ファイルの先頭から'+++'で囲まれた部分を抽出
front_matter_lines = []
content_start_index = 0
for i, line in enumerate(lines):
if line.strip() == "+++":
if not front_matter_lines: # front matterの始まりを検出
front_matter_lines.append(line)
else: # front matterの終わりを検出
content_start_index = i + 1
break
elif front_matter_lines: # front matterの内部を読み込む
front_matter_lines.append(line)
front_matter_str = "".join(front_matter_lines[1:]) # 最初の'+++'を除去
front_matter = toml.loads(front_matter_str) # TOMLをPythonの辞書に変換
# コンテンツの部分も抽出
content = "".join(lines[content_start_index:])
return front_matter, content
def save(self):
"""
投稿を保存する
"""
front_matter_str = toml.dumps(self.front_matter) # Pythonの辞書をTOMLに変換
front_matter_lines = ["+++\n", front_matter_str, "+++\n"] # '+++'で囲む
lines = front_matter_lines + [self.content]
with open(self.path, "w") as file:
file.writelines(lines)