しずかなインターネットをobsidianにいれる

obsidianにしずかなインターネットで書いた記事を全部移行していく。
markdownは諦めて、記事をそのまま食わせる形を目指す。
domレンダリングさせるのめんどかったのでchorome内consoleでjs書いて内容を取る。headless cli browserで一括で処理するのも考えたがハマったのでめんどかった。
https://sizu.me/shuzon?page=1
で以下を実行する。10ページほどしかなかったのでページネーションは加味せず各ページごとに実行してjsonにコピペする形にした。
chat gptに生成させたインスタントなコード。
(async () => {
// ① ページ上からパスを動的取得
const paths = Array.from(
document.querySelectorAll('article a[href^="/shuzon/posts/"]'),
a => a.getAttribute('href')
);
// ② 重複除去
const unique = Array.from(new Set(paths));
// ③ 取得 → 整形
const base = 'https://sizu.me';
const articles = await Promise.all(
unique.map(async path => {
const res = await fetch(base + path);
const html = await res.text();
const doc = new DOMParser().parseFromString(html, 'text/html');
const titleEl = doc.querySelector('h1');
const title = titleEl ? titleEl.textContent.trim() : '(タイトルなし)';
const bodyEl = doc.querySelector('.post-body_content__42O24');
const body = bodyEl
? Array.from(bodyEl.querySelectorAll('p'))
.map(p => p.textContent.trim())
.filter(Boolean)
.join('\n\n')
: '(本文なし)';
return {
Path: base + path,
Title: title,
Body: body
};
})
);
// ④ JSONとして出力
console.log(
JSON.stringify(
{ articles },
null,
2
)
);
})();
出力はこんな感じのjsonになる。
{
"articles": [
{
"Path": "https://sizu.me/shuzon/posts/3iuvao7m5t9f",
"Title": "風を借りる",
"Body": "内省や省察…"
},
…
]
}

jsonを各ページごとに作っておいて、各ページの結果を取得する。
これで準備完了。

obsidian を使うのでとりあえずcliを使ってみる
brew tap yakitrak/yakitrak
brew install yakitrak/yakitrak/obsidian-cli
Set Default Vault
Vaultってのは「Obsidianが認識しているファイル置き場」みたいなやつ。 .obsidian
という隠しフォルダがあれば挙動する。obsidianからフォルダ作ると自動生成されるはず。
私のVaultは ~/ObsidianValut
にあるので
obsidian-cli set-default "ObsidianValut"
にしてみた。

documentを読むと
obsidian-cli create "{note-name}" --content "abcde"
でどうやら記事が作成されるっぽい。つまりjsonをパースしてtitle, bodyをそれぞれ指定すればよしなにやってくれそう。
同ディレクトリに入っているjsonを一括で読んでobsidianに突っ込んでもらうようにした。
(カレントディレクトリの全jsonを読む)
# sizume ディレクトリ配下の全 JSON から articles を集めてマージ→Pathでユニーク化→個別に obsidian-cli を呼び出す
jq -s '
# 1) slurp で全ファイルを配列に
# 2) 各ファイルの .articles[] を flat map
# 3) Path キーで重複排除
map(.articles // []) | add | unique_by(.Path)
' ./*.json \
| jq -c '.[]' \
| while IFS= read -r article; do
# 各記事を個別にパース
title=$(jq -r '.Title' <<<"$article")
body=$(jq -r '.Body' <<<"$article")
# obsidian-cli に渡す
obsidian-cli create "$title" --content "$body"
done
コメントが含まれていると実行時にエラーが出るので除去。
jq -s '
map(.articles // []) | add | unique_by(.Path)
' ./*.json \
| jq -c '.[]' \
| while IFS= read -r article; do
title=$(jq -r '.Title' <<<"$article")
body=$(jq -r '.Body' <<<"$article")
obsidian-cli create "$title" --content "$body"
done

jsonに含まれる記事数と結果が2件ほどズレている。
どうもtitleに禁止文字が入ってるとcreateが失敗するようでそれを特定する。
実行時エラーでは「タイトルに/を含む記事は無効」のような感じでエラーが出ていたけど、その文字を含む記事がないため難航。
結論、タイトルに :
を含むと実行エラーが出るっぽかった(これが2件あったので件数一致)
見えない谷: 前編 私(I)を超えて私たち(We)で考える

漏れた記事の名前を修正し、sizume_exception.json
と名付けて同じ構造で保存。
※先ほどと同じファイルで実行してしまうと記事が重複するため対象記事のみを扱うこと
jq -s '
map(.articles // []) | add | unique_by(.Path)
' ./sizume_exception.json \
| jq -c '.[]' \
| while IFS= read -r article; do
title=$(jq -r '.Title' <<<"$article")
body=$(jq -r '.Body' <<<"$article")
obsidian-cli create "$title" --content "$body"
done
再度、指定ファイルを変えて実行。問題なく動いた