Chromeのブックマークが増える一方で、どれを実際に使っているのか分からなくなった。なのでClaude Codeに「ブックマークを使ってる順に並べて」と一文で頼んで、出てきたスクリプトを整えて回した。
結果、76件のうち、ブックマーク経由で開いたことがあるのは37件だった。残り39件、ちょうど半分は保存しただけで一度も使っていない。この記事はその集計スクリプト(コピペで動く)と、数えて分かったことの置き場にする。
[toc]
Chromeはどこに何を記録しているか
材料は2つのファイルで足りる。
| ファイル | 形式 | 入っているもの |
|---|---|---|
~/Library/Application Support/Google/Chrome/Default/Bookmarks |
JSON | ブックマーク本体。date_last_used(最後にブックマークから開いた日時)を持つ |
同ディレクトリの History
|
SQLite | URL別の訪問回数 visit_count
|
date_last_used が0のものは、ブックマークバーやメニューから一度も開かれていない。Historyの visit_count と突き合わせると「そのページ自体は見ているか」も分かる。この2軸で並べ替える。
スクリプト(このまま動く)
標準ライブラリのみ。Historyは Chrome起動中ロックされるので、コピーしてから開くのがコツ。
#!/usr/bin/env python3
"""Chromeのブックマークを実使用順に並べ替える。標準ライブラリのみ。"""
import datetime
import json
import pathlib
import shutil
import sqlite3
import tempfile
# Windowsは ~/AppData/Local/Google/Chrome/User Data/Default に読み替え
PROFILE = pathlib.Path.home() / "Library/Application Support/Google/Chrome/Default"
def chrome_time(us: int) -> datetime.datetime | None:
"""Chromeのタイムスタンプ(1601年起点マイクロ秒)をdatetimeに変換する。"""
if not us:
return None
return datetime.datetime(1601, 1, 1) + datetime.timedelta(microseconds=int(us))
def load_bookmarks() -> list[dict]:
data = json.loads((PROFILE / "Bookmarks").read_text())
out: list[dict] = []
def walk(node: dict) -> None:
if node.get("type") == "url":
out.append({
"title": node["name"],
"url": node["url"],
"last_used": chrome_time(int(node.get("date_last_used", 0))),
})
for child in node.get("children", []):
walk(child)
for root in data["roots"].values():
if isinstance(root, dict):
walk(root)
return out
def load_visit_counts() -> dict[str, int]:
"""閲覧履歴のvisit_countをURL別に返す。Historyはロックされるのでコピーして開く。"""
with tempfile.NamedTemporaryFile(suffix=".db") as tmp:
shutil.copy(PROFILE / "History", tmp.name)
con = sqlite3.connect(tmp.name)
counts = {url.rstrip("/"): cnt for url, cnt in
con.execute("SELECT url, visit_count FROM urls")}
con.close()
return counts
def main() -> None:
visits = load_visit_counts()
rows = [(visits.get(b["url"].rstrip("/"), 0), b) for b in load_bookmarks()]
rows.sort(key=lambda r: -r[0])
for cnt, b in rows:
flag = "" if b["last_used"] else " <- ブックマークからは一度も開いていない"
print(f"{cnt:>4}回 {b['title'][:40]}{flag}")
if __name__ == "__main__":
main()
出力は訪問回数の降順で、ブックマーク未使用のものに印が付く。
62回 (業務で毎日見るページ)
37回 (よく引く公式ドキュメント)
0回 (去年保存した記事) <- ブックマークからは一度も開いていない
うちの76件を数えた結果
| 項目 | 数字 |
|---|---|
| 総ブックマーク数 | 76件 |
ブックマークから開いたことがない(date_last_used=0) |
39件(51%) |
| ページ自体は履歴に残っている | 73件(96%) |
| 死リンク(応答なし/404) | 4件 |
| 追加時期 | 全件がこの15ヶ月以内 |
予想外だったのは死リンクの少なさと、未使用率の高さの組み合わせだった。リンク切れの掃除をするつもりで数え始めたのに、掃除対象の死リンクは4件しかない。代わりに、生きてるのに一度も使っていないブックマークが39件あった。
しかもその39件のURLの大半は、履歴には残っている。ページは見ているのに、ブックマークからは開いていない。開くときは検索バーに数文字打つか、履歴のサジェストで飛んでいる。ブックマークは「開くための道具」として使われてなくて、「あとで要るかもしれない」を収める箱になっていた。俺の場合、箱の中身の半分は15ヶ月間ただ寝ていた。
死リンクの掃除は1行で済む
76件程度なら死活確認は数十秒で終わる。エクスポートしたURL一覧に対してこれを回すだけ。
# urls.txt に1行1URLで置いた場合
while read -r u; do
printf "%s %s\n" "$(curl -sIL --max-time 6 -o /dev/null -w '%{http_code}' "$u")" "$u"
done < urls.txt | sort
注意点が1つ。403は死んでいるとは限らない。うちの76件では13件が403を返したが、全部bot避けをしているだけの生きたサイトだった。消していいのは000と404だけ。
この集計の限界
-
date_last_usedはブックマークUI経由の使用しか数えない。アドレスバーのサジェストからの訪問はカウント外なので、「ブックマークとして未使用」と「ページを見ていない」は別物。今回はそこを分けるためにHistoryと突き合わせている - Historyの保持はおおむね直近3ヶ月分。visit_countの母数もそこに引きずられる
- Chromeのバージョンによっては
date_last_usedを持たない。うちの環境(現行安定版)では全件に入っていた - n=76の1環境。ブックマークを数百件抱えている人では比率が変わる可能性が高い
自分の環境で回すなら
上のスクリプトをコピペして python3 bm_rank.py するだけ。Claude Codeに頼むなら、指示はこの一文で足りる。
Chromeのブックマークを、閲覧履歴のvisit_countと突き合わせて使ってる順に並べるスクリプトを書いて実行して
ランキングを見て一番効いたのは、バーの一等地に未使用が普通に並んでいたと分かったことだった。整理はこれからやる。上位10件だけバーに残す予定でいるが、未使用の39件を消すかはまだ決めてない。15ヶ月寝てたものが今後起きる確率は低い気がする一方で、消して困った記憶だけは妙に強い。まず数字を見てから悩む方が、少なくとも健全ではあった。