これでコピペを楽にしつつファイル出力も楽にできる。grep検索もできた。

ブツ

前回と同じリポジトリで編集した。

ひとつの記事あたりひとつのファイルとして出力する。

  • monaledge/backup/
    • list.tsv(記事IDとタイトル)
    • 記事ID.md(記事の数だけ作成する)

20220728104538_020_resize.png
20220728104453_020_resize.png

前回まで

モナレッジに投稿した自分の記事をすべて一括でローカルにファイル保存したかった。が、その前にモナレッジのバックエンドAPIサーバが落ちた。この作業が原因かもしれないため一旦自動化を断念して手動でのバックアップに切り替えることにした。

Calcにコピペすることでコピペ作業を少しだけ軽減できるようにした。

記事ID、カテゴリ、タイトルを取得する

mypageにアクセスし、デベロッパツールで以下のように記事データを取得できることを確認する。

const links = Array.from(document.querySelectorAll(`a[href^='/article/']`))
const ids = links.map(e=>parseInt(e.getAttribute('href').replace(/.*[\/\\]/, '')))
const dateViewCates = links.map(e=>e.querySelector('div.v-card__subtitle').textContent.trim().split('\n').map(s=>s.trim()))
for (let i=0; i<dateViewCates.length; i++) {dateViewCates[i][1]=parseInt(dateViewCates[i][1])}
const titles = links.map(e=>e.querySelector('div.v-card__title').textContent.trim())
const monas = links.map(e=>e.querySelector('span.subheading').textContent)
//const categories = links.map(e=>e.querySelector('span.v-chip__content').textContent.trim())
console.debug(links, ids, dateViewCates, titles, categories, monas)
console.debug(ids.map((id,i)=>`${id}\t${titles[i]}`).join('\n'))

これをTSV形式にしてクリップボードにコピーする。

// モナレッジのmypageから記事ID、カテゴリ、タイトルをTAB区切でクリップボード出力するブックマークレット
javascript:(async()=>{const links = Array.from(document.querySelectorAll(`a[href^='/article/']`));const ids = links.map(e=>parseInt(e.getAttribute('href').replace(/.*[\/\\]/, '')));const categories = links.map(e=>e.querySelector('span.v-chip__content').textContent.trim());const titles = links.map(e=>e.querySelector('div.v-card__title').textContent.trim());await navigator.clipboard.writeText(ids.map((id,i)=>`${id}\t${categories[i]}\t${titles[i]}`).join('\n')).catch(e=>console.error(e))})();

これをaタグにしてHTML表示する。

<a href="javascript:(async()=>{const links = Array.from(document.querySelectorAll(`a[href^='/article/']`));const ids = links.map(e=>parseInt(e.getAttribute('href').replace(/.*[\/\\]/, '')));const categories = links.map(e=>e.querySelector('span.v-chip__content').textContent.trim());const titles = links.map(e=>e.querySelector('div.v-card__title').textContent.trim());await navigator.clipboard.writeText(ids.map((id,i)=>`${id}\t${categories[i]}\t${titles[i]}`).join('\n')).catch(e=>console.error(e))})();">モナレッジのmypageから記事ID、カテゴリ、タイトルをTAB区切でクリップボード出力するブックマークレット</a>

以下をブラウザでブックマークして、mypageで実行し、Calcファイルのシートmonaledge-articles.odsにペーストする。これを1ページずつ全ページ行う。

モナレッジのmypageから記事ID、カテゴリ、タイトルをTAB区切でクリップボード出力するブックマークレット

あとはURL作成セルをクリックして記事編集画面を開き、そこからマークダウンをコピーし、シートのセルにペーストする。

こうして記事IDとマークダウンを貼り付けたら、次はこれを一記事ごとにファイル出力する。今回はマクロで行った。

マクロ作成

  1. monaledge-articles.odsファイルをCalcで開く
  2. メニュー→ツールマクロマクロの編集をクリック
  3. 編集ウインドウ左のオブジェクトカタログからマイマクロStandardModule1を選ぶ
  4. テキストエディタにコードを入力する
  5. 実行する(F5キーまたは)

構文や関数などはググって探した。ExcelのVBと混在しててググラビリティが低い。とりあえず最初のシートのセルA1から値を取得するときは以下コマンド。

ThisComponent.Sheets(0).getCellByPosition(0,0).String
  • UI
    • Msgbox
  • 文字列操作
    • Trim
  • ファイル操作
    • Open
    • FreeFile
    • Print
    • Close

全コード

' 記事IDとタイトルのTSVファイルを出力したい
Option VBASupport 1
Sub Export()
	Dim row as integer
	Dim col as integer
	row = 1
	text = ""
	Do
		id = ThisComponent.Sheets(0).getCellByPosition(0,row).String
		title = ThisComponent.Sheets(0).getCellByPosition(2,row).String
		If 0 < Len(id) Then
			text = text & id & Chr(9) & title & Chr(10)
			md = ThisComponent.Sheets(0).getCellByPosition(3,row).String
			WriteArticles id, title, md
		Else
			Exit Do
		End If
		row = row + 1
	Loop While 0 < Len(id)
	WriteList(Trim(text))
	Msgbox("処理完了") 
End Sub

Sub WriteList(strData as String)
	Dim strFileName As String
	Dim intFileNo As Integer
	strFileName = DirPath & "/list.tsv"
	intFileNo = FreeFile() 
	Open strFileName For Output As #intFileNo
	Print #intFileNo, strData
	Close #intFileNo
End Sub

Sub WriteArticles(id as String, title as String, md as String)
	Dim strFileName As String
	Dim intFileNo As Integer
	strFileName = DirPath & "/" & id & ".md"
	intFileNo = FreeFile() 
	Open strFileName For Output As #intFileNo
	Print #intFileNo, title & Chr(10) & Chr(10) & md
	Close #intFileNo
End Sub

Function DirPath() as String
	strDirPath = ThisComponent.Sheets(0).getCellByPosition(8,0).String
	If 0 < Len(Trim(strDirPath)) < 1 Then
		strDirPath = "/tmp/work/monaledge/backup"
	End If
	DirPath = strDirPath
End Function

Option VBASupport 1はいらないかもしれない。

気になったことは以下。

  • 戻り値があるときはSubでなくFunctionを使う
  • 文字列結合子は+でなく&を使う
  • End, Exit, return系が煩わしい
    • [End] [Sub|Function|If]
    • [Exit] [Do]
    • returnは関数名=値で示す
    • ほかにも多数ありそう
  • 変数の宣言と代入は同時にできない
  • 変数の宣言はしなくてもいい?
  • デバッグ出力がモーダルダイアログでしかできない……

マクロ実行ボタン作成

ボタンを押したらマクロを実行するようにした。

  1. Calcのメニュー→表示ツールバーフォームコントロールをチェックする
  2. フォームコントロールボックスからデザインモードボタンを押してONにする
  3. ボタンボタンを押す
  4. シートの適当な箇所をドラッグ&ドロップしてボタンを作成する
  5. 4のボタンを右クリックしてコントロールを選ぶ
  6. イベントタブにある実行前に作成したSub Exportをセットする
  7. フォームコントロールボックスからデザインモードボタンを押してOFFにする
  8. 4のボタンをクリックするとマクロが実行され処理完了ダイアログが表示される

マクロ実行

ボタンを押してマクロを実行するとシートの状態に応じてファイル出力される。

TUIファイラrangerでファイル閲覧したところ↓

20220728114426_020.png

ファイルがたくさん作られている。中身があるのは手動でコピペした3件だけ。

grep検索

複数のファイルをまたいでキーワードseleniumで検索するときは以下。

cd /tmp/work/monaledge/backup
find . -name "*.md" | xargs grep "selenium" --color=auto

これこれ。まさにこれがやりたかった!

20220728115549_020.png

あとは自分の記事をすべてコピペすれば完成!